Python/gspread APIErrorでQuota exceededでも処理継続する
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
制限を超えました。待ちます。
再開します。
...以下略...
お試しあれ。