解决Google Drive API的403共享配额超限及权限配置问题
问题说明
我有一段脚本,用来批量处理Google Sheet里的用户数据,逻辑是:
- 检查是否存在
Course Workbooks - [First Name] - [User ID]命名的文件夹 - 不存在就创建
- 把指定模板文件复制到这个文件夹里
- 给用户和
john@example.com加文件夹编辑权限(文件默认归服务账户所有) - 更新Sheet的
access列为yes,跳过已处理的行
现在碰到两个问题:
- 逻辑没问题,但频繁报403共享配额超限;换成
drive.file权限范围后直接报错 - 尝试把
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
相关产品推荐
相关产品推荐

