Amazon 搜索结果是零售领域最密集的公开数据集之一。每一次查询都会返回一个排名商品的网格,包含标题、价格、星级评分、评论数量、ASIN 和各商品详情页链接。这些数据驱动着竞争对手价格追踪、品类研究、需求信号分析和品类组合分析。问题在于,Amazon 用 JavaScript 渲染这些商品列表,并对自动化流量采取强力防御,导致普通的 HTTP 请求只能拿到一个近乎空白的外壳,而非你所要的商品数据。
本指南将向你展示如何用 Python 以可靠方式抓取 Amazon 搜索页面。你将构建一个可运行的小型爬虫,通过 Crawling API 获取完整渲染的搜索结果页,将每条结果卡解析为干净的记录,在多个结果页间分页抓取,并将数据导出为 JSON 和 CSV。整个流程仅限于公开搜索和商品列表数据,文末的合法性章节不是样板文字,请在指向任何真实规模的流量之前认真阅读。
你将构建什么
一个 Python 脚本,接受 Amazon 搜索关键词,通过 Crawling API 获取渲染后的结果页,并从每个商品中提取结构化记录。我们以搜索词"games"作为运行示例,从每张结果卡提取以下字段:
- 标题搜索卡上显示的商品名称。
- 价格卡片上显示的标价。
- 评分顾客平均评价,例如"4.5 out of 5 stars"。
- ASINAmazon 为每条商品列表分配的十位商品标识符。
- 链接商品详情页的 URL。
本文仅涉及搜索结果页(SERP)。如果你想在获取链接或 ASIN 后深入单个商品,姊妹指南抓取 Amazon 商品数据和通过 ASIN 抓取将从本文停止的地方继续。
为什么普通请求在 Amazon 上会失败
如果你用裸 HTTP 客户端请求 Amazon 搜索 URL,你会得到状态 200 的响应,但正文中几乎没有商品数据。两个因素对你不利。首先,Amazon 在客户端加载大部分搜索网格:初始 HTML 只是一个外壳,结果、筛选器和价格只有在浏览器中运行完页面 JavaScript 和 Ajax 请求后才会填充。其次,Amazon 对自动化流量的识别非常快。来自数据中心 IP 和不像真实浏览器的请求模式,会在到达渲染后的商品列表之前就遭遇 CAPTCHA 挑战、限流或封锁。
因此,一个可用的 Amazon 爬虫每次请求需要同时具备两样东西:一个真实渲染页面的浏览器,以及一个平台视为真实购物者的 IP。你可以自己组建无头浏览器加上轮换住宅代理池,但将这些整合在一起并保持健康才是主要工作量。Crawling API 将两者折叠进单次调用:你发送带 JavaScript 令牌的 URL,它在受信任的住宅 IP 后面渲染页面,并返回供你解析的完整数据。
Crawlbase 提供两种令牌类型。普通令牌(TCP)获取静态 HTML;JavaScript(JS)令牌先在真实浏览器中渲染页面。Amazon 的搜索网格是 JavaScript 驱动的,因此这里必须使用 JS 令牌。使用普通令牌返回的是与普通 fetch 一样的空白外壳,没有任何有用内容可供解析。
前提条件
编写任何代码之前,你需要准备好几样东西,都不需要太长时间。
Python 基础。你应该能够编写并运行 Python 脚本,以及用 pip 安装包。如果你刚接触这门语言,用 Python 进行网络爬取的指南涵盖了本教程所假定的基础知识。
Python 3.8 或更高版本。用 python --version 确认你的版本。如果没有,请从 python.org 安装,或通过 Anaconda 等发行版安装。
Crawlbase 账号和 JS 令牌。注册免费账号,打开 Dashboard,复制你的 JavaScript(JS)令牌。免费套餐包含 1,000 次请求,无需绑定信用卡,之后只需为成功请求付费。像对待密码一样对待令牌:它对你的请求进行身份验证,不要将其放入版本控制。
配置项目
创建虚拟环境以隔离项目依赖,然后安装爬虫所需的两个库。
python --version python -m venv amazon_env source amazon_env/bin/activate pip install crawlbase pandas
在 Windows 上,用 amazon_env\Scripts\activate 代替 source 命令激活环境。两个依赖各司其职:crawlbase 是 Crawling API 的官方零依赖客户端,pandas 整理抓取的记录并导出为 CSV。由于我们使用 Crawling API 内置的 amazon-serp 解析器,解析后的字段以结构化 JSON 返回,因此本文无需单独的 HTML 解析库(如 BeautifulSoup)。
了解 Amazon 搜索页面
Amazon 通过简单的 URL 模式暴露搜索功能。查询词放在 k 参数中:
https://www.amazon.com/s?k=games
每个搜索页面展示一个商品卡片的网格,每条商品列表对应一张卡片。卡片包含标题、价格、带评论数的星级评分、图片、商品详情页链接以及商品 ASIN。Amazon 还在结果顶部和中间穿插赞助商品列表,解析器会单独标记这些内容,方便你保留或过滤。网格下方是分页控件,让你翻看同一查询的更多结果页。数据都在那里,但动态加载,这正是下一节渲染步骤如此重要的原因。
步骤 1:获取渲染后的搜索页面
先获取完整页面。导入 CrawlingAPI 类,用你的 JS 令牌初始化,根据查询构建搜索 URL,并用允许动态内容加载完成的等待选项请求它。在解析之前检查状态码,可以让失败清晰可见而非悄无声息。
from crawlbase import CrawlingAPI api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) def crawl(page_url): options = {"page_wait": 2000, "ajax_wait": "true"} response = api.get(page_url, options) if response["status_code"] == 200: return response["body"].decode("latin1") print(f"Request failed: {response['status_code']}") return None if __name__ == "__main__": query = "games" search_url = f"https://www.amazon.com/s?k={query}" html = crawl(search_url) print(html[:500] if html else "No HTML returned")
两个等待选项对像这样的客户端渲染目标至关重要。ajax_wait 告诉 API 在捕获页面之前等待异步请求完成,page_wait 在加载后再固定等待若干毫秒,让延迟渲染的网格先出现。两秒是合理的起点;如果卡片返回时有缺失,可以适当提高。正文以 latin1 解码,因为 Amazon 页面混有严格 UTF-8 解码可能出错的字符。运行脚本后,你应该能看到真实的商品标记,而非普通 fetch 返回的空白外壳,这证明渲染在要求 API 解析任何内容之前就已正常工作。
Amazon 需要在受信任 IP 后面完整渲染页面,一次调用搞定。Crawling API 接受 JS 令牌,在真实浏览器中运行页面,在服务端轮换住宅 IP,并将完整输出交给你,让你无需自己运行无头浏览器集群和代理池。先用免费套餐测试。
步骤 2:用 amazon-serp 解析器解析结果
你可以拉取渲染后的 HTML 并自己编写选择器,但 Crawling API 为常见网站(包括 Amazon)内置了解析器。传入 scraper 选项会告知 API 在服务端解析页面,并返回结构化 JSON 而非原始 HTML。专为搜索结果页设计的解析器是 amazon-serp,它返回一个商品数组,每个商品包含名称、价格、顾客评价、ASIN、图片和链接等字段,以及一个分页块。
以下是 amazon-serp 单个商品条目的简略结构,让你了解需要读取哪些键:
{ "name": "Product Name", "price": "$19.99", "rawPrice": 19.99, "currency": "$", "offer": "Offer Details", "customerReview": "4.5 out of 5 stars", "customerReviewCount": "1,234", "asin": "B0XXXXXXXX", "image": "Product Image URL", "url": "Product URL", "isPrime": true, "sponsoredAd": false }
向请求添加 scraper 选项后,响应正文变为 JSON。解析后的有效载荷位于 body 键下,商品在 products 下,旁边是 pagination 块。下面的函数提取每个商品的五个关键字段,将 name 映射为 title,将 customerReview 映射为 rating,并直接读取 asin 和 url。
import json from crawlbase import CrawlingAPI api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) options = { "page_wait": 2000, "ajax_wait": "true", "scraper": "amazon-serp", } def parse_products(scraper_result): products = scraper_result.get("products", []) results = [] for product in products: results.append({ "title": product.get("name", ""), "price": product.get("price", ""), "rating": product.get("customerReview", ""), "reviews": product.get("customerReviewCount", ""), "asin": product.get("asin", ""), "link": product.get("url", ""), "isPrime": product.get("isPrime", False), "sponsored": product.get("sponsoredAd", False), }) return results
每个字段都用 .get() 加默认值读取,因此缺少价格或评分的商品会返回空字符串而不会使程序崩溃,这很常见,因为并非每条结果都有评价。我们同样在记录中保留 isPrime 和 sponsored:sponsored 标志让你在后续分析中过滤付费排名,保留纯有机结果。由于解析器在服务端完成提取工作,你不需要自己追踪 Amazon 的 CSS 类名,而这恰恰是手写解析器最容易出问题的部分。
步骤 3:整合并导出
现在将获取和解析整合进一个可运行的脚本。带 scraper 选项请求搜索页面,加载 JSON 正文,提取商品,然后同时写入 JSON 和 CSV,使数据可供电子表格或下游任务使用。
import json import pandas as pd from crawlbase import CrawlingAPI api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) options = { "page_wait": 2000, "ajax_wait": "true", "scraper": "amazon-serp", } def scrape_search(page_url): response = api.get(page_url, options) if response["status_code"] != 200: print(f"Request failed: {response['status_code']}") return [], None payload = json.loads(response["body"].decode("latin1")) scraper_result = payload["body"] return parse_products(scraper_result), scraper_result.get("pagination") def parse_products(scraper_result): products = scraper_result.get("products", []) results = [] for product in products: results.append({ "title": product.get("name", ""), "price": product.get("price", ""), "rating": product.get("customerReview", ""), "reviews": product.get("customerReviewCount", ""), "asin": product.get("asin", ""), "link": product.get("url", ""), "isPrime": product.get("isPrime", False), "sponsored": product.get("sponsoredAd", False), }) return results def main(): query = "games" search_url = f"https://www.amazon.com/s?k={query}" products, _ = scrape_search(search_url) print(f"Found {len(products)} products") with open("amazon_products.json", "w") as f: json.dump(products, f, indent=2) pd.DataFrame(products).to_csv("amazon_products.csv", index=False) if __name__ == "__main__": main()
scrape_search 辅助函数返回两样东西:解析后的商品列表和分页块,下一节将用后者遍历所有页面。同时写入 JSON 和 CSV 覆盖了两种常见的下游需求:JSON 方便另一个脚本处理,pandas 的 to_csv 调用一行就生成可供电子表格使用的表格。
输出结果示例
用 python scraper.py 运行完整脚本,你将得到一个干净的记录列表,每条结果一个,随时可写入任何地方。
[ { "title": "Catan Board Game (Base Game)", "price": "$43.99", "rating": "4.8 out of 5 stars", "reviews": "32,114", "asin": "B00U26V4VQ", "link": "https://www.amazon.com/dp/B00U26V4VQ", "isPrime": true, "sponsored": false }, { "title": "Ticket to Ride Board Game", "price": "$34.99", "rating": "4.8 out of 5 stars", "reviews": "28,907", "asin": "B0001WN5LE", "link": "https://www.amazon.com/dp/B0001WN5LE", "isPrime": true, "sponsored": false } ]
同样数据的 CSV 版本每个字段一列(title、price、rating、reviews、asin、link、isPrime、sponsored),每个商品一行,适合在电子表格中快速浏览或用于价格比较表。
跨结果页分页处理
单页只是演示;真实任务需要遍历某个查询的所有结果页。amazon-serp 解析器返回一个分页块,包含当前页、下一页和总页数:
"pagination": { "currentPage": 1, "nextPage": 2, "totalPages": 20 }
Amazon 用 &page= 参数分页搜索,与浏览器中一致:?k=games&page=1、?k=games&page=2,以此类推。从第一次响应读取 totalPages,然后从第二页循环到最后一页,沿途收集商品。
import time def scrape_all_pages(query, max_pages=5): base = f"https://www.amazon.com/s?k={query}" all_results, pagination = scrape_search(base) if not pagination: return all_results total_pages = min(pagination.get("totalPages", 1), max_pages) for page in range(2, total_pages + 1): page_url = f"{base}&page={page}" products, _ = scrape_search(page_url) if not products: break all_results.extend(products) print(f"Page {page}: {len(products)} products") time.sleep(2) return all_results
max_pages 上限防止像"games"这样的宽泛查询意外翻遍全部二十页,空结果判断则在查询只有少数几页时提前终止。页面之间的 time.sleep(2) 控制请求节奏,避免在紧密循环中轰炸搜索,那是遭到限流的最快方式。将 main 中的单页调用替换为 scrape_all_pages(query),同样的 JSON 和 CSV 导出逻辑即可处理合并后的数据集。
选择解析器还是自己动手
scraper 选项让代码如此简洁。去掉它,API 返回完整的渲染 HTML,你需要用 BeautifulSoup 等库自己解析,手动定位 Amazon 结果卡片的选择器。这条路给你完全的控制权,但维护负担也落到你身上:Amazon 的 CSS 类名会漂移,手写解析器需要定期更新。amazon-serp 解析器为你承担了这部分工作,这就是它作为默认方案的原因。如果你需要解析器未暴露的自定义字段,BeautifulSoup 指南涵盖了解析技术,而Crawling API 则通过专用端点暴露相同的 Amazon 解析器,如果你偏好那种调用方式。
保持不被封锁
即使渲染已被处理,Amazon 仍会监控爬虫特征的流量。一些习惯可以保持大规模运行健康,适用于任何防御严密的商业目标。
-
控制请求节奏。在页面之间添加延迟分散请求,轮换查询词而非全速爬取单一关键词。分页循环中的
time.sleep是下限,不是上限。 - 善用轮换。住宅 IP 池将请求分散到许多真实用户地址,使单个地址不会触发限流。Crawling API 为你处理这些;如果你自己搭建,这部分是关键投入。
- 关注状态码。当运行开始返回挑战或错误时,说明当前速率或 IP 级别已不够用。这是降速的信号,而非可忽略的噪声。
更广泛的反封锁策略请参阅如何在不被封锁的情况下抓取网站。
抓取 Amazon 合法吗?
是否允许抓取 Amazon 取决于 Amazon 的服务条款、你所在的司法管辖区以及数据的用途。Amazon 的使用条件限制自动化访问和数据提取,因此无论你的工具多么谨慎,爬取行为都可能与这些条款相悖。此处的任何代码都不会改变这一点;它只是让技术层面的工作得以实现。阅读 Amazon 的使用条件及其 robots.txt,并将两者视为你收集数据的边界。
有几条原则值得坚守。只收集公开数据:无需账号即可在搜索结果页上看到的商品标题、价格、评分、评论数量、ASIN 和商品链接。遵守 Amazon 声明的速率预期,将请求量控制在不给其服务器造成压力的范围内。避免个人数据,包括任何与可识别购物者、评论者或第三方卖家绑定的信息,但结果页上公开列出的除外。不要将受版权保护的媒体(如商品图片或评论文本)作为自己的内容重新分发。如果你打算将数据用于商业用途,请获取许可或数据协议,而非假设沉默即为默许。
本指南有意限定在公开搜索和商品列表页面,因为这是保持工作可辩护的边界。不涉及任何需要登录的内容、账号或订单数据、个人信息、支付或结账流程,以及任何绕过身份验证的尝试。对于获授权或批量访问,Amazon 提供了 Product Advertising API 和其他官方渠道,当你需要大规模数据、有保证的结构或商业权利时,这才是正确工具。如果你的项目所需数据超出公开商品列表范围,官方 API 或数据协议才是正确路径,而非更聪明的爬虫。
核心要点
- Amazon 搜索是 JavaScript 渲染的。普通 fetch 只返回空白外壳,因此必须先渲染页面才能读取任何商品。
-
JS 令牌同时处理渲染和受信任 IP。带
page_wait和ajax_wait的单次 Crawling API 调用等待动态网格加载,并返回完整输出。 -
amazon-serp 解析器为你完成解析。传入
scraper选项,每个商品以结构化 JSON 形式返回 name、price、customerReview、asin 和 url,无需自己编写选择器。 -
用 page 参数分页。从分页块读取
totalPages,用上限和延迟遍历&page=,合并结果。 - 坚守公开数据范围。遵守 Amazon 的使用条件和 robots.txt,对于获授权或批量数据优先使用官方 Product Advertising API,切勿触碰账号、订单或个人信息。
常见问题
为什么普通请求从 Amazon 拿不到任何商品?
因为 Amazon 用 JavaScript 和 Ajax 在客户端加载大部分搜索网格。初始 HTML 是一个外壳,只有在浏览器中运行完页面脚本后才会填充商品,因此原始 HTTP 请求返回状态 200 但商品列表为空。要获取真实数据,必须先渲染页面,这正是 Crawling API 的 JS 令牌为你处理的工作。
处理 Amazon 需要普通令牌还是 JS 令牌?
JS 令牌。普通令牌获取静态 HTML,对 Amazon 来说与普通 fetch 一样返回空白外壳。JS 令牌在解析前先在真实浏览器中渲染页面,使 amazon-serp 解析器提取时商品卡片已经存在。
amazon-serp 解析器返回什么内容?
它返回一个 JSON 对象,包含 products 数组和 pagination 块。每个商品携带 name、price、rawPrice、currency、customerReview、customerReviewCount、asin、image、url、isPrime 和 sponsoredAd 等字段。本文将其中几个映射为干净的 title、price、rating、ASIN 和 link 记录。
如何抓取 Amazon 搜索的所有页面?
从第一次响应的分页块读取 totalPages,然后用递增的 &page= 参数从第二页开始循环搜索 URL。用相同函数抓取每一页,设置页数上限防止宽泛查询无限延伸,并在请求之间添加短暂延迟控制节奏,避免被限流。
我可以抓取 Amazon 的订单、账号或结账数据吗?
不可以,本指南也不涉及这些内容。订单历史、账号详情和结账流程都在登录之后,属于非公开数据。抓取登录后内容,或绕过身份验证访问它,超出了本文范围,并违反 Amazon 的条款。对于获授权访问更丰富的数据,正确路径是官方 Product Advertising API 或合作协议。
抓取 Amazon 时如何避免被封锁?
控制每个 IP 的请求频率,在页面之间添加延迟,轮换查询词而非循环单一关键词,并通过轮换住宅 IP 路由,使单个地址不会触发限流。Crawling API 为你管理轮换和受信任的 IP 池;如果你自己搭建,这部分是关键投入。关注状态码,出现挑战时主动降速。
大规模爬取任何站点,无需与基础设施对抗。
Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。
