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

Python脚本调用Google Sheets API遭遇写入配额超限问题咨询

Google Sheets批量更新触发429配额超限问题排查与解决

问题重现

我编写了以下Python函数,意图每3秒更新一次Google表格:

def update_gsheet():
    global ce_trade_status, pe_trade_status
    while ce_trade_status != 'completed' or pe_trade_status != 'completed':
        try:
            batch_update_values = [
                {"range": "A2", "values": [[selectedCEStrike]]},
                {"range": "B2", "values": [[ce_sell_time]]},
                {"range": "C2", "values": [[ce_sell_price]]},
                {"range": "D2", "values": [[ce_sl_price]]},
                {"range": "E2", "values": [[ce_buy_time]]},
                {"range": "F2", "values": [[ce_buy_price]]},
                {"range": "G2", "values": [[ce_exit_reason]]},
                {"range": "H2", "values": [[ce_limit_price]]},
                {"range": "I2", "values": [[ltp[selectedCEStrike]]]},
                {"range": "A3", "values": [[selectedPEStrike]]},
                {"range": "B3", "values": [[pe_sell_time]]},
                {"range": "C3", "values": [[pe_sell_price]]},
                {"range": "D3", "values": [[pe_sl_price]]},
                {"range": "E3", "values": [[pe_buy_time]]},
                {"range": "F3", "values": [[pe_buy_price]]},
                {"range": "G3", "values": [[pe_exit_reason]]},
                {"range": "H3", "values": [[pe_limit_price]]},
                {"range": "I3", "values": [[ltp[selectedPEStrike]]]},
            ]
            sh.batch_update(batch_update_values, value_input_option="USER_ENTERED")
        except Exception as e:
            logger.error(e)
            sys.exit(1)
    time.sleep(3)

但触发了WriteRequestsPerMinutePerUser的429错误:

{'code': 429, 'message': "Quota exceeded for quota metric 'Write requests' and limit 'Write requests per minute per user' of service 'sheets.googleapis.com' for consumer 'project_number:XXXX'.", 'status': 'RESOURCE_EXHAUSTED', 'details': [{'@type': 'type.googleapis.com/google.rpc.ErrorInfo', 'reason': 'RATE_LIMIT_EXCEEDED', 'domain': 'googleapis.com', 'metadata': {'service': 'sheets.googleapis.com', 'quota_location': 'global', 'quota_limit': 'WriteRequestsPerMinutePerUser', 'consumer': 'projects/XXXX', 'quota_metric': 'sheets.googleapis.com/write_requests', 'quota_limit_value': '120'}}, {'@type': 'type.googleapis.com/google.rpc.Help', 'links': [{'description': 'Request a higher quota limit.', 'url': 'https://cloud.google.com/docs/quota#requesting_higher_quota'}]}]}

当前WriteRequestsPerMinutePerUser配额为每分钟120次,使用batch_update本应整组更新算单次请求,设置3秒延迟后仍超配额,已提交配额提升申请,需其他解决办法。

核心原因

你的代码中time.sleep(3)放在while循环外部,只有当ce_trade_status和pe_trade_status都变为completed后才会执行一次sleep。这意味着,只要交易状态未完成,循环会无延迟地反复发送batch_update请求,短时间内请求量会远超每分钟120次的配额上限,直接触发429错误。

修复方案

1. 调整sleep位置到循环内部

将time.sleep(3)移到while循环的每次迭代中,确保每次请求后都等待3秒,这样每分钟最多发送20次请求,远低于120次的配额:

def update_gsheet():
    global ce_trade_status, pe_trade_status
    while ce_trade_status != 'completed' or pe_trade_status != 'completed':
        try:
            batch_update_values = [
                {"range": "A2", "values": [[selectedCEStrike]]},
                {"range": "B2", "values": [[ce_sell_time]]},
                # ... 省略其他项
                {"range": "I3", "values": [[ltp[selectedPEStrike]]]},
            ]
            sh.batch_update(batch_update_values, value_input_option="USER_ENTERED")
            # 每次请求后等待3秒
            time.sleep(3)
        except Exception as e:
            logger.error(e)
            # 遇到错误不要直接退出,可添加重试逻辑
            time.sleep(5)

2. 优化batch_update请求结构

当前的batch_update拆分成了18个独立range,虽然仍算1次请求,但可以合并连续单元格减少操作数,让请求更高效:

# 合并A2:I2和A3:I3的连续范围
batch_update_values = [
    {"range": "A2:I2", "values": [[
        selectedCEStrike, ce_sell_time, ce_sell_price, ce_sl_price,
        ce_buy_time, ce_buy_price, ce_exit_reason, ce_limit_price, ltp[selectedCEStrike]
    ]]},
    {"range": "A3:I3", "values": [[
        selectedPEStrike, pe_sell_time, pe_sell_price, pe_sl_price,
        pe_buy_time, pe_buy_price, pe_exit_reason, pe_limit_price, ltp[selectedPEStrike]
    ]]}
]

3. 添加429错误重试机制

遇到配额超限错误时,不要直接退出,而是采用指数退避策略重试,避免浪费配额:

from googleapiclient.errors import HttpError
import time

def update_gsheet():
    global ce_trade_status, pe_trade_status
    retry_delay = 3  # 初始延迟3秒
    max_retries = 5
    while ce_trade_status != 'completed' or pe_trade_status != 'completed':
        try:
            # 构建batch_update请求...
            sh.batch_update(batch_update_values, value_input_option="USER_ENTERED")
            retry_delay = 3  # 请求成功后重置延迟
            time.sleep(retry_delay)
        except HttpError as e:
            if e.resp.status == 429 and max_retries > 0:
                logger.warning(f"配额超限,{retry_delay}秒后重试")
                time.sleep(retry_delay)
                retry_delay *= 2  # 指数退避
                max_retries -= 1
            else:
                logger.error(e)
                break
        except Exception as e:
            logger.error(e)
            time.sleep(5)

4. 仅在数据变化时发送请求

维护一份上次更新的缓存数据,只有当数据实际发生变化时才调用batch_update,避免无意义的重复请求:

# 初始化缓存
last_ce_data = None
last_pe_data = None

def update_gsheet():
    global ce_trade_status, pe_trade_status, last_ce_data, last_pe_data
    while ce_trade_status != 'completed' or pe_trade_status != 'completed':
        # 收集当前数据
        current_ce_data = [
            selectedCEStrike, ce_sell_time, ce_sell_price, ce_sl_price,
            ce_buy_time, ce_buy_price, ce_exit_reason, ce_limit_price, ltp[selectedCEStrike]
        ]
        current_pe_data = [
            selectedPEStrike, pe_sell_time, pe_sell_price, pe_sl_price,
            pe_buy_time, pe_buy_price, pe_exit_reason, pe_limit_price, ltp[selectedPEStrike]
        ]
        
        # 仅当数据变化时更新
        if current_ce_data != last_ce_data or current_pe_data != last_pe_data:
            try:
                batch_update_values = [
                    {"range": "A2:I2", "values": [current_ce_data]},
                    {"range": "A3:I3", "values": [current_pe_data]}
                ]
                sh.batch_update(batch_update_values, value_input_option="USER_ENTERED")
                # 更新缓存
                last_ce_data = current_ce_data.copy()
                last_pe_data = current_pe_data.copy()
            except Exception as e:
                logger.error(e)
        
        time.sleep(3)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 05:09:54