每个公开的 Amazon 商品页面都是一条密集的结构化记录:标题、当前价格、星级评分、评论数量、库存状态,以及一组商品图片。这正是驱动价格追踪、竞争对手监控、目录丰富和市场研究的数据。问题在于,以任何规模获取这些数据都比看起来难,因为 Amazon 在浏览器中渲染部分页面内容,并对自动化流量进行防御,使请求在到达内容之前就被拦截。

本指南将向你展示如何用 JavaScript 和 Node.js 以可靠的方式抓取 Amazon 商品数据。你将构建一个可运行的小型爬虫,通过 Crawling API 获取公开商品页面,提取商品标题、价格、评分、评论数量、库存状态和图片,并导出干净的 JSON 记录。本指南涵盖两种方法:首先是直接返回结构化字段的内置自动解析器,然后是通过 CSS 选择器读取相同字段的 cheerio 备用方案。整个流程仅涉及公开商品数据,文末的合法性章节不是样板文字,请在正式投入使用前认真阅读。

你将构建什么

一个 Node.js 脚本,接受公开的 Amazon 商品 URL,通过 Crawling API 获取页面,并为该商品生成结构化记录。我们以 PHILIPS A4216 无线运动耳机页面为运行示例,提取以下字段:

  • 标题商品名称,例如"PHILIPS A4216 Wireless Sports Headphones"。
  • 价格当前标价,如"$24.99"。
  • 评分平均顾客评分,例如"4.3 out of 5 stars"。
  • 评论数量该平均分背后的评分数量。
  • 库存状态库存状态行,如"In Stock"或"Currently unavailable"。
  • 图片主图及其他图库图片的 URL。

为什么直接请求 Amazon 会失败

如果你用裸 HTTP 客户端访问 Amazon 商品 URL,你很少能获得在浏览器中看到的干净页面。两个因素对你不利。首先,Amazon 用 JavaScript 渲染部分商品列表,包括一些价格和图库元素,因此在页面脚本运行之前,初始 HTML 可能不完整。其次,Amazon 对自动化流量采取激进的防御措施:来自数据中心 IP 和不像真实浏览器的请求模式,会在到达商品数据之前就收到 CAPTCHA、机器人检测或直接封锁。

因此,一个可用的 Amazon 爬虫每次请求需要同时具备两样东西:一个真实渲染的页面,以及一个平台视为真实访客的 IP。你可以自己组建无头浏览器加上轮换住宅代理池,但将这些整合在一起并保持健康才是主要工作量所在。Crawling API 将两者折叠进单次调用:你发送商品 URL,它在受信任的轮换 IP 后面获取页面,并返回完整 HTML,或者通过一个额外选项直接返回解析为 JSON 的商品字段。

两种解析方式

页面返回后,你有两种选择。传入 scraper 选项,Crawlbase 在服务端运行其内置的 Amazon 解析器,将结构化字段直接交给你,无需维护任何选择器。省略它,你将获得原始 HTML,可用 cheerio 自己解析。本指南两种方法都会介绍:首先是自动解析器,因为它最不脆弱;然后是手动方式,让你了解底层的工作原理。

前提条件

开始编写代码之前,你需要准备好以下几样东西。每项都不需要太长时间。

JavaScript 和 Node.js 基础。你应该能够编写并运行 Node 脚本,以及用 npm 安装包。如果你刚接触 Node,官方文档和任何入门课程将帮助你达到本教程所需的水平。有关更完整的入门,请参阅我们的用 Node.js 构建网络爬虫指南。

Node.js 16 或更高版本。node --version 确认你的版本。如果没有,请从 Node.js 官网安装,或通过 nvm 等版本管理器安装。

Crawlbase 账号和令牌。注册账号,打开 Dashboard,从账号文档页面复制你的令牌。前 1,000 次请求免费且无需绑定信用卡,足以运行这里的所有示例。像对待密码一样对待令牌:它对你的请求进行身份验证,不要将其提交到版本控制系统。

配置项目

创建项目文件夹,初始化它,并安装爬虫所需的两个库。

bash
node --version

mkdir amazon-scraper && cd amazon-scraper
npm init -y

npm install crawlbase cheerio

两个依赖各司其职:crawlbase 是 Crawling API 的官方 Node 客户端,cheerio 以类 jQuery 的 API 解析返回的 HTML,让你可以通过 CSS 选择器提取字段。自动解析路径只需要 crawlbase;手动备用方案还需要 cheerio。如果你对选择器不熟悉,XPath 和 CSS 选择器入门是很好的补充阅读。

步骤 1:用自动解析器获取商品页面

先从最不脆弱的方式开始。导入 CrawlingAPI 类,用你的令牌初始化它,并将 scraper 选项设置为 amazon-product-details 来请求商品 URL。这告诉 Crawlbase 在服务端解析页面并以 JSON 形式返回结构化商品字段,而非原始 HTML。

javascript
const { CrawlingAPI } = require('crawlbase');

const api = new CrawlingAPI({ token: 'YOUR_CRAWLBASE_TOKEN' });

const productUrl = 'https://www.amazon.com/dp/B099MPWPRY';

api
  .get(productUrl, { scraper: 'amazon-product-details' })
  .then((response) => {
    if (response.statusCode === 200) {
      const data = JSON.parse(response.body);
      console.log(data.body);
    }
  })
  .catch((error) => console.error('Request error:', error));

node scraper.js 运行。由于设置了 scraper 选项,响应体是 JSON 而非 HTML,因此用 JSON.parse 解析,并在 data.body 下读取解析后的商品。Crawlbase Amazon 解析器以命名字段的形式返回商品的名称、价格、货币、评分、库存信息、图片 URL 等,因此你完全不需要编写和维护选择器。在解析之前先检查状态码,可以让失败清晰可见而非悄无声息。

Crawlbase Amazon Scraper

那个单独的 scraper: 'amazon-product-details' 选项就是自动解析器在为你工作。Crawling API 在轮换住宅 IP 后面渲染 Amazon 页面,并以现成的 JSON 返回标题、价格、评分、库存和图片,这样你无需自己运行无头浏览器集群、代理池,以及每次 Amazon 改版就要更新的大量 CSS 选择器。先用免费套餐在公开商品页面上测试。

步骤 2:提取你关心的字段

自动解析器返回一个丰富的对象,但大多数任务只需要少数几个字段。将解析后的响应映射为标题、价格、评分、评论数量、库存状态和图片,当字段缺失时回退到 null,这样一个缺失值永远不会导致程序崩溃。

javascript
function extractProduct(parsed) {
  return {
    title: parsed.name || null,
    price: parsed.price || null,
    rating: parsed.customerReview || null,
    reviewCount: parsed.customerReviewCount || null,
    availability: parsed.inStock ? 'In Stock' : 'Unavailable',
    mainImage: parsed.mainImage || null,
    images: parsed.images || [],
  };
}

这里的字段名称(namepricecustomerReviewmainImage 等)是 Amazon 解析器在 data.body 内返回的键。availability 行由解析器的库存标志推导而来,而不是从页面直接复制,因此无论 Amazon 如何措辞,你都能得到一致的字符串。将这个映射保存在一个小函数中:以后想要新字段时,只需在这里添加一行,而无需修改获取逻辑。

步骤 3:用 cheerio 解析原始 HTML(备用方案)

有时你需要原始页面而不是解析后的对象,要么是为了获取自动解析器未返回的字段,要么是想了解每个值具体在哪里。去掉 scraper 选项,Crawling API 将返回渲染后的 HTML,你用 cheerio 加载并通过 CSS 选择器读取。以下是 Amazon 标准商品页面上使用的选择器。

javascript
const cheerio = require('cheerio');

function parseHtml(html) {
  const $ = cheerio.load(html);

  const images = [];
  $('#altImages img').each((_, el) => {
    const src = $(el).attr('src');
    if (src) images.push(src);
  });

  return {
    title: $('#productTitle').text().trim() || null,
    price: $('.a-price .a-offscreen').first().text().trim() || null,
    rating: $('#acrPopover').attr('title') || null,
    reviewCount: $('#acrCustomerReviewText').text().trim() || null,
    availability: $('#availability').text().trim() || null,
    mainImage: $('#landingImage').attr('src') || null,
    images,
  };
}

每个字段都映射到页面上的真实元素。标题在 #productTitle 中;可见价格是第一个 .a-price 块内的无障碍文本,Amazon 将其保留为干净的货币格式字符串;评分存在于 #acrPopovertitle 属性中("4.3 out of 5 stars");评分数量是 #acrCustomerReviewText 的文本;库存状态在 #availability 中;图库缩略图是 #altImages 下的 img 标签,主图在 #landingImage。防御性地读取每个字段(带 null 回退),可以防止缺失元素破坏整个解析过程。

选择器会漂移

Amazon 的元素 ID 和类名(#productTitle.a-price#acrPopover 等)会在不同布局、地区和商品类别之间变化。将上面的选择器视为起始模板,而非不变的规范。当某个字段返回 null 时,在浏览器开发工具中重新检查实时页面并更新选择器。这正是自动解析器为你节省的维护工作,也是它作为默认路径的原因。

步骤 4:整合代码并导出 JSON

现在将获取、提取和 JSON 导出整合进一个可运行的脚本。这个版本使用自动解析器作为主路径,并将最终记录写入文件,方便你输入数据库、比较引擎或价格追踪器。

javascript
const fs = require('fs');
const { CrawlingAPI } = require('crawlbase');

const api = new CrawlingAPI({ token: 'YOUR_CRAWLBASE_TOKEN' });

function extractProduct(parsed) {
  return {
    title: parsed.name || null,
    price: parsed.price || null,
    rating: parsed.customerReview || null,
    reviewCount: parsed.customerReviewCount || null,
    availability: parsed.inStock ? 'In Stock' : 'Unavailable',
    mainImage: parsed.mainImage || null,
    images: parsed.images || [],
  };
}

async function main() {
  const productUrl = 'https://www.amazon.com/dp/B099MPWPRY';
  const response = await api.get(productUrl, { scraper: 'amazon-product-details' });

  if (response.statusCode !== 200) {
    console.error(`Request failed: ${response.statusCode}`);
    return;
  }

  const parsed = JSON.parse(response.body).body;
  const product = extractProduct(parsed);

  fs.writeFileSync('product.json', JSON.stringify(product, null, 2));
  console.log('Saved product.json');
  console.log(product);
}

main().catch((error) => console.error('Error:', error));

node scraper.js 运行完整脚本。它获取页面,用 extractProduct 映射解析后的字段,写入整洁的 product.json,并将记录打印到控制台。要改用 cheerio 备用方案,从 api.get 调用中去掉 scraper 选项,并将 response.body 直接传给步骤 3 中的 parseHtml 函数。

输出结果示例

导出的 product.json 是单条干净记录,可以存储、与上次运行的结果比较,或加载到追踪器中。

json
{
  "title": "PHILIPS A4216 Wireless Sports Headphones",
  "price": "$24.99",
  "rating": "4.3 out of 5 stars",
  "reviewCount": "2,184 ratings",
  "availability": "In Stock",
  "mainImage": "https://m.media-amazon.com/images/I/61abc123.jpg",
  "images": [
    "https://m.media-amazon.com/images/I/41def456.jpg",
    "https://m.media-amazon.com/images/I/51ghi789.jpg"
  ]
}

扩展至多个商品

单个商品只是演示;真实任务需要处理一个列表。由于每个标准商品页面共享相同的解析器和相同的选择器,你收集一组商品 URL(或 Amazon ASIN,直接映射到 /dp/<ASIN> URL),并循环遍历它们,对每个商品复用完全相同的 extractProduct 逻辑。

javascript
async function scrapeMany(asins) {
  const records = [];
  for (const asin of asins) {
    const url = `https://www.amazon.com/dp/${asin}`;
    const response = await api.get(url, { scraper: 'amazon-product-details' });
    if (response.statusCode === 200) {
      const parsed = JSON.parse(response.body).body;
      records.push(extractProduct(parsed));
    }
  }
  return records;
}

scrapeMany(['B099MPWPRY', 'B08PZHYWJS']).then((rows) => {
  console.log(`Collected ${rows.length} products`);
});

要首先发现这些 URL,可以抓取 Amazon 搜索或品类页面获取商品链接并输入到这个循环中。搜索页面步骤是另一个话题,在我们的用 Crawling API 抓取 Amazon 搜索页面指南中有所介绍。如果你宁愿完全跳过选择器和定价逻辑,我们关于用 AI 从 Amazon 抓取价格的教程展示了针对相同数据的模型驱动方法。

保持不被封锁

Crawling API 为你处理渲染和 IP 轮换,但一些习惯可以保持大规模运行健康,这些习惯适用于任何难以爬取的商业目标。

  • 控制请求节奏。在紧密循环中频繁轰炸 Amazon 是被限流的最快方式。在商品之间分散请求并添加短暂延迟,而不是全速爬取。
  • 善用轮换。住宅 IP 池将请求分散到许多真实用户地址上,使单个地址不会触发速率限制。Crawling API 为你处理这些;如果你构建自己的方案,Smart AI Proxy 提供相同的轮换作为即插即用的端点。
  • 关注状态码。当运行开始返回检测或非 200 响应时,这意味着当前速率或 IP 级别已不够用。将其视为降速的信号,而非可忽略的噪声。

更广泛的策略请参阅如何在不被封锁的情况下抓取网站。Amazon 也是更广泛的电商网络爬取工作中的常见目标,相同的获取后提取模式可以跨其他电商平台使用。

抓取 Amazon 合法吗?

是否允许抓取 Amazon 取决于 Amazon 的使用条件、你所在的司法管辖区以及数据的用途。Amazon 的条款限制自动化访问,因此无论你的工具多么谨慎,爬取行为都可能与这些条款相悖。此处的任何代码都不会改变这一点;它只是让技术层面的工作得以实现。阅读 Amazon 的使用条件及其 robots.txt,并将两者视为你收集数据的边界。

有几条原则值得坚守。只收集公开商品数据:任何人无需账号即可看到的标题、价格、评分、评论数量、库存状态和图片。遵守 Amazon 声明的速率预期,将请求量控制在不给其服务器造成压力的范围内。不要抓取任何需要登录的内容,也不要收集超出页面公开评论文本之外的评论者个人数据。受版权保护的媒体(包括商品图片)归其所有者所有:可以引用,但不要将其作为自己的内容重新分发。如果你计划将数据用于商业用途,请获取许可或数据协议,而非假设沉默即为默许。

对于获授权的大规模访问,Amazon 提供了 Product Advertising API(适用于联盟营销)和 Selling Partner API(适用于卖家)等官方渠道,当你需要有保证的结构、数量或商业权利时,这才是正确的工具。本指南有意限定在公开商品页面范围内,因为这是使工作合理可辩护的边界。本指南不涉及任何需要登录的内容、买家或卖家账号数据,或任何绕过身份验证的尝试。如果你的项目需要的数据超出公开商品数据的范围,Amazon 官方 API 或许可协议才是正确路径,而非更聪明的爬虫。

回顾

核心要点

  • 普通请求会被封锁。Amazon 在客户端渲染部分页面并对自动化流量进行防御,因此你需要同时具备渲染能力和受信任的 IP,而 Crawling API 在一次调用中提供了这两者。
  • 自动解析器是最不脆弱的路径。传入 scraper: 'amazon-product-details' 将以结构化 JSON 返回标题、价格、评分、库存和图片,无需维护任何选择器。
  • cheerio 是备用方案。去掉 scraper 选项以获取原始 HTML,然后当自动解析器未返回某个字段时,通过 #productTitle.a-price#acrPopover#availability 和图片元素自己读取。
  • 通过循环 URL 或 ASIN 实现扩展。相同的 extractProduct 函数可对列表运行,搜索页面爬虫为其提供 URL 来源。
  • 坚守公开数据范围。遵守 Amazon 的使用条件和 robots.txt,对于批量或商业用途请使用官方 Product Advertising 或 Selling Partner API,切勿触碰登录信息或评论者个人数据。

常见问题

为什么直接请求 Amazon 会失败?

Amazon 用 JavaScript 渲染部分商品列表,并对自动化流量采取激进的防御措施,因此裸 HTTP 客户端经常得到不完整的页面、CAPTCHA 或封锁,而不是商品数据。要可靠地获取完整页面,需要在受信任的轮换 IP 后面进行渲染,这正是 Crawling API 为你处理的工作。

我应该使用自动解析器还是 cheerio?

默认使用自动解析器(scraper: 'amazon-product-details'):它返回结构化字段,当 Amazon 更改布局时无需维护选择器。只有当你需要解析器未返回的特定字段,或想了解某个值在原始 HTML 中的具体位置时,才使用 cheerio 备用方案。

我可以提取哪些商品字段?

从标准的公开商品页面,你可以获取标题、当前价格、平均评分、评论数量、库存状态,以及主图和图库图片 URL。自动解析器还返回货币、卖家名称和父 ASIN 等额外信息。你可以将所有这些内容以结构化 JSON 或 CSV 格式收集,用于价格追踪、比较引擎或竞争研究。

我的选择器一直返回 null,是什么原因?

这通常意味着 Amazon 的标记发生了变化。其元素 ID 和类名(#productTitle.a-price#acrPopover 等)在不同布局、地区和品类之间有所不同,因此上个月还有效的选择器可能会失效。在浏览器开发工具中重新检查实时页面并更新选择器,或切换到自动解析器,它会为你吸收这些变化。

抓取 Amazon 时如何避免被封锁?

控制每个 IP 的请求频率,在商品之间添加延迟,并通过轮换住宅 IP 路由,使单个地址不会触发速率限制。Crawling API 为你管理轮换和受信任的 IP 池;如果你构建自己的方案,这是需要投入的部分。关注状态码,当开始出现检测时主动降速。

我可以抓取评论者或卖家的个人数据吗?

不可以,本指南也不涉及这些内容。坚守页面上显示的公开商品字段。账号数据、任何需要登录的内容,以及超出公开评论文本之外的评论者或卖家个人详情,都不在范围之内,并且违反 Amazon 的条款。对于获授权的访问,正确的途径是 Amazon 官方 API 或数据协议。

开始构建

大规模爬取任何站点,无需与基础设施对抗。

Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。

自助开通 · 无需销售通话 · 提供企业级爬取量