如何用Apps Script从沃尔玛提取贵金属产品实时数据并计算均价?
沃尔玛产品平均价格抓取代码优化建议
需求与背景
需实现沃尔玛平台产品查询:按“metadata”筛选产品,取前5条计算平均价格,已配置每8小时执行的触发器。核心限制是沃尔玛无法通过IMPORTXML等简单方式获取可靠的metadata标签,可选实现方案包括:
- 结构化搜索结果HTML抓取
- 产品API(优先推荐)
- 搜索→解析→标题关键词筛选的混合方案
期望输出格式:
Date Collected | SPOT Gold Price | SPOT Silver Price | Walmart Gold Price | Walmart Silver Price
现有代码的优化建议
针对你提供的Google Apps Script代码,以下是具体优化方向:
1. 增强错误处理与容错能力
- 新增HTTP响应状态码校验,避免无效HTML解析
- 对数据路径做多层容错,防止沃尔玛页面结构变更导致脚本崩溃
- 为空结果添加日志记录,便于排查筛选逻辑问题
2. 优化筛选逻辑(模拟metadata筛选)
当前仅靠searchTerm首词+“bar”的筛选逻辑过于简陋,可优化为:
- 支持配置自定义关键词列表(比如针对黄金/白银的规格、纯度关键词)
- 区分产品类型(如黄金条、白银条)的精准匹配
- 可选添加价格区间过滤,排除异常低价/高价商品
3. 提升数据稳定性
- 处理沃尔玛价格字段的多种格式(部分商品价格可能嵌套在
priceInfo对象中,或带有货币符号) - 对
__NEXT_DATA__的提取正则做兼容,适配可能的HTML转义差异 - 添加数据去重逻辑,避免重复商品影响平均价格计算
4. 合规性与性能优化
- 使用更贴近真实浏览器的
User-Agent字符串,降低反爬拦截概率 - 批量调用时添加请求间隔,避免触发沃尔玛的访问限制
- 为触发器配置错误通知,及时发现执行异常
5. 适配期望输出格式
新增日期获取、现货价格对接逻辑,将计算出的平均价格映射到对应字段,生成符合要求的表格行数据
优化后的代码示例
function getWalmartAverage(searchTerm, config = {}) { // 默认配置:可根据需求调整关键词、价格区间 const defaultConfig = { requiredKeywords: [searchTerm.split(" ")[0], "bar"], minPrice: 10, maxPrice: 10000 }; const { requiredKeywords, minPrice, maxPrice } = {...defaultConfig, ...config}; const query = encodeURIComponent(searchTerm); const url = `https://www.walmart.com/search?q=${query}`; const response = UrlFetchApp.fetch(url, { muteHttpExceptions: true, headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36", "Accept-Language": "en-US,en;q=0.9" } }); // 校验HTTP响应状态 const statusCode = response.getResponseCode(); if (statusCode < 200 || statusCode >= 300) { console.error(`请求失败,状态码:${statusCode}`); return null; } const html = response.getContentText(); // 提取Next.js数据,兼容可能的转义差异 const jsonMatch = html.match(/<script id="__NEXT_DATA__" type="application\/json">(.*?)<\/script>/s); if (!jsonMatch) { console.error("无法提取沃尔玛页面数据"); return null; } let data; try { data = JSON.parse(jsonMatch[1]); } catch (e) { console.error("JSON解析失败:", e.message); return null; } // 多层容错获取商品列表 const itemStacks = data?.props?.pageProps?.initialData?.searchResult?.itemStacks || data?.props?.pageProps?.searchResult?.itemStacks || []; let items = []; itemStacks.forEach(stack => { stack.items?.forEach(item => { if (!item?.name) return; // 处理不同格式的价格字段 let price = null; if (typeof item.price === "number") { price = item.price; } else if (typeof item.price === "string") { price = parseFloat(item.price.replace(/[^0-9.]/g, "")); } else if (item?.priceInfo?.currentPrice) { price = parseFloat(item.priceInfo.currentPrice.amount); } if (!isNaN(price) && price > 0) { items.push({ name: item.name.toLowerCase(), price: price, id: item?.id || item?.usItemId // 用于去重 }); } }); }); // 去重 const uniqueItems = Array.from(new Map(items.map(item => [item.id, item])).values()); // 多关键词筛选 const filtered = uniqueItems.filter(p => { return requiredKeywords.every(keyword => p.name.includes(keyword.toLowerCase())); }); // 价格区间过滤+取前5条 const clean = filtered .filter(p => p.price >= minPrice && p.price <= maxPrice) .sort((a, b) => a.price - b.price) // 按价格排序,可选 .slice(0, 5); if (clean.length === 0) { console.warn(`未找到符合条件的${searchTerm}商品`); return null; } const avg = clean.reduce((sum, p) => sum + p.price, 0) / clean.length; return Number(avg.toFixed(2)); } // 生成期望格式的表格行示例 function generatePriceRow() { const dateCollected = new Date().toISOString().split("T")[0]; // 假设已有获取现货价格的函数getSpotPrices() const spotPrices = getSpotPrices(); // 需自行实现该函数 const walmartGoldAvg = getWalmartAverage("gold bar", { minPrice: 50, maxPrice: 5000 }); const walmartSilverAvg = getWalmartAverage("silver bar", { minPrice: 10, maxPrice: 1000 }); return `${dateCollected} | ${spotPrices.gold} | ${spotPrices.silver} | ${walmartGoldAvg || "N/A"} | ${walmartSilverAvg || "N/A"}`; }
额外建议
优先考虑使用沃尔玛官方API替代网页抓取,API提供稳定的metadata字段和产品数据,避免页面结构变更导致的脚本失效,同时符合平台使用规范。
内容的提问来源于stack exchange,提问作者nic.db
相关产品推荐
相关产品推荐

