因为这里的参考资料实在是太少了,我把我的解决过程写下来跟大家分享一下
1.代码报错示例:
- gspread.exceptions.APIError: APIError: [429]: Quota exceeded for quota metric ‘Read requests’ and limit ‘Read requests per minute per user’ of service ‘sheets.googleapis.com’ for consumer ‘project_number:587923780411’.
- gspread.exceptions.APIError: APIError: [429]: Quota exceeded for quota metric ‘Write requests’ and limit ‘Write requests per minute per user’ of service ‘sheets.googleapis.com’ for consumer ‘project_number:587923780411’.
2.解决Read requests
错误代码 429 意味着“请求过多”。
错误信息表明在使用 Google Sheets API 时达到了每分钟读取请求的配额限制。
以下是一些解决此问题的方法:
- 减少请求频率
确保你的代码在每次读取数据时不会频繁调用 API。可以通过添加延迟(例如 time.sleep(1))来控制请求的频率。 - 批量读取数据
尝试一次性读取更多的数据,而不是多次调用 col_values。例如,使用 get_all_values() 方法一次性获取整个工作表的数据。
all_values = sheet.get_all_values()
services_in_sheet = [row[1] for row in all_values] # 获取 B 列
- 优化代码逻辑
检查你的代码逻辑,确保没有不必要的重复请求。例如,避免在循环中多次调用同一个 API 方法。 - 使用服务账户
如果你在使用 API 限制较低的用户帐户,可以考虑使用服务账户来提高请求配额。 - 查看配额限制
访问 Google Cloud Console 查看你的项目的 API 配额,确保你了解当前的限制,并根据需要进行调整。 - 错误处理
实施错误处理机制,捕获 APIError 异常,并在遇到配额限制时进行重试。可以使用指数退避策略来实现。
import time
from gspread.exceptions import APIError
def safe_get_col_values(sheet, col):
while True:
try:
return sheet.col_values(col)
except APIError as e:
if e.response.status_code == 429: # Quota exceeded
print("Quota exceeded. Retrying after a delay...")
time.sleep(60) # 等待 1 分钟后重试
else:
raise # 其他错误则抛出
- 分布请求
如果可能,可以将请求分布到不同的时间段,避免在短时间内发送大量请求。
通过以上方法,你应该能够解决 API 请求过多的问题。如果问题仍然存在,请根据具体情况进行调整。
我使用的方法是批量读取数据:
- 修改前代码
# 修改前代码
# 获取 B 列和 C 列的所有值
services_in_sheet = sheet.col_values(2) # B 列
endpoints_in_sheet = sheet.col_values(3) # C 列
print("Services in Sheet:", services_in_sheet) # 打印 B 列的所有值
print("Endpoints in Sheet:", endpoints_in_sheet) # 打印 C 列的所有值
- 修改后代码
# 修改后代码
# 获取整个工作表的所有值
all_values = sheet.get_all_values()
# 提取 B 列和 C 列的值
services_in_sheet = [row[1] for row in all_values] # B 列
endpoints_in_sheet = [row[2] for row in all_values] # C 列
print("Services in Sheet:", services_in_sheet) # 打印 B 列的所有值
print("Endpoints in Sheet:", endpoints_in_sheet) # 打印 C 列的所有值
3.解决Write requests
“Quota exceeded” 的错误,说明应用程序在短时间内进行了过多的写请求,超出了 Google Sheets API 的配额限制。
为了避免这个问题,可以考虑以下几种解决方案:
- 减少写请求的频率
合并更新:如果可以的话,尽量将多个更新合并成一次写操作。
例如,可以使用 batch_update 方法一次性更新多个单元格。 - 使用批量更新
使用 batch_update 方法可以减少对 API 的调用次数。以下是如何实现的示例:
# 查找符合条件的行
for row_index, row in enumerate(all_values):
if row[1] == SERVICE_URL_MAPPING.get(service) and row[2] == endpoint: # B 列和 C 列匹配
print("Mapped service and endpoint found at row:", row_index + 1)
print("Updating cells...")
# 使用批量更新
cell_updates = [
{'range': f'E{row_index + 1}', 'values': [[max_qps]]}, # E 列
{'range': f'F{row_index + 1}', 'values': [[referer_url]]} # F 列
]
body = {'valueInputOption': 'RAW', 'data': cell_updates}
sheet.batch_update(body)
return
print("Mapped service and endpoint combination not found in the sheet.")
- 实施重试机制
在遇到配额超限的错误时,可以实现一个重试机制,稍后再尝试请求。
可以使用 time.sleep() 来延迟请求。需要使用time库,记得import time
# 查找符合条件的行
for row_index, row in enumerate(all_values):
if row[1] == SERVICE_URL_MAPPING.get(service) and row[2] == endpoint:
print("Mapped service and endpoint found at row:", row_index + 1)
print("Updating cells...")
# 尝试更新,直到成功或达到最大重试次数
max_retries = 5
for attempt in range(max_retries):
try:
cell_updates = [
{'range': f'E{row_index + 1}', 'values': [[max_qps]]},
{'range': f'F{row_index + 1}', 'values': [[referer_url]]}
]
body = {'valueInputOption': 'RAW', 'data': cell_updates}
sheet.batch_update(body)
return # 成功后退出
except gspread.exceptions.APIError as e:
if e.response.status_code == 429: # Quota exceeded
print(f"Quota exceeded. Retrying in {2 ** attempt} seconds...")
time.sleep(2 ** attempt) # 指数退避
else:
raise # 其他错误直接抛出
print("Mapped service and endpoint combination not found in the sheet.")
- 检查配额设置
访问 Google Cloud Console,检查 API 的配额设置,看看是否可以增加配额。
配额一般为300,且最大为300
如何查看配额,请点击
我使用的方法是实施重试机制:
我的代码是挨个写入的,每过20个数据停1s,如果遇到429错误代码就重试。
我使用逐个写入的具体原因如下:
- 我的数据量不大
- 可能是因为我写入的数据包含链接,总是报错Error during batch update: string indices must be integers,
数据格式总是不对但我查了很多资料还是解决不了
我的调试语句:
all_values = sheet.get_all_values()
cell_updates = []
for row_index, row in enumerate(all_values):
if row[1] == mapped_service and row[2] == endpoint:
print("Mapped service and endpoint found at row:", row_index + 1)
cell_updates.append({
'range': f'E{row_index + 1}',
'values': [[max_qps]]
})
cell_updates.append({
'range': f'F{row_index + 1}',
'values': [[referer_url]]
})
break
if cell_updates:
body = {
'valueInputOption': 'RAW',
'data': cell_updates
}
print("Body content:", body) # 打印请求体内容以便调试
print("Cell updates:", cell_updates) # 打印 cell_updates 以便调试
try:
sheet.batch_update(body)
print("Batch update successful.")
except Exception as e:
print("Error during batch update:", e)
import traceback
traceback.print_exc()
else:
print("Mapped service and endpoint combination not found in the sheet.")
报错内容:
Body content: {'valueInputOption': 'RAW', 'data': [{'range': 'E2', 'values': [[844.77]]}, {'range': 'F2', 'values': [['非常长的链接,至少三行']]}]}
Error during batch update: string indices must be integers
上述问题如果有大佬能指导可以在评论区评论,我将万分感谢
所以我的最终代码如下:
for row_index, row in enumerate(all_values):
if row[1] in mapped_services and row[2] == endpoint: # B 列匹配多个映射,C 列匹配
print("Mapped service and endpoint found at row:", row_index + 1)
found_match = True # 找到匹配项
# 每更新 20 个数据等待 1 秒
if row_index % 20 == 0 and row_index != 0:
print("Updated 20 entries, waiting for 1 second...")
time.sleep(1) # 等待 1 秒
print("Updating cells...")
retry_count = 0
max_retries = 5 # 最大重试次数
wait_time = 3 # 初始等待时间
while retry_count < max_retries:
try:
# 更新 E 列
sheet.update_cell(row_index + 1, 5, max_qps) # E 列
# 更新 F 列
sheet.update_cell(row_index + 1, 6, referer_url) # F 列
# 更新 H 列
sheet.update_cell(row_index + 1, 8, h_value) # H 列
print("Update successful.")
break # 成功后退出重试循环
except APIError as error:
if error.response.status_code == 429:
print(f"Rate limit exceeded (429). Waiting for {wait_time} seconds and retrying...")
time.sleep(wait_time)
wait_time *= 2 # 指数退避
retry_count += 1
else:
print(f"An error occurred: {error}")
break # 其他错误直接退出