You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Power BI API的Python自动化生成个性化PDF报表问题咨询

解决Power BI API 401未授权错误及自动化生成PDF方案

一、先排查修复401错误

401问题核心是认证环节失效,按以下步骤逐一验证:

  • 确认认证方式适配场景:
    • 企业环境优先用服务主体(Service Principal):需在Azure AD完成应用注册,给该应用分配目标Power BI工作区的「成员/管理员」权限,同时在Power BI管理员门户开启「允许服务主体访问Power BI API」开关。
    • 个人测试可用用户名密码认证:需确保账号未启用MFA,且是工作区成员,同时在Power BI管理员门户开启「允许通过用户名/密码访问API」。
  • 核对凭证准确性:
    • 服务主体:检查tenant_id、client_id、client_secret无空格、拼写错误。
    • 用户名密码:确认username、password、tenant_id对应有权限的账号。
  • 验证Token权限:获取Token后,调用https://api.powerbi.com/v1.0/myorg/groups测试,若仍返回401,说明Token未获取到正确权限,重新检查应用权限分配。

二、完整自动化生成PDF实现步骤

1. 安装依赖

pip install requests python-dotenv

2. 认证获取Access Token(服务主体示例)

import os
import requests
from dotenv import load_dotenv

load_dotenv()

def get_powerbi_token():
    tenant_id = os.getenv("TENANT_ID")
    client_id = os.getenv("CLIENT_ID")
    client_secret = os.getenv("CLIENT_SECRET")
    
    url = f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token"
    payload = {
        "grant_type": "client_credentials",
        "client_id": client_id,
        "client_secret": client_secret,
        "scope": "https://analysis.windows.net/powerbi/api/.default"
    }
    
    response = requests.post(url, data=payload)
    response.raise_for_status()
    return response.json()["access_token"]

将TENANT_ID、CLIENT_ID、CLIENT_SECRET写入.env文件,避免硬编码。

3. 获取报表中Names列的唯一值

def get_unique_names(token, workspace_id, dataset_id):
    url = f"https://api.powerbi.com/v1.0/myorg/groups/{workspace_id}/datasets/{dataset_id}/executeQueries"
    headers = {"Authorization": f"Bearer {token}"}
    
    # 替换为实际表名
    query = {
        "queries": [{"query": "EVALUATE DISTINCT('业务数据表'[Names])"}],
        "serializerSettings": {"includeNulls": True}
    }
    
    response = requests.post(url, json=query, headers=headers)
    response.raise_for_status()
    results = response.json()["results"][0]["tables"][0]["rows"]
    return [row["Names"] for row in results]

workspace_id从报表URL提取,dataset_id在工作区数据集列表中查看。

4. 为每个Name生成过滤后的PDF

def export_report_to_pdf(token, workspace_id, report_id, name_value, output_path):
    url = f"https://api.powerbi.com/v1.0/myorg/groups/{workspace_id}/reports/{report_id}/ExportTo"
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }
    
    # 替换为实际页面名、视觉对象名、表名
    payload = {
        "format": "PDF",
        "powerBIReportConfiguration": {
            "pages": [],  # 留空导出所有页面
            "slicers": [
                {
                    "filter": f"业务数据表[Names] eq '{name_value}'",
                    "target": {
                        "pageName": "首页",
                        "visualName": "姓名切片器"
                    }
                }
            ]
        }
    }
    
    # 发起导出请求
    response = requests.post(url, json=payload, headers=headers)
    response.raise_for_status()
    
    # 轮询导出状态
    export_id = response.headers["Location"].split("/")[-1]
    status_url = f"https://api.powerbi.com/v1.0/myorg/groups/{workspace_id}/reports/{report_id}/exports/{export_id}"
    
    while True:
        status_response = requests.get(status_url, headers=headers)
        status_response.raise_for_status()
        status = status_response.json()["status"]
        
        if status == "Succeeded":
            download_url = status_response.json()["resourceLocation"]
            pdf_response = requests.get(download_url, headers=headers)
            with open(f"{output_path}/{name_value}.pdf", "wb") as f:
                f.write(pdf_response.content)
            break
        elif status == "Failed":
            raise Exception(f"导出失败:{status_response.json()['error']}")
        else:
            import time
            time.sleep(5)

页面名从报表标签获取,视觉对象名在编辑模式下选中切片器后,从「格式」面板的「视觉对象名称」查看。

5. 主函数整合

def main():
    token = get_powerbi_token()
    workspace_id = "你的工作区ID"
    report_id = "你的报表ID"
    dataset_id = "你的数据集ID"
    output_path = "./pdf_output"
    
    os.makedirs(output_path, exist_ok=True)
    
    names = get_unique_names(token, workspace_id, dataset_id)
    for name in names:
        print(f"正在生成 {name} 的PDF...")
        export_report_to_pdf(token, workspace_id, report_id, name, output_path)
    print("所有PDF生成完成!")

if __name__ == "__main__":
    main()

三、额外注意事项

  • 若Names包含单引号,需在DAX查询和过滤参数中替换为''(两个单引号)避免语法错误。
  • Power BI API导出有频率限制,短时间大量请求需增加轮询间隔。
  • 确保账号对工作区、报表、数据集均有「成员」及以上权限,否则会触发403或401错误。

内容的提问来源于stack exchange,提问作者karan kurdia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 15:37:02