Google App Script解析RSS慢、缓存报错及配额超限问题
Google App Script RSS翻译脚本异常修复
问题根因
- 缓存写入报“值过高”错误:CacheService单条缓存值最大支持100KB,翻译后的完整RSS全文体积普遍超过该限制,直接整段写入必然触发超限报错。
- 无缓存时执行耗时超10分钟:免费版GAS Web应用单次执行硬超时阈值为6分钟,脚本对每篇文章额外加了1秒强制等待,且每篇文章要调用3次翻译接口,单源20篇以上内容的处理时长累加后必然超过平台和对接站点的超时阈值。
- 翻译接口配额超限:免费版LanguageApp每日调用配额为1000次,脚本每处理1篇文章需要调用3次翻译接口(标题、摘要、正文),单源全量处理一次就要消耗近百次配额,多次调试后很容易打满当日额度。
- 偶发启动失败、返回空内容、超时等待:脚本本身存在逻辑缺陷:遍历文章列表时先将节点从父元素移除,一旦某篇内容翻译/解析报错被catch捕获,该节点不会被重新加回RSS结构,直接导致内容丢失;XmlService对非标准RSS格式兼容性差,遇到异常节点直接抛全局解析错误;没有任何请求重试、降级逻辑,第三方接口波动就会触发全流程失败。
修复方案
- 重构缓存逻辑:放弃整段RSS缓存方案,拆分缓存粒度为单篇文章维度,以文章唯一标识+语言对作为缓存key,单条缓存体积远小于100KB限制,同时避免重复翻译相同内容,大幅降低配额消耗和执行时长。
- 优化执行逻辑:移除无意义的1秒强制等待代码;调整节点操作顺序,等单篇内容全部翻译完成后再替换原节点文本,避免异常导致内容丢失;给RSS拉取请求加超时配置和异常捕获,拉取失败时返回友好降级提示。
- 增加超时与配额防护:单次执行仅处理最新的10篇内容,将单次执行时长控制在30秒内,适配站点请求等待阈值;增加翻译接口配额异常捕获,触发限额时返回已处理完成的内容,避免返回空响应。
修复后完整代码
function doGet() { var fromLang = "ru"; var toLang = "en"; var rssFeed = "https://theins.ru/feed.xml"; var feed = parseRSS(rssFeed, fromLang, toLang); return ContentService.createTextOutput(feed) .setMimeType(ContentService.MimeType.RSS); } function parseRSS(feedUrl, fromLang, toLang) { var cache = CacheService.getPublicCache(); var fetchOptions = { muteHttpExceptions: true, timeout: 10 }; var txt; try { txt = UrlFetchApp.fetch(feedUrl, fetchOptions).getContentText(); } catch (e) { return '<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>RSS拉取失败</title></channel></rss>'; } var doc = XmlService.parse(txt); var contentNs = XmlService.getNamespace("http://purl.org/rss/1.0/modules/content/"); var root = doc.getRootElement(); var channel = root.getChild("channel"); channel.getChild("title").setText(channel.getChild("title").getText() + " (" + toLang + ")"); channel.getChild("language").setText(toLang); var link = channel.getChild("link").getText(); link += (link.split("?")[1] ? "&":"?") + "lang=" + toLang; channel.getChild("link").setText(link); var items = channel.getChildren("item"); var processLimit = Math.min(items.length, 10); var quotaExceeded = false; for (var i = 0; i < processLimit; i++) { if (quotaExceeded) break; try { var item = items[i]; var guid = item.getChild("guid").getText(); var itemCacheKey = Utilities.base64Encode(guid + fromLang + toLang); var cachedItem = cache.get(itemCacheKey); var title, desc, cont; if (cachedItem != null) { var cachedData = JSON.parse(cachedItem); title = cachedData.title; desc = cachedData.desc; cont = cachedData.cont; } else { title = item.getChild("title").getText(); desc = item.getChild("description").getText(); cont = item.getChild("encoded", contentNs) ? item.getChild("encoded", contentNs).getText() : ""; title = LanguageApp.translate(title, fromLang, toLang); desc = LanguageApp.translate(desc, fromLang, toLang, {contentType: "html"}); if (cont) { cont = LanguageApp.translate(cont, fromLang, toLang, {contentType: "html"}); } cache.put(itemCacheKey, JSON.stringify({ title: title, desc: desc, cont: cont }), 86400); } item.getChild("title").setText(title); item.getChild("description").setText(desc); var newGuid = guid + (guid.split("?")[1] ? "&":"?") + "lang=" + toLang; item.getChild("guid").setText(newGuid); if (cont && item.getChild("encoded", contentNs)) { item.getChild("encoded", contentNs).setText(cont); } } catch (e) { Logger.log(e); if (e.message && e.message.includes("Quota")) { quotaExceeded = true; } } } return XmlService.getCompactFormat().setEncoding('UTF-8').format(doc); }
内容的提问来源于stack exchange,提问作者uuu
相关产品推荐
相关产品推荐

