使用Python通过Graph API读取SharePoint列表及获取AccessToken求助
问题背景
- 目标:通过Python调用Microsoft Graph API读取SharePoint列表数据
- 现状:已能获取SharePoint列表的ID和名称,但无法读取列表行数据
- 限制:账号开启MFA,无管理员权限无法获取Client Secret,仅能通过手机验证码登录
- 错误:使用ADAL设备码流获取AccessToken时,触发
AdalError: Unexpected polling state invalid_client
现有获取列表代码
from pypac import PACSession session = PACSession() headers = { 'authorization': f'Bearer {access_token}', } response = session.request("GET", 'https://graph.microsoft.com/v1.0/sites/xxx.sharepoint.com:/sites/xxx/xxx/BTADMCA:/lists?select=id,name', headers=headers, ) response.json()
原AccessToken获取代码(报错)
import adal authority_url = "https://login.microsoftonline.com/common" client_id = "de8bc8b5-d9f9-48b1-a8ad-b748da725064" auth_context = adal.AuthenticationContext(authority_url) # Initiate device code flow device_code = auth_context.acquire_user_code("https://graph.microsoft.com/", client_id) print(device_code['message']) # Poll for token using device code token_response = auth_context.acquire_token_with_device_code("https://graph.microsoft.com/", device_code, client_id) access_token = token_response["accessToken"] print("Access token acquired successfully:", access_token)
错误信息
AdalError Traceback (most recent call last) <ipython-input-24-afa3aa1fa6be> in <module> 11 12 # Poll for token using device code ---> 13 token_response = auth_context.acquire_token_with_device_code("https://graph.microsoft.com/", device_code, client_id) 14 15 access_token = token_response["accessToken"] c:\ProgramData\python36\lib\site-packages\adal\authentication_context.py in acquire_token_with_device_code(self, resource, user_code_info, client_id) 309 return token 310 ---> 311 return self._acquire_token(token_func, user_code_info.get('correlation_id', None)) 312 313 def cancel_request_to_get_token_with_device_code(self, user_code_info): c:\ProgramData\python36\lib\site-packages\adal\authentication_context.py in _acquire_token(self, token_func, correlation_id) 126 correlation_id or self.correlation_id, self._call_context.get('enable_pii', False)) 127 self.authority.validate(self._call_context) ---> 128 return token_func(self) 129 130 def acquire_token(self, resource, user_id, client_id): c:\ProgramData\python36\lib\site-packages\adal\authentication_context.py in token_func(self) 302 self._token_requests_with_user_code[key] = token_request ... ---> 362 wire_response) 363 else: 364 try: AdalError: Unexpected polling state invalid_client
解决方案
一、修复AccessToken获取问题
invalid_client错误的核心原因是ADAL已被微软弃用,对设备码流的支持存在兼容性问题,建议改用MSAL(Microsoft Authentication Library)实现登录。
改用MSAL实现设备码流登录
import msal # 配置参数 CLIENT_ID = "de8bc8b5-d9f9-48b1-a8ad-b748da725064" # 保持原客户端ID AUTHORITY = "https://login.microsoftonline.com/common" SCOPE = ["https://graph.microsoft.com/Sites.Read.All"] # 按需调整权限,只读用Sites.Read.All # 初始化MSAL公共客户端应用 app = msal.PublicClientApplication(CLIENT_ID, authority=AUTHORITY) # 启动设备码流 flow = app.initiate_device_flow(scopes=SCOPE) if "user_code" not in flow: raise ValueError(f"设备码流初始化失败: {flow.get('error_description')}") # 输出用户操作指引 print(flow["message"]) # 轮询获取令牌 result = app.acquire_token_by_device_flow(flow) if "access_token" in result: access_token = result["access_token"] print("Access Token获取成功") else: raise Exception(f"获取令牌失败: {result.get('error')}, {result.get('error_description')}")
关键说明
- 权限范围
SCOPE需匹配实际需求,若需要写入权限可改为Sites.ReadWrite.All - 无管理员权限时,用户登录后会自动完成授权(需租户允许用户自行授权第三方应用)
二、读取SharePoint列表行数据
获取有效AccessToken后,使用以下代码读取列表行:
from pypac import PACSession session = PACSession() headers = { 'Authorization': f'Bearer {access_token}', 'Content-Type': 'application/json' } # 替换为你的站点路径和目标列表ID site_url = "xxx.sharepoint.com:/sites/xxx/xxx/BTADMCA:" list_id = "你的列表ID" # 从之前获取列表的接口返回中提取 # 请求列表行数据,可通过select指定需要的字段 response = session.get( f'https://graph.microsoft.com/v1.0/sites/{site_url}/lists/{list_id}/items?select=id,fields', headers=headers ) if response.status_code == 200: items = response.json() print("列表行数据:", items) else: print(f"请求失败,状态码: {response.status_code}, 错误信息: {response.json()}")
注意事项
- 列表ID必须是Graph API返回的标准ID,而非SharePoint界面显示的名称
- 若列表行数超过100条,需处理分页:通过
$top参数调整单次返回数量,或遍历响应中的@odata.nextLink链接获取后续数据 - 可通过
select=fields/字段名1,fields/字段名2精准指定需要返回的字段,减少数据传输量
内容的提问来源于stack exchange,提问作者Drew
相关产品推荐
相关产品推荐

