我的 Amazon 评论爬虫曾经连续稳定运行了整整六周——直到某天早上,它返回了 200 OK,但页面却空空如也。没有报错,没有 CAPTCHA,只有一片空白的 HTML,原本成百上千条评论的位置什么都没有了。
如果你也遇到过这种情况,那你并不孤单。到了 2025 年底,Amazon 开始把完整评论页挡在登录墙后面,导致大量 Python 爬虫脚本一夜之间失效。过去几个月里,我一直在 一边开发我们的 AI 爬虫,一边维护自己的 Python 评论采集流程,所以我觉得现在正是把当初我最希望拥有的这份指南写出来的时候。这篇文章会讲清楚一套真正可用的方法:基于 Cookie 的身份验证、能扛住 Amazon CSS 混淆的稳定选择器、突破 10 页分页限制的技巧、反爬应对策略,以及一个额外的情感分析部分,把原始评论文本变成真正有价值的商业洞察。如果你读到一半开始想:“我真的不想维护这么多代码”,我也会告诉你如何用 在大约两分钟内、几乎不写 Python 就完成同样的工作。
什么是 Amazon 评论抓取?为什么它重要?
Amazon 评论抓取,是指通过程序自动提取 Amazon 商品页面上的用户评论数据——包括星级评分、评论正文、作者名称、发布日期、是否为已验证购买等信息。由于 Amazon 早在 2010 年就从其 Product Advertising API 中移除了评论内容(而且再也没有恢复), 是获取这类数据的唯一程序化途径。
数据也证明了这一点。,而且 。在商品页只展示 5 条评论,就可能让 。系统分析评论情绪的公司,客户留存率可 。这不是什么抽象的数据科学概念,而是竞争情报、产品改进信号和营销文案素材,全都赤裸裸地躺在 Amazon 的服务器上。
为什么要用 Python 抓取 Amazon 评论
Python 依然是这类工作的首选语言。它是 中最受欢迎的语言之一,而 requests、BeautifulSoup、pandas、Scrapy 这些生态工具,也让即使不是全职开发者的人也能上手网页抓取。
不同团队会以不同方式使用这些数据:
| 团队 | 使用场景 | 提取内容 |
|---|---|---|
| 产品 / 研发 | 识别高频抱怨,优先处理问题 | 1–2 星评论文本、关键词频次 |
| 销售 | 监控竞品口碑变化 | 评分、评论量趋势 |
| 市场营销 | 提取用户原话,用于广告文案 | 正向评论短语、功能提及 |
| 电商运营 | 跟踪自家产品口碑随时间变化 | 星级分布、已验证购买比例 |
| 市场调研 | 对比不同功能维度下的品类头部产品 | 多 ASIN 评论数据集 |
某厨房用品品牌曾 ,于是重新调整配方,60 天内就重新夺回了 Best Seller 第一名。某健身追踪器公司则从 ,定位出乳胶过敏问题,推出低敏版本后,退货率下降了 40%。这类回报率,足以让工程投入变得非常值得。
登录墙:为什么你的 Amazon 评论爬虫突然失效了
2024 年 11 月 14 日起,。这一变化随后在 和 中得到了确认。如果你在无痕窗口访问 /product-reviews/{ASIN}/,看到的将是登录页,而不是评论数据。

症状很隐蔽:脚本收到了 200 OK,但 HTML 正文里出现的是登录表单(如 name="email"、id="ap_password"),而不是评论内容。没有错误码,没有 CAPTCHA,只有……什么都没有。
Amazon 这样做,主要是为了反机器人和地区合规。这个限制的执行并不完全一致——有时一个新的浏览器窗口在墙生效前还能看到几条评论,尤其是第一页——但对于任何规模化爬虫来说,都应该默认登录墙始终存在。
不同国家站点(.de、.co.uk、.co.jp)各自独立执行这道限制。正如论坛里有人说的:“每个国家都需要单独登录。”你的 .com Cookie 不会在 .co.uk 上生效。
精选评论 vs. 完整评论:不登录还能拿到什么
Amazon 商品页(/dp/{ASIN}/)在未认证状态下,仍然会展示大约 。这些评论是 Amazon 算法挑选出来的,适合快速做个口碑判断,但它们不能排序、不能筛选,也不能分页。
而完整评论页(/product-reviews/{ASIN}/)——可以按最新排序、按星级过滤,并跨数百条评论分页浏览——则需要登录。
如果你只需要少量评论做快速判断,抓商品页就够了;如果你要几百甚至上千条,就必须处理身份验证。
开始之前你需要什么:Python 环境与库
在写代码之前,先把环境准备好:
- 难度: 中级(会 Python,懂一点 HTML 即可)
- 所需时间: 完整流程约 45 分钟;基础抓取约 10 分钟
- 你需要: Python 3.8+、Chrome 浏览器、一个有效的 Amazon 账号
安装核心库:
1pip install requests beautifulsoup4 lxml pandas textblob
可选安装(用于更高级的情感分析):
1pip install transformers torch
ASIN 是什么? 它是 Amazon 的 10 位商品标识。你可以在任何商品 URL 中找到它——例如在 amazon.com/dp/B0BCNKKZ91 里,ASIN 就是 B0BCNKKZ91。这就是你后面要放进评论 URL 的关键字段。
第一步:用基于 Cookie 的身份验证绕过登录墙
最稳妥的方法,是先在浏览器里登录 Amazon,复制会话 Cookie,再把它们注入到 Python 的 requests.Session() 中。这样可以避免 Selenium 自动登录经常触发的 CAPTCHA 和短信二次验证。
你需要这 7 个 Cookie:
| Cookie 名称 | 用途 |
|---|---|
session-id | 会话标识,可能会轮换 |
session-id-time | 会话时间戳 |
session-token | 会话令牌,可能会轮换 |
ubid-main | 用户浏览标识 |
at-main | 主要认证令牌 |
sess-at-main | 会话级认证信息 |
x-main | 与邮箱关联的用户标识 |
如何从 Chrome DevTools 中导出 Cookie
- 在 Chrome 中登录 amazon.com
- 打开 DevTools(F12,或右键 → 检查)
- 进入 Application → Storage → Cookies →
https://www.amazon.com - 在上表中找到每个 Cookie 名称并复制其值
- 按分号分隔格式整理成 Python 可用字符串
这样设置你的 session:
1import requests
2session = requests.Session()
3# 把你的 Cookie 值贴在这里
4cookies = {
5 "session-id": "YOUR_SESSION_ID",
6 "session-id-time": "YOUR_SESSION_ID_TIME",
7 "session-token": "YOUR_SESSION_TOKEN",
8 "ubid-main": "YOUR_UBID_MAIN",
9 "at-main": "YOUR_AT_MAIN",
10 "sess-at-main": "YOUR_SESS_AT_MAIN",
11 "x-main": "YOUR_X_MAIN",
12}
13headers = {
14 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36",
15 "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
16 "Accept-Language": "en-US,en;q=0.5",
17}
18session.cookies.update(cookies)
19session.headers.update(headers)
重要: 后续所有请求都复用同一个 session 对象。这样能保持 Cookie 一致,更像真实浏览器会话。Cookie 通常在高频抓取下可以维持几天到几周,但如果你又开始被重定向到登录页,就需要重新从浏览器刷新一次 Cookie。
对于非 .com 的站点,Cookie 名称会略有不同——amazon.de 使用的是 at-acbde 而不是 at-main,amazon.co.uk 用的是 at-acbuk,等等。每个站点都需要独立 session。
第二步:构造请求,并用 BeautifulSoup 解析评论 HTML
Amazon 评论页的 URL 模式如下:
1https://www.amazon.com/product-reviews/{ASIN}/ref=cm_cr_arp_d_viewopt_srt?sortBy=recent&pageNumber=1
核心函数如下:
1from bs4 import BeautifulSoup
2import time, random
3def get_soup(session, url):
4 time.sleep(random.uniform(2, 5)) # 礼貌性延迟
5 response = session.get(url, timeout=15)
6 # 检测登录墙
7 if "ap_email" in response.text or "Amazon Sign-In" in response.text:
8 raise Exception("检测到登录墙——请刷新你的 Cookie")
9 if response.status_code != 200:
10 raise Exception(f"HTTP {response.status_code}")
11 return BeautifulSoup(response.text, "lxml")
有个小技巧很有用:在进入评论页之前,先访问一次商品页。这会让你的 session 看起来更像正常浏览。
1# 先访问商品页(模拟真实浏览)
2product_url = f"https://www.amazon.com/dp/{asin}"
3session.get(product_url, timeout=15)
4time.sleep(random.uniform(1, 3))
5# 再访问评论页
6reviews_url = f"https://www.amazon.com/product-reviews/{asin}/ref=cm_cr_arp_d_viewopt_srt?sortBy=recent&pageNumber=1"
7soup = get_soup(session, reviews_url)
第三步:使用稳定选择器提取评论数据(别再依赖 CSS class 了)
这就是大多数 2022–2023 年教程失效的地方。Amazon 会混淆 CSS class 名称——它们会定期变化,正如某位苦恼的开发者在论坛里说的:“没有一个 span 标签的 class 名称是有规律的。”
解决办法是:Amazon 在评论元素上使用了 data-hook 属性,而且这些属性出奇地稳定。它们是语义化标识,Amazon 自家前端代码也依赖它们,所以不会轻易随机化。
| 评论字段 | 稳定选择器(data-hook) | 脆弱选择器(class) |
|---|---|---|
| 评论正文 | [data-hook="review-body"] | .review-text-content(会变) |
| 星级评分 | [data-hook="review-star-rating"] | .a-icon-alt(有歧义) |
| 评论标题 | [data-hook="review-title"] | .review-title(有时可用) |
| 作者名 | span.a-profile-name | 相对稳定 |
| 评论日期 | [data-hook="review-date"] | .review-date(依地区而定) |
| 已验证购买 | [data-hook="avp-badge"] | span.a-size-mini |
使用 data-hook 选择器的提取代码如下:
1import re
2def extract_reviews(soup):
3 reviews = []
4 review_divs = soup.select('[data-hook="review"]')
5 for div in review_divs:
6 # 星级评分
7 rating_el = div.select_one('[data-hook="review-star-rating"]')
8 rating = None
9 if rating_el:
10 rating_text = rating_el.get_text(strip=True)
11 match = re.search(r'(\d\.?\d?)', rating_text)
12 if match:
13 rating = float(match.group(1))
14 # 标题
15 title_el = div.select_one('[data-hook="review-title"]')
16 title = title_el.get_text(strip=True) if title_el else ""
17 # 正文
18 body_el = div.select_one('[data-hook="review-body"]')
19 body = body_el.get_text(strip=True) if body_el else ""
20 # 作者
21 author_el = div.select_one('span.a-profile-name')
22 author = author_el.get_text(strip=True) if author_el else ""
23 # 日期和国家/地区
24 date_el = div.select_one('[data-hook="review-date"]')
25 date_text = date_el.get_text(strip=True) if date_el else ""
26 # 格式示例:"Reviewed in the United States on January 15, 2025"
27 country_match = re.search(r'Reviewed in (.+?) on', date_text)
28 date_match = re.search(r'on (.+)$', date_text)
29 country = country_match.group(1) if country_match else ""
30 date = date_match.group(1) if date_match else ""
31 # 已验证购买
32 verified_el = div.select_one('[data-hook="avp-badge"]')
33 verified = bool(verified_el)
34 reviews.append({
35 "author": author,
36 "rating": rating,
37 "title": title,
38 "content": body,
39 "date": date,
40 "country": country,
41 "verified": verified,
42 })
43 return reviews
我已经用这套选择器在多个 ASIN 上连续跑了好几个月,data-hook 属性一次都没变过。反过来,CSS class 在同一时期至少轮换了两次。
第四步:处理分页,以及 Amazon 的 10 页上限
Amazon 会把 pageNumber 参数限制在每组筛选条件下最多 10 页,每页 10 条评论——也就是每个筛选组合硬性封顶大约 100 条评论。“下一页”按钮在第 10 页之后就会消失。
基础分页循环如下:
1all_reviews = []
2for page in range(1, 11):
3 url = f"https://www.amazon.com/product-reviews/{asin}/ref=cm_cr_arp_d_viewopt_srt?sortBy=recent&pageNumber={page}"
4 soup = get_soup(session, url)
5 page_reviews = extract_reviews(soup)
6 if not page_reviews:
7 break # 这一页已经没有更多评论了
8 all_reviews.extend(page_reviews)
9 print(f"Page {page}: {len(page_reviews)} reviews")
如何抓取超过 10 页的 Amazon 评论
解决办法是“分桶筛选”。每一种 filterByStar 和 sortBy 的组合,都会得到一个独立的 10 页窗口。
星级筛选值: one_star、two_star、three_star、four_star、five_star
排序值: recent、helpful(默认)
把 5 个星级筛选 × 2 个排序方式组合起来,你最多可以访问 100 页,也就是每个商品最多约 1,000 条评论;对于星级分布不均的商品,往往能接近完整评论集。
1star_filters = ["one_star", "two_star", "three_star", "four_star", "five_star"]
2sort_orders = ["recent", "helpful"]
3all_reviews = []
4seen_titles = set() # 简单去重
5for star in star_filters:
6 for sort in sort_orders:
7 for page in range(1, 11):
8 url = (
9 f"https://www.amazon.com/product-reviews/{asin}"
10 f"?filterByStar={star}&sortBy={sort}&pageNumber={page}"
11 )
12 soup = get_soup(session, url)
13 page_reviews = extract_reviews(soup)
14 if not page_reviews:
15 break
16 for review in page_reviews:
17 # 用标题 + 作者组合去重
18 key = (review["title"], review["author"])
19 if key not in seen_titles:
20 seen_titles.add(key)
21 all_reviews.append(review)
22 print(f"[{star}/{sort}] Page {page}: {len(page_reviews)} reviews")
23print(f"Total unique reviews: {len(all_reviews)}")
不同分桶之间会有重叠,所以去重非常重要。我通常用“评论标题 + 作者名”的组合当作去重键——虽然不完美,但能过滤掉绝大多数重复项。
第五步:躲开反爬防线(轮换、限速、重试)
Amazon 使用的是 AWS WAF Bot Control,而且现在的检测强度明显更高了。单层对抗手段(只换 User-Agent,或只加延迟)已经不够用了。
| 技巧 | 实现方式 |
|---|---|
| 轮换 User-Agent | 从 10+ 种真实浏览器字符串中随机选择 |
| 指数退避 | 遇到 503 时按 2s → 4s → 8s 递增重试 |
| 请求限速 | 每页之间等待 random.uniform(2, 5) 秒 |
| 代理轮换 | 循环使用住宅代理 |
| 会话指纹一致性 | 同一 session 保持相同 Cookie + Headers |
| TLS 伪装 | 生产环境中用 curl_cffi 代替原生 requests |
一个适合生产环境的重试包装器:
1import time, random
2USER_AGENTS = [
3 "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36",
4 "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36",
5 "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0",
6 "Mozilla/5.0 (Macintosh; Intel Mac OS X 15_7_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Safari/605.1.15",
7 "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36",
8]
9def scrape_with_retries(session, url, max_retries=3):
10 for attempt in range(max_retries):
11 try:
12 session.headers["User-Agent"] = random.choice(USER_AGENTS)
13 time.sleep(random.uniform(2, 5))
14 response = session.get(url, timeout=15)
15 # 检测封锁
16 if "validateCaptcha" in response.url or "Robot Check" in response.text:
17 wait = (2 ** attempt) * 5
18 print(f"检测到 CAPTCHA。等待 {wait}s...")
19 time.sleep(wait)
20 continue
21 if response.status_code in (429, 503):
22 wait = (2 ** attempt) * 2
23 print(f"请求被限流({response.status_code})。等待 {wait}s...")
24 time.sleep(wait)
25 continue
26 if "ap_email" in response.text:
27 raise Exception("登录墙——Cookie 已过期")
28 return BeautifulSoup(response.text, "lxml")
29 except Exception as e:
30 if attempt == max_retries - 1:
31 raise
32 print(f"第 {attempt + 1} 次尝试失败:{e}")
33 return None
关于代理:Amazon 会在网络层封禁已知的数据中心 IP 段(AWS、GCP、Azure、DigitalOcean)。如果你抓取的页面超过几百页,住宅代理几乎是必须的——根据规模不同,成本大约是每月 50–200 美元甚至更高。对于小项目(每天少于 100 次请求),从家用 IP 谨慎限速通常也够用。
Amazon 还会检查 TLS 指纹。Python 原生 requests 库有一个 。如果是生产级爬虫,可以考虑 curl_cffi,它可以伪装成真实浏览器的 TLS 栈。对于教程级别的抓取(几百页以内),带好请求头的 requests 往往就能完成任务。
第六步:把抓到的 Amazon 评论导出为 CSV 或 Excel
评论抓完之后,用 pandas 处理成可用格式非常简单:
1import pandas as pd
2df = pd.DataFrame(all_reviews)
3df.to_csv("amazon_reviews.csv", index=False)
4print(f"Exported {len(df)} reviews to amazon_reviews.csv")
示例输出:
| author | rating | title | content | date | country | verified |
|---|---|---|---|---|---|---|
| Sarah M. | 5.0 | Best purchase this year | Battery lasts all day, screen is gorgeous... | January 15, 2025 | the United States | True |
| Mike T. | 2.0 | Disappointed after 2 weeks | The charging port stopped working... | February 3, 2025 | the United States | True |
| Priya K. | 4.0 | Great value for the price | Does everything I need, minor lag on heavy apps... | March 10, 2025 | the United States | False |
导出到 Excel:df.to_excel("amazon_reviews.xlsx", index=False)(需要 openpyxl)。
如果要导出到 Google Sheets,gspread 库可以用,但配置步骤有 ——创建项目、启用两个 API、生成服务账号凭据、共享表格。要是你觉得这比真正抓取还麻烦,那你没感觉错。(这也是为什么像 这样能一键导出到 Google Sheets 的工具,会突然变得特别有吸引力。)
额外收获:用 5 行 Python 给评论加上情感分析
大多数抓取教程都会在 CSV 导出这里停下。但真正把原始数据变成业务决策的,是情感评分。
最快的基线方案是 TextBlob:
1from textblob import TextBlob
2df["sentiment"] = df["content"].apply(lambda x: TextBlob(str(x)).sentiment.polarity)
这样每条评论都会得到一个从 -1.0(非常负面)到 +1.0(非常正面)的极性分数。示例输出:
| content (截断) | rating | sentiment |
|---|---|---|
| "Battery lasts all day, screen is gorgeous..." | 5.0 | 0.65 |
| "The charging port stopped working after..." | 2.0 | -0.40 |
| "Does everything I need, minor lag on..." | 4.0 | 0.25 |
| "Absolute garbage. Returned immediately." | 1.0 | -0.75 |
| "It's okay. Nothing special but works." | 3.0 | 0.10 |
最值得关注的是那些“不一致”的行——比如 3 星但文本却偏正向,或者 5 星但语言明显负面。这些差异往往能揭示出仅靠星级看不出来的细腻用户意见。

如果追求生产级准确率,更推荐 Hugging Face Transformers。,而且与词典类工具相比,。nlptown/bert-base-multilingual-uncased-sentiment 这个模型甚至可以直接预测 1–5 星:
1from transformers import pipeline
2clf = pipeline("sentiment-analysis",
3 model="nlptown/bert-base-multilingual-uncased-sentiment")
4df["predicted_stars"] = df["content"].apply(
5 lambda x: int(clf(str(x)[:512])[0]["label"][0])
6)
Amazon 评论通常呈现 ——5 星处有一个大峰值,1 星处有一个较小峰值,中间则形成低谷。这意味着平均星级往往并不能真实代表产品质量。最好把 1 星评论单独拿出来,提取其中反复出现的主题——通常真正可修复的问题,就藏在这里。
诚实的取舍:自建 Python、付费 API,还是 Thunderbit
我自己维护过 Amazon 的 Python 爬虫,所以我可以坦白地说:它们真的会坏。选择器会变,Cookie 会过期,Amazon 会上线新的反爬层,然后你的周六上午就变成了修爬虫,而不是分析数据。论坛里的用户也有同样的感受——那些“上个月还能跑”的 DIY 脚本,现在需要不停打补丁。
下面是三种主流方案的对比:
| 对比项 | 自建 Python(BS4/Selenium) | 付费爬取 API | Thunderbit(无代码) |
|---|---|---|---|
| 上手时间 | 1–3 小时 | 30 分钟(API Key) | 2 分钟 |
| 成本 | 免费(+ 代理成本) | 每月 50–200 美元以上 | 有免费额度 |
| 登录墙处理 | 手动管理 Cookie | 通常已处理 | 自动处理 |
| 维护成本 | 高(选择器容易失效) | 低(由服务商维护) | 几乎为零(AI 自适应) |
| 分页支持 | 需要自写代码 | 内置 | 内置 |
| 多国家支持 | 每个域名独立 session | 通常支持 | 基于浏览器 = 使用你的本地环境 |
| 情感分析 | 需要自己写代码 | 有时包含 | 导出到表格后可在任意地方分析 |
| 最适合 | 学习、完全控制 | 规模化生产管道 | 快速取数、非开发团队 |
Python 让你拥有完整控制权,而且确实是理解网页抓取底层机制的最佳方式。但如果你的需求是“周五前把竞品评论数据放进表格里”,而不是“我要搭一个生产级数据管道”,那么自定义爬虫的维护成本可能并不划算。
可以通过点击完成身份验证、选择器、分页和导出——试试 ,看看它是否适合你的工作流。随着 Amazon 不断加强反爬措施,能够实时自适应的 AI 工具,未来会从“加分项”变成“必需品”。
你也可以查看我们的 ,那里有抓取流程的视频演示。
常见问题
1. 不登录可以抓取 Amazon 评论吗?
可以,但只能拿到商品详情页(/dp/{ASIN}/)上显示的约 8 条“精选评论”。截至 2024 年底,带排序、筛选和分页的完整评论页都需要认证。对大多数业务场景来说,你还是要处理登录墙。
2. 抓取 Amazon 评论合法吗?
Amazon 的服务条款禁止自动化抓取。不过,近期美国判例(Meta v. Bright Data,2024;hiQ v. LinkedIn)支持抓取公开可访问数据。由于你已经同意了 Amazon 的条款,登录后抓取的法律风险会更高。商业使用请咨询律师。
3. 每个商品最多能抓多少条 Amazon 评论?
Amazon 对每个排序和星级筛选组合限制最多 10 页。利用 5 个星级筛选 × 2 个排序方式,单个商品最多可访问 100 页(大约 1,000 条评论)。如果再加关键词筛选,理论上上限更高,但重复数据也会更多。
4. 抓取 Amazon 评论最好的 Python 库是什么?
requests + BeautifulSoup 是最常见也最稳定的静态 HTML 解析组合。如果需要 JavaScript 渲染,Selenium 会更有用。想要一个不写代码、还能自动处理登录墙和分页的替代方案,可以试试 。
5. 抓取 Amazon 时,怎么避免被封?
从 10+ 种真实浏览器的 User-Agent 中轮换,给每次请求加入 2–5 秒的随机延迟,对 503/429 错误做指数退避,规模化时使用住宅代理(数据中心 IP 往往会被预先封禁),并保持请求之间的 Cookie 一致性。如果你想完全省心,Thunderbit 会通过你的浏览器会话自动处理反爬防御。
了解更多
