我是如何用 Python 抓取 Amazon 评论的(绕过登录墙)

最后更新于 April 16, 2026
我是如何用 Python 抓取 Amazon 评论的(绕过登录墙)

我的 Amazon 评论爬虫曾经连续稳定运行了整整六周——直到某天早上,它返回了 200 OK,但页面却空空如也。没有报错,没有 CAPTCHA,只有一片空白的 HTML,原本成百上千条评论的位置什么都没有了。

如果你也遇到过这种情况,那你并不孤单。到了 2025 年底,Amazon 开始把完整评论页挡在登录墙后面,导致大量 Python 爬虫脚本一夜之间失效。过去几个月里,我一直在 Thunderbit 一边开发我们的 AI 爬虫,一边维护自己的 Python 评论采集流程,所以我觉得现在正是把当初我最希望拥有的这份指南写出来的时候。这篇文章会讲清楚一套真正可用的方法:基于 Cookie 的身份验证、能扛住 Amazon CSS 混淆的稳定选择器、突破 10 页分页限制的技巧、反爬应对策略,以及一个额外的情感分析部分,把原始评论文本变成真正有价值的商业洞察。如果你读到一半开始想:“我真的不想维护这么多代码”,我也会告诉你如何用 Thunderbit 在大约两分钟内、几乎不写 Python 就完成同样的工作。

什么是 Amazon 评论抓取?为什么它重要?

Amazon 评论抓取,是指通过程序自动提取 Amazon 商品页面上的用户评论数据——包括星级评分、评论正文、作者名称、发布日期、是否为已验证购买等信息。由于 Amazon 早在 2010 年就从其 Product Advertising API 中移除了评论内容(而且再也没有恢复),Amazon review scraper 是获取这类数据的唯一程序化途径。

数据也证明了这一点。95% 的消费者会在购买前查看评论,而且 94% 的人把 Amazon 视为获取产品评论的第一来源。在商品页只展示 5 条评论,就可能让 转化率提升 270%。系统分析评论情绪的公司,客户留存率可 提升最高 15%。这不是什么抽象的数据科学概念,而是竞争情报、产品改进信号和营销文案素材,全都赤裸裸地躺在 Amazon 的服务器上。

为什么要用 Python 抓取 Amazon 评论

Python 依然是这类工作的首选语言。它是 2024 年 Stack Overflow 开发者调查 中最受欢迎的语言之一,而 requests、BeautifulSoup、pandas、Scrapy 这些生态工具,也让即使不是全职开发者的人也能上手网页抓取。

不同团队会以不同方式使用这些数据:

团队使用场景提取内容
产品 / 研发识别高频抱怨,优先处理问题1–2 星评论文本、关键词频次
销售监控竞品口碑变化评分、评论量趋势
市场营销提取用户原话,用于广告文案正向评论短语、功能提及
电商运营跟踪自家产品口碑随时间变化星级分布、已验证购买比例
市场调研对比不同功能维度下的品类头部产品多 ASIN 评论数据集

某厨房用品品牌曾 挖掘负面评论,发现 22% 的差评提到“涂层脱落”,于是重新调整配方,60 天内就重新夺回了 Best Seller 第一名。某健身追踪器公司则从 评论文本中发现“表带过敏”,定位出乳胶过敏问题,推出低敏版本后,退货率下降了 40%。这类回报率,足以让工程投入变得非常值得。

登录墙:为什么你的 Amazon 评论爬虫突然失效了

2024 年 11 月 14 日起,Amazon 开始要求登录后才能查看完整的商品评论页。这一变化随后在 社区论坛爬虫服务商博客 中得到了确认。如果你在无痕窗口访问 /product-reviews/{ASIN}/,看到的将是登录页,而不是评论数据。

python-web-scraping-diagram.webp

症状很隐蔽:脚本收到了 200 OK,但 HTML 正文里出现的是登录表单(如 name="email"id="ap_password"),而不是评论内容。没有错误码,没有 CAPTCHA,只有……什么都没有。

Amazon 这样做,主要是为了反机器人和地区合规。这个限制的执行并不完全一致——有时一个新的浏览器窗口在墙生效前还能看到几条评论,尤其是第一页——但对于任何规模化爬虫来说,都应该默认登录墙始终存在。

不同国家站点(.de.co.uk.co.jp)各自独立执行这道限制。正如论坛里有人说的:“每个国家都需要单独登录。”你的 .com Cookie 不会在 .co.uk 上生效。

精选评论 vs. 完整评论:不登录还能拿到什么

Amazon 商品页(/dp/{ASIN}/)在未认证状态下,仍然会展示大约 8 条“精选评论”。这些评论是 Amazon 算法挑选出来的,适合快速做个口碑判断,但它们不能排序、不能筛选,也不能分页。

而完整评论页(/product-reviews/{ASIN}/)——可以按最新排序、按星级过滤,并跨数百条评论分页浏览——则需要登录。

如果你只需要少量评论做快速判断,抓商品页就够了;如果你要几百甚至上千条,就必须处理身份验证。

开始之前你需要什么:Python 环境与库

在写代码之前,先把环境准备好:

  • 难度: 中级(会 Python,懂一点 HTML 即可)
  • 所需时间: 完整流程约 45 分钟;基础抓取约 10 分钟
  • 你需要: Python 3.8+、Chrome 浏览器、一个有效的 Amazon 账号

安装核心库:

pip install requests beautifulsoup4 lxml pandas textblob

可选安装(用于更高级的情感分析):

pip 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

  1. 在 Chrome 中登录 amazon.com
  2. 打开 DevTools(F12,或右键 → 检查)
  3. 进入 ApplicationStorageCookieshttps://www.amazon.com
  4. 在上表中找到每个 Cookie 名称并复制其值
  5. 按分号分隔格式整理成 Python 可用字符串

这样设置你的 session:

import requests

session = requests.Session()

# 把你的 Cookie 值贴在这里
cookies = {
    "session-id": "YOUR_SESSION_ID",
    "session-id-time": "YOUR_SESSION_ID_TIME",
    "session-token": "YOUR_SESSION_TOKEN",
    "ubid-main": "YOUR_UBID_MAIN",
    "at-main": "YOUR_AT_MAIN",
    "sess-at-main": "YOUR_SESS_AT_MAIN",
    "x-main": "YOUR_X_MAIN",
}

headers = {
    "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",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Language": "en-US,en;q=0.5",
}

session.cookies.update(cookies)
session.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 模式如下:

https://www.amazon.com/product-reviews/{ASIN}/ref=cm_cr_arp_d_viewopt_srt?sortBy=recent&pageNumber=1

核心函数如下:

from bs4 import BeautifulSoup
import time, random

def get_soup(session, url):
    time.sleep(random.uniform(2, 5))  # 礼貌性延迟
    response = session.get(url, timeout=15)

    # 检测登录墙
    if "ap_email" in response.text or "Amazon Sign-In" in response.text:
        raise Exception("检测到登录墙——请刷新你的 Cookie")

    if response.status_code != 200:
        raise Exception(f"HTTP {response.status_code}")

    return BeautifulSoup(response.text, "lxml")

有个小技巧很有用:在进入评论页之前,先访问一次商品页。这会让你的 session 看起来更像正常浏览。

# 先访问商品页(模拟真实浏览)
product_url = f"https://www.amazon.com/dp/{asin}"
session.get(product_url, timeout=15)
time.sleep(random.uniform(1, 3))

# 再访问评论页
reviews_url = f"https://www.amazon.com/product-reviews/{asin}/ref=cm_cr_arp_d_viewopt_srt?sortBy=recent&pageNumber=1"
soup = 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 选择器的提取代码如下:

import re

def extract_reviews(soup):
    reviews = []
    review_divs = soup.select('[data-hook="review"]')

    for div in review_divs:
        # 星级评分
        rating_el = div.select_one('[data-hook="review-star-rating"]')
        rating = None
        if rating_el:
            rating_text = rating_el.get_text(strip=True)
            match = re.search(r'(\d\.?\d?)', rating_text)
            if match:
                rating = float(match.group(1))

        # 标题
        title_el = div.select_one('[data-hook="review-title"]')
        title = title_el.get_text(strip=True) if title_el else ""

        # 正文
        body_el = div.select_one('[data-hook="review-body"]')
        body = body_el.get_text(strip=True) if body_el else ""

        # 作者
        author_el = div.select_one('span.a-profile-name')
        author = author_el.get_text(strip=True) if author_el else ""

        # 日期和国家/地区
        date_el = div.select_one('[data-hook="review-date"]')
        date_text = date_el.get_text(strip=True) if date_el else ""
        # 格式示例:"Reviewed in the United States on January 15, 2025"
        country_match = re.search(r'Reviewed in (.+?) on', date_text)
        date_match = re.search(r'on (.+)$', date_text)
        country = country_match.group(1) if country_match else ""
        date = date_match.group(1) if date_match else ""

        # 已验证购买
        verified_el = div.select_one('[data-hook="avp-badge"]')
        verified = bool(verified_el)

        reviews.append({
            "author": author,
            "rating": rating,
            "title": title,
            "content": body,
            "date": date,
            "country": country,
            "verified": verified,
        })

    return reviews

我已经用这套选择器在多个 ASIN 上连续跑了好几个月,data-hook 属性一次都没变过。反过来,CSS class 在同一时期至少轮换了两次。

第四步:处理分页,以及 Amazon 的 10 页上限

Amazon 会把 pageNumber 参数限制在每组筛选条件下最多 10 页,每页 10 条评论——也就是每个筛选组合硬性封顶大约 100 条评论。“下一页”按钮在第 10 页之后就会消失。

基础分页循环如下:

all_reviews = []

for page in range(1, 11):
    url = f"https://www.amazon.com/product-reviews/{asin}/ref=cm_cr_arp_d_viewopt_srt?sortBy=recent&pageNumber={page}"
    soup = get_soup(session, url)
    page_reviews = extract_reviews(soup)

    if not page_reviews:
        break  # 这一页已经没有更多评论了

    all_reviews.extend(page_reviews)
    print(f"Page {page}: {len(page_reviews)} reviews")

如何抓取超过 10 页的 Amazon 评论

解决办法是“分桶筛选”。每一种 filterByStarsortBy 的组合,都会得到一个独立的 10 页窗口。

星级筛选值: one_startwo_starthree_starfour_starfive_star
排序值: recenthelpful(默认)

把 5 个星级筛选 × 2 个排序方式组合起来,你最多可以访问 100 页,也就是每个商品最多约 1,000 条评论;对于星级分布不均的商品,往往能接近完整评论集。

star_filters = ["one_star", "two_star", "three_star", "four_star", "five_star"]
sort_orders = ["recent", "helpful"]
all_reviews = []
seen_titles = set()  # 简单去重

for star in star_filters:
    for sort in sort_orders:
        for page in range(1, 11):
            url = (
                f"https://www.amazon.com/product-reviews/{asin}"
                f"?filterByStar={star}&sortBy={sort}&pageNumber={page}"
            )
            soup = get_soup(session, url)
            page_reviews = extract_reviews(soup)

            if not page_reviews:
                break

            for review in page_reviews:
                # 用标题 + 作者组合去重
                key = (review["title"], review["author"])
                if key not in seen_titles:
                    seen_titles.add(key)
                    all_reviews.append(review)

            print(f"[{star}/{sort}] Page {page}: {len(page_reviews)} reviews")

print(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

一个适合生产环境的重试包装器:

import time, random

USER_AGENTS = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36",
    "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",
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0",
    "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",
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36",
]

def scrape_with_retries(session, url, max_retries=3):
    for attempt in range(max_retries):
        try:
            session.headers["User-Agent"] = random.choice(USER_AGENTS)
            time.sleep(random.uniform(2, 5))

            response = session.get(url, timeout=15)

            # 检测封锁
            if "validateCaptcha" in response.url or "Robot Check" in response.text:
                wait = (2 ** attempt) * 5
                print(f"检测到 CAPTCHA。等待 {wait}s...")
                time.sleep(wait)
                continue

            if response.status_code in (429, 503):
                wait = (2 ** attempt) * 2
                print(f"请求被限流({response.status_code})。等待 {wait}s...")
                time.sleep(wait)
                continue

            if "ap_email" in response.text:
                raise Exception("登录墙——Cookie 已过期")

            return BeautifulSoup(response.text, "lxml")

        except Exception as e:
            if attempt == max_retries - 1:
                raise
            print(f"第 {attempt + 1} 次尝试失败:{e}")

    return None

关于代理:Amazon 会在网络层封禁已知的数据中心 IP 段(AWS、GCP、Azure、DigitalOcean)browseract.com。如果你抓取的页面超过几百页,住宅代理几乎是必须的——根据规模不同,成本大约是每月 50–200 美元甚至更高。对于小项目(每天少于 100 次请求),从家用 IP 谨慎限速通常也够用。

Amazon 还会检查 TLS 指纹。Python 原生 requests 库有一个 广为人知的 JA3 hash,很多 WAF 会直接预先拦截。如果是生产级爬虫,可以考虑 curl_cffi,它可以伪装成真实浏览器的 TLS 栈。对于教程级别的抓取(几百页以内),带好请求头的 requests 往往就能完成任务。

第六步:把抓到的 Amazon 评论导出为 CSV 或 Excel

评论抓完之后,用 pandas 处理成可用格式非常简单:

import pandas as pd

df = pd.DataFrame(all_reviews)
df.to_csv("amazon_reviews.csv", index=False)
print(f"Exported {len(df)} reviews to amazon_reviews.csv")

示例输出:

authorratingtitlecontentdatecountryverified
Sarah M.5.0Best purchase this yearBattery lasts all day, screen is gorgeous...January 15, 2025the United StatesTrue
Mike T.2.0Disappointed after 2 weeksThe charging port stopped working...February 3, 2025the United StatesTrue
Priya K.4.0Great value for the priceDoes everything I need, minor lag on heavy apps...March 10, 2025the United StatesFalse

导出到 Excel:df.to_excel("amazon_reviews.xlsx", index=False)(需要 openpyxl)。

如果要导出到 Google Sheets,gspread 库可以用,但配置步骤有 8 步以上的 Google Cloud 设置——创建项目、启用两个 API、生成服务账号凭据、共享表格。要是你觉得这比真正抓取还麻烦,那你没感觉错。(这也是为什么像 Thunderbit 这样能一键导出到 Google Sheets 的工具,会突然变得特别有吸引力。)

额外收获:用 5 行 Python 给评论加上情感分析

大多数抓取教程都会在 CSV 导出这里停下。但真正把原始数据变成业务决策的,是情感评分。

最快的基线方案是 TextBlob:

from textblob import TextBlob

df["sentiment"] = df["content"].apply(lambda x: TextBlob(str(x)).sentiment.polarity)

这样每条评论都会得到一个从 -1.0(非常负面)到 +1.0(非常正面)的极性分数。示例输出:

content (截断)ratingsentiment
"Battery lasts all day, screen is gorgeous..."5.00.65
"The charging port stopped working after..."2.0-0.40
"Does everything I need, minor lag on..."4.00.25
"Absolute garbage. Returned immediately."1.0-0.75
"It's okay. Nothing special but works."3.00.10

最值得关注的是那些“不一致”的行——比如 3 星但文本却偏正向,或者 5 星但语言明显负面。这些差异往往能揭示出仅靠星级看不出来的细腻用户意见。

ai-review-analysis.webp

如果追求生产级准确率,更推荐 Hugging Face Transformers。VADER 是在推文上训练的,不是产品评论,而且与词典类工具相比,Transformer 模型在评论分类上可达到 98% 以上准确率nlptown/bert-base-multilingual-uncased-sentiment 这个模型甚至可以直接预测 1–5 星:

from transformers import pipeline

clf = pipeline("sentiment-analysis",
               model="nlptown/bert-base-multilingual-uncased-sentiment")
df["predicted_stars"] = df["content"].apply(
    lambda x: int(clf(str(x)[:512])[0]["label"][0])
)

Amazon 评论通常呈现 J 形分布——5 星处有一个大峰值,1 星处有一个较小峰值,中间则形成低谷。这意味着平均星级往往并不能真实代表产品质量。最好把 1 星评论单独拿出来,提取其中反复出现的主题——通常真正可修复的问题,就藏在这里。

诚实的取舍:自建 Python、付费 API,还是 Thunderbit

我自己维护过 Amazon 的 Python 爬虫,所以我可以坦白地说:它们真的会坏。选择器会变,Cookie 会过期,Amazon 会上线新的反爬层,然后你的周六上午就变成了修爬虫,而不是分析数据。论坛里的用户也有同样的感受——那些“上个月还能跑”的 DIY 脚本,现在需要不停打补丁。

下面是三种主流方案的对比:

对比项自建 Python(BS4/Selenium)付费爬取 APIThunderbit(无代码)
上手时间1–3 小时30 分钟(API Key)2 分钟
成本免费(+ 代理成本)每月 50–200 美元以上有免费额度
登录墙处理手动管理 Cookie通常已处理自动处理
维护成本高(选择器容易失效)低(由服务商维护)几乎为零(AI 自适应)
分页支持需要自写代码内置内置
多国家支持每个域名独立 session通常支持基于浏览器 = 使用你的本地环境
情感分析需要自己写代码有时包含导出到表格后可在任意地方分析
最适合学习、完全控制规模化生产管道快速取数、非开发团队

Python 让你拥有完整控制权,而且确实是理解网页抓取底层机制的最佳方式。但如果你的需求是“周五前把竞品评论数据放进表格里”,而不是“我要搭一个生产级数据管道”,那么自定义爬虫的维护成本可能并不划算。

Thunderbit 可以通过点击完成身份验证、选择器、分页和导出——试试 免费套餐,看看它是否适合你的工作流。随着 Amazon 不断加强反爬措施,能够实时自适应的 AI 工具,未来会从“加分项”变成“必需品”。

你也可以查看我们的 Thunderbit YouTube 频道,那里有抓取流程的视频演示。

常见问题

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 会更有用。想要一个不写代码、还能自动处理登录墙和分页的替代方案,可以试试 Thunderbit

5. 抓取 Amazon 时,怎么避免被封?

从 10+ 种真实浏览器的 User-Agent 中轮换,给每次请求加入 2–5 秒的随机延迟,对 503/429 错误做指数退避,规模化时使用住宅代理(数据中心 IP 往往会被预先封禁),并保持请求之间的 Cookie 一致性。如果你想完全省心,Thunderbit 会通过你的浏览器会话自动处理反爬防御。

了解更多

Fawad Khan
Fawad Khan
Fawad 靠写作谋生,而且说实话,他挺喜欢这份工作。他花了很多年琢磨,什么样的文案能真正打动人,什么样的内容又会让读者直接划过去。你要是问他营销,他能聊上几个小时;你要是问他卡邦尼意面,他能聊得更久。
目录

只要开口,就能抓取网页

用简单英文说出你的需求。或者更简单,什么都不用说。

试用 Thunderbit 免费
使用 AI 提取数据
轻松将数据传输到 Google Sheets、Airtable 或 Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week