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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 19:03:42