Google Apps Script urlfetch调用超限与执行超时问题咨询
TRIVIA接口批量查询限流问题修复方案
问题根因
- 原代码使用
UrlFetchApp.fetchAll一次性并发所有请求,短时间请求量触发Google Apps Script的UrlFetch服务限流,同时目标站点triviapw自身也有接口访问频率限制 - 此前修改版本出现「无效请求」是因为请求参数格式和测试通过的单请求版本不一致,出现「超出最大执行时间」是因为全量串行请求加长等待,总耗时超过Apps Script单次执行6分钟的上限
- 单请求可正常运行,说明接口本身、参数逻辑没有问题,核心是平衡请求频率和执行时长
修复逻辑
- 采用小批量并发+批次间限流的模式:每次发少量并发请求,批次间加1秒以上间隔,既不触发限流规则,也比单条串行请求效率高,避免超时
- 请求参数完全对齐单请求测试通过的格式,杜绝无效请求
- 提前过滤A列ID为空的无效行,减少不必要的请求消耗
- 所有结果收集完成后一次性写入表格,减少表格API调用,压缩总执行时长
可直接运行的修复代码
function myTrivia() { const f = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('TRIVIA'); // 配置项:可根据实际限流情况调整 const BATCH_SIZE = 8; // 每批并发请求数,触发限流就调小(最小为1) const REQ_INTERVAL = 1200; // 批次间等待毫秒数,留200ms冗余避免边界触发限流 const MAX_EXE_TIME = 300000; // 单次执行最大安全时长5分钟,留1分钟余量写数据 const startTime = new Date().getTime(); // 初始化日期 const today = new Date(); const data = Utilities.formatDate(today, "GMT-03:00", "yyyy/MM/dd"); f.getRange('F1').setValue(data); const serverName = f.getRange('G1').getValue(); // 读取有效数据,过滤空ID行 const allData = f.getDataRange().getValues(); const [header, ...rows] = allData; const validRows = rows.filter(row => row[0] && String(row[0]).trim() !== ''); const totalCount = validRows.length; const allResults = []; const url = 'https://triviapw.com.br/cotacao/api/item/'; // 分批处理请求 for (let i = 0; i < totalCount; i += BATCH_SIZE) { // 超时判断:如果剩余时间不足以处理下一批+写数据,提前终止(可配合触发器续跑) if (new Date().getTime() - startTime > MAX_EXE_TIME) { console.log(`执行时长接近上限,已处理${i}条,剩余${totalCount - i}条请配合时间驱动触发器续跑`); break; } // 构造当前批次的请求,参数完全对齐单请求测试版本 const batchRows = validRows.slice(i, i + BATCH_SIZE); const batchReqs = batchRows.map(([itemId]) => ({ url: url, method: "post", payload: { id: itemId, date: data, server: serverName }, muteHttpExceptions: true })); // 执行当前批次请求 const batchRes = UrlFetchApp.fetchAll(batchReqs); // 解析当前批次结果 batchRes.forEach(res => { const resText = res.getContentText(); if (res.getResponseCode() === 200 && resText !== "Requisição inválida") { const obj = JSON.parse(resText); allResults.push([obj.nome, obj.venda, obj.compra]); } else { allResults.push(Array(3).fill("")); } }); // 非最后一批次时等待限流间隔 if (i + BATCH_SIZE < totalCount) { Utilities.sleep(REQ_INTERVAL); } } // 一次性写入所有结果 if (allResults.length > 0) { f.getRange(2, 2, allResults.length, 3).setValues(allResults); } }
调优说明
- 如果运行仍然触发urlfetch限流错误,将
BATCH_SIZE调小至3-5,或将REQ_INTERVAL调整为1500-2000 - 如果表格数据量超过300条,可搭配Apps Script时间驱动触发器,设置每5分钟运行一次,每次记录上次处理到的行号续跑,完全规避执行时长限制
- 运行前确认F1日期格式、G1服务器参数和单请求测试时的取值完全一致,避免参数错误导致的无效请求
内容的提问来源于stack exchange,提问作者Wagner Vaz
相关产品推荐
相关产品推荐

