Walmart 的搜索结果并非一个简单的平铺列表。在靠近顶部及页面中段,会出现赞助位:卖家付费投放以在某个关键词下曝光的产品。每个赞助位都携带着竞争对手梦寐以求的公开信号, 谁在竞投"headphones",他们排在哪里,定价多少,以及产品名称是什么。随时间追踪,这就是从任何人都可以加载的页面上提取的直接广告和竞争情报。
本指南介绍如何使用 JavaScript 和 Node.js 抓取 Walmart 赞助广告。你将构建一个小型可运行的爬虫,通过 Crawling API 获取渲染后的 Walmart 搜索页面,用 cheerio 将每个赞助位解析为干净的记录(标题、价格、排名、链接),然后汇总这些记录以回答一个问题:哪些产品在某个关键词上投放了广告。整个演练仅限于公开搜索结果数据,文末的合法性部分不是套话,请在将此工具指向真实规模目标前仔细阅读。
你将构建什么
一个 Node.js 脚本,接受 Walmart 公开搜索 URL,通过 Crawling API 获取渲染后的 HTML,并为结果页面上的每个赞助位提取一条结构化记录。我们以搜索"headphones"作为运行示例,每条广告提取以下字段:
- 标题 赞助产品名称,例如"Bose QuietComfort 45 Headphones Noise Cancelling Over-Ear Wireless Bluetooth Earphones, Black"。
- 价格 卡片上显示的标价,如"$329.00"。
- 排名 该广告在页面赞助位中的名次,以便区分搜索顶部竞价和搜索中段竞价。
- 链接 广告指向的单个产品页面 URL。
一旦每条广告都成为一条记录,一个简短的汇总步骤就能按广告主分组,让你看到谁在该关键词上购买了曝光位,以及处于什么排名。这就是广告情报的价值所在:Walmart 已经向每位购物者展示的相同数据,经过重新整理后用于分析。
为何普通请求在 Walmart 上失效
如果你用裸 HTTP 客户端请求 Walmart 搜索 URL,你看不到在浏览器中看到的那个整洁的结果页面。有两个原因让你陷入困境。第一,Walmart 用 JavaScript 在客户端渲染搜索网格,因此赞助卡片、价格和产品链接在页面脚本运行之前并不在初始 HTML 中。第二,Walmart 能快速识别自动化流量:来自数据中心 IP 且请求模式不像真实浏览器的请求,会在抵达渲染内容之前就遭到挑战或封锁。
因此,一个有效的 Walmart 爬虫需要在一次请求中同时具备两样东西:能真正渲染页面的浏览器,以及平台认为是真实访客的 IP。你可以自己组合一个无头浏览器加一批轮换住宅代理,但将它们整合在一起并持续维护才是大头工作。Crawling API 将两者合并为一次调用:你发送 URL,它在受信任的 IP 后渲染页面,并返回完整 HTML 供你解析。它还提供一个 autoparse 选项,可将结构化页面内容作为 JSON 返回,在你自己编写选择器之前快速浏览时很方便。
Walmart 将付费位与自然结果混在同一网格中,并在卡片内用一个小"Sponsored"标签标注付费位。下方的解析器读取该标签,让你能将广告与自然列表分开,而不是将整个网格都视为广告。如果标签文本发生变化,那个选择器就是首先需要重新检查的字段。
前置条件
在编写任何代码之前,你需要准备好以下几样东西,每项都不需要太长时间。
基础 JavaScript 和 Node.js。 你应该能够编写和运行 Node 脚本,并使用 npm 安装包。如果你是 Node 新手,我们关于如何用 Node.js 构建网页爬虫的指南涵盖了本教程所假设的基础知识。
Node.js 16 或更高版本。 用 node --version 确认你的版本。如果还没有安装,请从 Node.js 官网下载,或通过版本管理工具(如 nvm)安装。
Crawlbase 账号和令牌。 注册后打开控制台,从账号文档页面复制你的令牌。免费层包含 1,000 次请求,无需绑定信用卡,足以从头到尾跟完本教程。请像对待密码一样保管令牌:它验证你的请求,不要将其提交到版本控制系统。
搭建项目
创建一个项目文件夹,初始化,并安装爬虫所需的两个库。
node --version mkdir walmart-ads-scraper && cd walmart-ads-scraper npm init -y npm install crawlbase cheerio
两个依赖各司其职:crawlbase 是 Crawling API 的官方 Node 客户端,cheerio 用类似 jQuery 的 API 解析返回的 HTML,让你可以通过 CSS 选择器提取各个字段。如果你对选择器还不熟悉,XPath 和 CSS 选择器入门指南与本文搭配阅读效果很好。
步骤 1:获取渲染后的搜索页面
首先获取完整页面。导入 CrawlingAPI 类,用你的令牌初始化,并请求搜索 URL。在解析前检查状态码,让失败保持可见而非静默。
const { CrawlingAPI } = require('crawlbase'); const api = new CrawlingAPI({ token: 'YOUR_CRAWLBASE_TOKEN' }); const walmartPageURL = 'https://www.walmart.com/search?q=headphones'; api .get(walmartPageURL) .then((response) => { if (response.statusCode === 200) { console.log(response.body.slice(0, 500)); } }) .catch((error) => console.error('API request error:', error));
这段代码通过 Crawling API 请求 Walmart headphones 搜索页面,并打印返回 HTML 的前 500 个字符。用 node scraper.js 运行,你应该能看到真实的搜索网格标记,而非一个剥离后的空壳或挑战页面。这就在你编写任何选择器之前确认了渲染和受信任 IP 都已正常工作。如果你想跳过编写选择器的步骤快速得到初始结果,可以添加 autoparse 选项,即 api.get(walmartPageURL, { autoparse: 'true' }),API 将以 JSON 格式返回页面的主要内容。
第一次调用就在受信任的 IP 后返回了渲染好的 Walmart 网格,只需一次请求。Crawling API 在真实浏览器中运行页面,在服务端通过住宅 IP 轮换,并将完整 HTML(或自动解析的 JSON)返回给你,省去了自己运行无头浏览器集群和代理池的麻烦。先在免费的 1,000 次请求额度上将其指向一个公开搜索页面试试。
步骤 2:用 cheerio 解析赞助位
拿到渲染后的 HTML,将其加载到 cheerio 中并遍历结果网格。Walmart 将每个结果布局在重复的产品容器中,因此你选取所有卡片,读取"Sponsored"标签,只保留携带该标签的卡片。对于每张赞助卡片,你提取标题、价格、排名和链接。防御性地读取每个字段,避免单个缺失值导致整个运行崩溃。
const cheerio = require('cheerio'); function parseSponsored(html) { const $ = cheerio.load(html); const ads = []; let position = 0; const containers = $('.sans-serif.mid-gray.relative.flex.flex-column.w-100.hide-child-opacity'); containers.each((index, element) => { const card = $(element); // Only keep cards tagged "Sponsored" const label = card.find('[data-testid^="variant-"] .gray').text().trim(); if (!/sponsored/i.test(label)) return; position += 1; const title = card .find('[data-automation-id="product-title"]') .text() .replace(/\s+/g, ' ') .trim(); const priceString = card .find('[data-automation-id="product-price"] .w_iUH7') .text() .trim(); const priceMatch = priceString.match(/([^\d]+)([\d,\.]+)/); const price = priceMatch ? `${priceMatch[1].trim()}${priceMatch[2]}` : ''; const href = card.find('a[link-identifier]').attr('href') || ''; const link = href && href.startsWith('/') ? `https://www.walmart.com${href}` : href; ads.push({ position, title, price, link }); }); return ads; }
这些选择器直接来自 Walmart 的搜索标记。每个结果都位于长实用类容器 .sans-serif.mid-gray.relative.flex.flex-column.w-100.hide-child-opacity 中;"Sponsored"标签位于 [data-testid^="variant-"] .gray span 中;标题是 [data-automation-id="product-title"];价格字符串位于 [data-automation-id="product-price"] .w_iUH7 内,我们用正则表达式将其拆分为货币符号和数字部分。提前 return 丢弃没有赞助标签的卡片,因此 position 只计算付费位,从搜索顶部开始,搜索中段在后。链接从卡片锚点的 href 读取,当 Walmart 返回相对路径时转换为绝对链接。
Walmart 的类名和 data-testid 值随时可能在不通知的情况下更改,上方那个长实用类容器尤其脆弱,因为它是生成样式而非稳定钩子。请将这些选择器视为起始模板,而非固定合约。当字段返回为空时,请在浏览器开发者工具中重新检查实时页面并更新选择器。定期维护选择器对任何生产爬虫来说都是正常操作,而非故障的表现。
步骤 3:组合脚本并分析广告主
现在将获取和解析连接成一个可运行的脚本,然后添加分析步骤。一旦每张赞助卡片都成为一条记录,按产品页面链接分组就能告诉你哪些广告主在该关键词上购买了曝光位,以及他们的排名。
const { CrawlingAPI } = require('crawlbase'); const cheerio = require('cheerio'); const api = new CrawlingAPI({ token: 'YOUR_CRAWLBASE_TOKEN' }); async function crawl(pageUrl) { const response = await api.get(pageUrl); if (response.statusCode === 200) return response.body; console.error(`Request failed: ${response.statusCode}`); return null; } // parseSponsored from Step 2 goes here function advertiserReport(ads) { return ads.map((ad) => ({ advertiser: ad.title.split(' ').slice(0, 2).join(' '), position: ad.position, price: ad.price, link: ad.link, })); } async function main() { const keyword = 'headphones'; const url = `https://www.walmart.com/search?q=${encodeURIComponent(keyword)}`; const html = await crawl(url); if (!html) return; const ads = parseSponsored(html); console.log(`${ads.length} sponsored placements on "${keyword}"`); console.log(JSON.stringify(advertiserReport(ads), null, 2)); } main();
advertiserReport 步骤刻意保持简单:它从每个产品标题的前几个单词推导出一个简短的广告主标识,并将排名、价格和链接一并保留。在真实流水线中,你会基于更稳定的字段来标识广告主(品牌字段或从产品页面解析出的卖家),但即便这种粗略分组也能回答核心问题:谁在该关键词上付费曝光,处于什么排名。按计划定期运行多个关键词,这些记录就变成了竞争数据馈送:新广告主进入某个词,赞助位价格变动,排名位移。
输出结果示例
用 node scraper.js 运行完整脚本,你将得到按排名排序的赞助位干净数组,可直接写入 JSON、CSV 或数据库。
[ { "advertiser": "Bose QuietComfort", "position": 1, "price": "$329.00", "link": "https://www.walmart.com/ip/Bose-QuietComfort-45-Headphones/376188834" }, { "advertiser": "COWIN E7", "position": 2, "price": "$35.00", "link": "https://www.walmart.com/ip/COWIN-E7-Active-Noise-Cancelling-Headphones/123456789" }, { "advertiser": "OneOdio Wired", "position": 3, "price": "$31.99", "link": "https://www.walmart.com/ip/OneOdio-Wired-Over-Ear-Headphones/950096760" } ]
每行代表一个赞助位:谁在投放广告、在付费位中排名多少、定价多少,以及广告指向的产品页面。每次运行附上时间戳后存储,这就是关键词级广告情报的原始素材。
跨关键词和页面扩展规模
单个关键词的单页只是演示;真实任务需要遍历关键词列表及每个关键词背后的分页。Walmart 通过 page 查询参数暴露页码,因此你可以在循环中构建每页 URL,通过 Crawling API 获取,用同一函数解析,并收集所有行。由于每个结果页面共享相同的卡片结构,你已编写的解析器无需任何修改即可在所有页面上工作。
async function scrapeKeyword(keyword, totalPages) { const all = []; for (let page = 1; page <= totalPages; page++) { const url = `https://www.walmart.com/search?q=${encodeURIComponent(keyword)}&page=${page}`; const html = await crawl(url); if (html) all.push(...parseSponsored(html)); } return all; } scrapeKeyword('headphones', 3).then((rows) => { console.log(`Collected ${rows.length} sponsored placements`); });
将单个关键词替换为一个数组并循环遍历你的目标词,就能构建整个品类的竞争地图。要用完整详情(品牌、卖家、评分、完整规格列表)丰富每条广告,取每条记录的 link,通过同一 crawl 函数获取该产品页面,然后为产品布局编写一个小型解析器。模式完全一致:渲染,然后解析。这种先获取后解析的方式可以直接延伸到更广泛的电商网页抓取工作,将这些价格输入模型也是价格情报的自然延伸。
保持不被封锁
即便渲染问题已经解决,Walmart 仍然会监控爬虫特征的流量。以下几个习惯能保持运行健康,适用于任何高防御商业目标。
- 控制请求速率。 在紧密循环中快速请求页面是被限速的最快方式。分散请求,变换关键词,而不是以全速爬取单一路径。
- 依赖轮换。 住宅 IP 池将请求分散到众多真实用户地址,使任何单个地址都不会触发速率限制。Crawling API 为你处理这些;如果你自己搭建,这是最需要做好的部分。
- 关注状态码。 运行中开始返回挑战或错误,说明当前速率或 IP 层级已不够用。将其视为回退信号,而非可忽略的噪音。
关于更全面的应对方案,请参阅如何在不被封锁的情况下抓取网站。如果你更倾向于通过轮换池自己路由流量,而不是使用托管 API,Smart AI Proxy 提供与托管 API 相同的住宅 IP 轮换,作为直接代入的代理端点。
抓取 Walmart 广告数据合法吗?
抓取 Walmart 赞助广告是否被允许取决于 Walmart 的服务条款、你所在的司法管辖区以及你如何使用这些数据。Walmart 的使用条款限制自动化访问,因此无论你的工具多么谨慎,抓取行为都可能与这些条款相悖。此处的代码不会改变这一点,它只是让技术部分得以运作。请阅读 Walmart 的使用条款和 robots.txt,并将两者都视为你可以收集哪些内容的边界。
以下几条值得遵守。只收集公开的搜索结果数据:任何人无需账户即可看到的赞助标题、价格、排名和产品链接。"Sponsored"标签和排名是 Walmart 已经向每位购物者展示的公开信号,这正是在这个维度上做广告情报具有可辩护性的原因。涉及产品图片和广告文案时请注意版权,这些是卖家和 Walmart 的知识产权,因此应当分析而非以自己的名义重新发布。遵守 Walmart 规定的速率预期,并将请求量控制在不对其服务器造成压力的范围内。完全避免个人数据,包括任何与可识别买家或卖家相关的信息(公开列表上的内容除外)。如果你计划将数据用于商业用途,请获得许可或正式协议,而非假设沉默即同意。
对于大规模或商业用途,正确的途径是官方渠道:Walmart 运营 Connect 广告平台,为广告主提供自有报告;Walmart.io 开发者项目则提供经授权的 API。当你需要大量数据、保证结构或商业权益时,这些才是正确的工具。本指南刻意将范围限定在公开搜索和赞助位数据,因为这是让工作保持可辩护的界限。它不涵盖任何需要登录的内容、买家或卖家个人数据、内部广告系列指标(如其他广告主的花费或点击率),以及任何绕过身份验证的尝试。如果你的项目需要超出公开位置的内容,Walmart 的官方广告工具或数据协议才是正确的途径,而非更聪明的爬虫。
核心要点
- Walmart 在客户端渲染搜索结果。 普通请求返回不完整的页面,因此必须先渲染,Crawling API 在一次调用中在受信任的 IP 后完成这项工作。
-
按"Sponsored"标签过滤。 读取每张卡片内的
[data-testid^="variant-"] .gray标签,只保留付费位,从而将广告与自然结果分开。 -
捕获标题、价格、排名和链接。 Walmart 的
product-title和product-price钩子提供这些字段;只计算赞助卡片就能得到排名。 - 按广告主分组以获取情报。 将记录按产品或品牌汇总,回答谁在某个关键词上投放广告、处于什么排名,并随时间追踪,作为竞争数据馈送。
- 只收集公开数据。 遵守 Walmart 的服务条款和 robots.txt,大规模或商业用途优先使用 Walmart Connect 或 Walmart.io 官方工具,切勿触及登录、个人数据或其他广告主的私人广告系列指标。
常见问题
Walmart 上的赞助商品是什么?
赞助商品是广告主付费让其在 Walmart 搜索结果或分类页面中排名更靠前的产品。它们与自然列表位于同一网格中,但带有一个小"Sponsored"标签,是 Walmart 广告平台的一部分,卖家在该平台上竞价让自己的产品出现在搜索特定关键词的购物者面前。由于曝光位和标签对每位购物者都是可见的,它们是你可以从页面上读取的公开信号。
为何普通请求会从 Walmart 返回不完整的数据?
因为 Walmart 用 JavaScript 在客户端渲染搜索网格。初始 HTML 在页面脚本在浏览器中运行之前是不完整的,因此裸 HTTP 请求返回的页面,赞助卡片、价格和链接全都缺失或为空;Walmart 还经常向自动化流量提供挑战页面。通过 Crawling API 在受信任的 IP 后渲染页面,你将获得可解析的完整网格。
我的选择器返回空字符串。是什么变了?
几乎可以肯定是 Walmart 的标记发生了变化。长实用类容器、data-testid 值和 w_iUH7 价格类随时可能在不通知的情况下更改,因此上个月有效的选择器可能已失效。生成的实用类容器是其中最脆弱的。请在浏览器开发者工具中重新检查实时页面并更新选择器。定期维护选择器对任何生产爬虫来说都是正常操作。
如何知道哪些产品在某个关键词上投放了广告?
只解析携带"Sponsored"标签的卡片,捕获每张卡片的标题、价格、排名和产品链接,然后按广告主对记录分组。按计划运行相同关键词并对比各次运行结果,就能看到新广告主进入该词、赞助位价格变动和排名变化。这种关键词级别的汇总就是本指南最终要构建的广告情报输出。
我可以获取其他广告主的花费或点击率吗?
不可以,本指南也不涵盖这一内容。花费、展示量和点击率是存在于广告主自己账户内的私人广告系列指标,不在公开结果页面上。爬虫无法获取这些数据,尝试这样做意味着需要访问受登录门控的内容。这类数据的唯一授权来源是你自己在 Walmart Connect 中运行的广告系列报告。
我应该多久收集一次 Walmart 广告数据?
取决于该关键词的变化有多快。对于广告主和价格频繁变化的竞争激烈词,每天或接近每天的运行频率能保持视图的时效性;对于变化较慢的品类,每周通常就足够了。无论选择什么频率,都要保持每个 IP 的请求速率较低,并依靠轮换,这样更快的计划节奏不至于导致被限速。关注状态码,当开始看到挑战时及时回退。
大规模爬取任何站点,无需与基础设施对抗。
Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。
