Python/gspread APIErrorでQuota exceededでも処理継続する

- Python -
2023.09.06
Python[パイソン]

Python × gspreadモジュールでスプレッドシート書き込みをしたらこんなエラーがでて処理が途中で止まってしまった。

Traceback (most recent call last):
File "/Users/yuki/Desktop/test.py", line 48, in <module>
sheet.update_cell(i, 1, "test")
File "/usr/local/lib/python3.9/site-packages/gspread/worksheet.py", line 669, in update_cell
data = self.spreadsheet.values_update(
File "/usr/local/lib/python3.9/site-packages/gspread/spreadsheet.py", line 226, in values_update
r = self.client.request("put", url, params=params, json=body)
File "/usr/local/lib/python3.9/site-packages/gspread/client.py", line 92, in request
raise APIError(response)
gspread.exceptions.APIError: {'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:396061378843'.", 'status': 'RESOURCE_EXHAUSTED', 'details': [{'@type': 'type.googleapis.com/google.rpc.ErrorInfo', 'reason': 'RATE_LIMIT_EXCEEDED', 'domain': 'googleapis.com', 'metadata': {'quota_limit': 'WriteRequestsPerMinutePerUser', 'service': 'sheets.googleapis.com', 'quota_metric': 'sheets.googleapis.com/write_requests', 'consumer': 'projects/396061378843', 'quota_location': 'global', 'quota_limit_value': '60'}}, {'@type': 'type.googleapis.com/google.rpc.Help', 'links': [{'description': 'Request a higher quota limit.', 'url': 'https://cloud.google.com/docs/quota#requesting_higher_quota'}]}]}

これは、Google SpreadSheet APIの「1分間で60回の書き込み制限」が原因で起こります。

一番単純な例だと、こういうふう↓にループを回しつつ1セルずつ書き込まざるを得ない場合に発生しやすい。

test.pyfor i in range(1, 100):
    sheet.update_cell(i, 1, "test")

Google様にお金を払ってQuota上限を上げてもらう手もあるんでしょうが、やはりお金はかけたくない。

それならgspread.exceptions.APIErrorをtry - exceptで拾ってあげればいいんですが、ちょいとハマったことがあったので「こうしたら処理が継続できた」という例をメモしておきます。

gspread.exceptions.APIError × Quota exceededで処理継続

結論これでできたっていうコードを先に書いておきますが、ポイントは26行目、except句内で60秒以上待った後にシートを取得しなおすことでした。

test.pyimport gspread
from oauth2client.service_account import ServiceAccountCredentials
from time import sleep

# シート取得
def get_sheet():
    scope = ['https://www.googleapis.com/auth/spreadsheets']
    creds = ServiceAccountCredentials.from_json_keyfile_name('★credentials.json★', scope)
    client = gspread.authorize(creds)
    sheet = client.open_by_key('★xxx★').worksheet('★シート名★')
    return sheet

sheet = get_sheet()

for i in range(1, 200):
    try:
        sheet.update_cell(i, 1, "test")
    except gspread.exceptions.APIError as e:
        error_json = e.response.json()
        error_status = error_json.get('error', {}).get('status')
        print('Error Status: ' + error_status)
        if error_status == 'RESOURCE_EXHAUSTED':
            print("制限を超えました。待ちます。")
            sleep(65)
            # シートを再取得するのがポイント
            sheet = get_sheet()
            print("再開します。")
            sheet.update_cell(i, 1, "test")
        else:
            print("想定外のエラー")
            raise Exception
    except Exception as e:
        print(e)

APIErrorを拾って60秒以上待っても、シートを再取得せずに28行目sheet.update_cellをすると、100秒待っても200秒待っても直後にAPIError:Quota Exceededが出続けてしまって「あれ?」ってなったのがハマった点でした。

シートを取得しなおすことで、同じ例外が発生するたびに待つ→続きのセルから処理を継続する→待つ→処理継続...することに無事成功。

~$ python test.py
Error Status: RESOURCE_EXHAUSTED
制限を超えました。待ちます。
再開します。
Error Status: RESOURCE_EXHAUSTED
制限を超えました。待ちます。
再開します。
...以下略...

お試しあれ。

↑TOP