Notion同步Google Sheets触发Client secrets类ValueError排查
问题现象
实现Google Sheets与Notion对接同步时,代码运行触发报错:ValueError: Client secrets must be for a web or installed app
报错根因
代码同时混用了Google API两套完全不兼容的鉴权逻辑,凭证类型不匹配:
- 全局位置编写了服务账号(Service Account)鉴权代码,当前使用的
credentials.json是服务账号类型密钥 get_sheet()函数内又实现了OAuth桌面客户端授权流程,调用InstalledAppFlow.from_client_secrets_file方法时,要求传入「桌面应用/Web应用」类型的OAuth客户端密钥,和现有服务账号密钥格式完全不匹配,直接触发报错。
解决思路
二选一固定鉴权方案即可,新手优先选择服务账号鉴权,配置步骤更少,不需要每次运行弹浏览器授权。
- 方案1(推荐):全程使用服务账号鉴权,删除冗余的OAuth本地授权逻辑
注意:必须把待同步的Google表格共享给服务账号邮箱(打开credentials.json找到client_email字段对应的xxx@xxx.iam.gserviceaccount.com地址,赋予编辑权限),否则会报403无权限错误 - 方案2:如果要使用OAuth本地授权流程,去Google Cloud控制台创建「桌面应用」类型的OAuth2.0客户端ID,下载对应密钥文件替换现有
credentials.json,同时删除代码中服务账号鉴权的相关代码。
修正后可运行代码(服务账号方案)
from __future__ import print_function from googleapiclient.discovery import build from google.oauth2 import service_account from notion.client import NotionClient # 配置项 替换为自己的实际值 SCOPES = ['https://www.googleapis.com/auth/spreadsheets'] SERVICE_ACCOUNT_FILE = 'credentials.json' SAMPLE_SPREADSHEET_ID = 'OCCLUDED' SAMPLE_RANGE_NAME = 'OCCLUDED' NOTION_V2_TOKEN = "OCCLUDED" NOTION_PAGE_LINK = "OCCLUDED" # 原代码漏定义该变量,需补充为目标Notion数据库链接 # 初始化服务账号凭证 creds = service_account.Credentials.from_service_account_file( SERVICE_ACCOUNT_FILE, scopes=SCOPES) def get_sheet(): """读取Google Sheets指定范围数据""" service = build('sheets', 'v4', credentials=creds) sheet = service.spreadsheets() result = sheet.values().get(spreadsheetId=SAMPLE_SPREADSHEET_ID, range=SAMPLE_RANGE_NAME).execute() values = result.get('values', []) if not values: print('No data found.') return None print('%s rows found.' % len(values)) return values def get_prop_type(data): # 可扩展支持更多属性类型,当前默认返回文本类型 return 'text' def notion_update(data): client = NotionClient(token_v2=NOTION_V2_TOKEN) block = client.get_block(NOTION_PAGE_LINK) cv = block.collection rows = cv.get_rows() # 清空数据库原有属性和行 cv.clear_properties() for row in rows: row.remove() # 写入表头属性 title = data[0][0] cv.set_property('title', title, slug=title, property_type='title') for i, prop in enumerate(data[0][1:]): prop_type = get_prop_type(data[1][1:][i]) if len(data) > 1 and data[1][1:] else 'text' cv.set_property(prop, prop, slug=prop, property_type=prop_type) # 逐行写入数据 for row in data[1:]: new_row = cv.add_row() for i, value in enumerate(row): prop = data[0][i] new_row.set_property(prop, value) if __name__ == '__main__': data = get_sheet() if data: notion_update(data)
额外注意事项
- 原代码存在冗余导入,比如
pickle、Request、Credentials等仅OAuth流程需要的依赖,服务账号方案下不需要导入,修正代码已经移除 - 原代码漏定义
NOTION_PAGE_LINK变量,直接运行会触发变量不存在错误,修正代码已经补充占位符,替换为实际的Notion数据库页面链接即可 - 如果选择OAuth方案,不要使用当前的服务账号密钥,必须创建对应类型的OAuth客户端密钥,否则还是会触发同类报错
内容的提问来源于stack exchange,提问作者Hugo
相关产品推荐
相关产品推荐

