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

解决Google Drive API的403共享配额超限及权限配置问题

问题说明

我有一段脚本,用来批量处理Google Sheet里的用户数据,逻辑是:

  1. 检查是否存在Course Workbooks - [First Name] - [User ID]命名的文件夹
  2. 不存在就创建
  3. 把指定模板文件复制到这个文件夹里
  4. 给用户和john@example.com加文件夹编辑权限(文件默认归服务账户所有)
  5. 更新Sheet的access列为yes,跳过已处理的行

现在碰到两个问题:

  1. 逻辑没问题,但频繁报403共享配额超限;换成drive.file权限范围后直接报错
  2. 尝试把john@example.com设为文件夹所有者时也报403,但这个需求更符合我的业务场景
解决方案

一、权限配置必须到位

  • 别用drive.file权限,这个范围只能访问脚本自己创建的文件,没法做所有权转移和跨文件操作,必须用完整的https://www.googleapis.com/auth/drive权限
  • 确保john@example.com是Google Workspace账户:免费Gmail账户接不了服务账户转的所有权;如果是同域Workspace账户,默认支持所有权转移,跨域的话需要管理员开启外部所有权转移权限

二、解决共享配额超限问题

Google Drive对每日创建/修改权限的操作有配额限制,这么改能缓解:

  • 批量操作权限:用Drive的batch请求一次性提交多个权限修改,减少请求次数
  • 加延迟:每次权限操作后睡1-2秒,别在短时间内刷请求
  • 只给文件夹加权限:文件夹的权限会自动继承给内部文件,不用单独给复制的模板文件加权限,省一半操作

三、正确转移所有权的姿势

服务账户创建的文件默认它就是所有者,不用额外给自己加所有权,直接转就行:

  • 转移时必须加transferOwnership=True参数,不然只会给对方加管理员权限,不会转所有权
  • 代码里创建文件夹后直接调用permissions().create,把角色设为owner,指定john@example.com的邮箱,加上transferOwnership=True

四、代码优化点

  • 合并认证逻辑:Sheets和Drive用同一个服务账户凭证就行,不用分开认证
  • 批量更新Sheet:别一行一行调用update_acell,攒一批一起更,减少API调用
  • 优化文件夹查询:加'me' in owners条件,避免查到其他用户创建的同名文件夹
  • 去掉多余的凭证刷新:服务账户凭证会自动刷新,循环里手动刷没用
修改后的完整代码
import os
import gspread
from google.oauth2 import service_account
from googleapiclient.discovery import build
from datetime import datetime
import time

# Google Sheets配置
SPREADSHEET_ID = "1i4E6V70CVo5cJY2vn1e3YeAmO80ZePt2lYFatlgy5SA"
WORKSHEET_NAME = "gdrive_database"

# Google Drive配置
DRIVE_API_VERSION = 'v3'

# 统一授权Sheets和Drive
SCOPES = ["https://www.googleapis.com/auth/spreadsheets", "https://www.googleapis.com/auth/drive"]
credentials = service_account.Credentials.from_service_account_file(
    "/Users/kpi-updates-411718-82c6069f468f.json", 
    scopes=SCOPES
)

# 初始化服务
gc = gspread.authorize(credentials)
worksheet = gc.open_by_key(SPREADSHEET_ID).worksheet(WORKSHEET_NAME)
drive_service = build('drive', DRIVE_API_VERSION, credentials=credentials)

# 获取所有行数据(表头在第1行,数据从第2行开始)
rows = worksheet.get_all_records()
start_row = 2

# 批量更新Sheet的缓存
update_batch = []

for idx, row in enumerate(rows):
    current_row_num = start_row + idx
    access_cell = f'I{current_row_num}'
    
    # 跳过已处理的行
    if worksheet.acell(access_cell).value:
        print(f"跳过行 {current_row_num}: 已处理")
        continue
    
    email = row['email']
    user_id = row['user_id']
    folder_name = f"Course Workbooks - {row['first_name']} - {user_id}"
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    # 检查当前服务账户名下的同名文件夹
    folders = drive_service.files().list(
        q=f"name='{folder_name}' and mimeType='application/vnd.google-apps.folder' and trashed=false and 'me' in owners",
        fields='files(id)'
    ).execute().get('files', [])
    
    if folders:
        folder_id = folders[0]['id']
        print(f"行 {current_row_num}: 文件夹已存在,ID: {folder_id}")
    else:
        # 创建新文件夹
        folder_metadata = {
            'name': folder_name,
            'mimeType': 'application/vnd.google-apps.folder',
            'parents': ['root']
        }
        folder = drive_service.files().create(body=folder_metadata, fields='id').execute()
        folder_id = folder['id']
        print(f"行 {current_row_num}: 创建新文件夹,ID: {folder_id}")
        
        # 转移所有权给john@example.com(必须是Workspace账户)
        owner_permission = {
            'type': 'user',
            'role': 'owner',
            'emailAddress': 'john@example.com'
        }
        drive_service.permissions().create(
            fileId=folder_id,
            body=owner_permission,
            transferOwnership=True,
            fields='id'
        ).execute()
        time.sleep(1)  # 加延迟避免配额超限

    # 复制模板文件到文件夹
    template_file_id = "1iIKXMgJqySm__SUyP08x4GOAlvIXVdfBXzq_pA_DcLM"
    copy_metadata = {'parents': [folder_id]}
    copied_file = drive_service.files().copy(
        fileId=template_file_id,
        body=copy_metadata,
        fields='id'
    ).execute()
    copied_file_id = copied_file['id']

    # 给用户添加编辑权限
    user_permission = {
        'type': 'user',
        'role': 'writer',
        'emailAddress': email
    }
    drive_service.permissions().create(
        fileId=folder_id,
        body=user_permission,
        fields='id'
    ).execute()
    time.sleep(1)

    # 收集批量更新数据
    update_batch.extend([
        {'range': f'F{current_row_num}', 'values': [[folder_id]]},
        {'range': f'G{current_row_num}', 'values': [[copied_file_id]]},
        {'range': f'H{current_row_num}', 'values': [[timestamp]]},
        {'range': f'I{current_row_num}', 'values': [['Yes']]}
    ])

# 批量更新Sheet
if update_batch:
    worksheet.batch_update(update_batch)
    print("批量更新完成")
else:
    print("没有需要更新的行")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 09:54:55