Amazon 的畅销榜页面是一份关于整个目录中实际在卖什么的实时、公开排名。每一个品类,从电子产品到电脑及配件,都有自己一份排好序的热销产品列表,由 Amazon 每小时刷新,每件商品的排名、标题、价格和评分就摆在页面里。那份排名是开放网络上最干净的需求信号之一,这正是产品研究者、卖家和分析师为发现趋势、做竞争分析和定价决策而追踪它的原因。

本指南向你展示如何用 Python 抓取一份 Amazon 畅销榜列表。你将构建一个可运行的小抓取器,它通过 Crawling API 获取一个品类的畅销榜页面,为每件排名产品解析出一条干净的记录,并把结果导出为 JSON 和 CSV。整个演练始终限定在公开的排名数据:任何人无需登录就能在一个畅销榜页面上看到的名称、价格、评分和链接。

你将构建什么

一个 Python 脚本,它接受一个 Amazon 畅销榜品类的 URL,通过 Crawling API 取回渲染后的页面,并为每件排名产品提取一条结构化记录。我们用电脑及配件畅销榜页面作为贯穿示例,与旧版演练所用的是同一个品类,并从每张排名卡片拉取以下字段:

  • 排名该产品在列表中的位置,例如 1 表示榜首热销品。
  • 标题排名卡片上显示的产品名称。
  • 价格当产品显示价格时,所列出的价格。
  • 评分平均星级评分,例如"4.7 out of 5 stars"。
  • 链接通往该产品自己详情页的 URL。

为什么普通请求在 Amazon 上会失败

如果你把一个裸 HTTP 客户端指向一个 Amazon 畅销榜 URL,你很少能拿到你想要的那份排名列表。有两件事在跟你作对。第一,那张排名网格的大部分是在客户端渲染的:Amazon 投送一个轻量外壳,并随着页面的 JavaScript 运行和你滚动而填入卡片,所以初始 HTML 往往缺少排名靠后的条目。第二,Amazon 很快就会标记自动化流量。数据中心 IP 段以及看起来不像真实浏览器的请求模式,会在你够到列表之前就被一个 CAPTCHA、一个"机器人检查"插页,或一个彻底的封锁所迎击。

所以一个可行的畅销榜抓取器在一次请求里需要两样东西:一个能渲染页面的浏览器,以及一个 Amazon 读起来像真实购物者的 IP。你可以用一个无头浏览器和一个轮换住宅代理池自己组装这套,但保持那个技术栈健康就是大部分工作。Crawling API 把两者折叠进一次调用:你把品类 URL 发给它,它在一个受信任的住宅 IP 背后渲染页面,处理轮换和 CAPTCHA 求解,并返回完成的 HTML 供你解析。

前置条件

在写任何代码之前你需要准备好几样东西。没有一样耗时。

基础的 Python。你应当能自如地编写并运行一个 Python 脚本,并用 pip 安装包。如果你对这门语言还是新手,官方 Python 文档或任何入门课程都覆盖了本教程所假定的水平。

Python 3.8 或更高版本。python --version(或 python3 --version)确认你的版本。如果还没有,从 python.org 安装,并确保 Python 在你的系统 PATH 上。

一个 Crawlbase 账户和 token。注册一个免费账户,打开你的仪表盘,从账户文档页复制你的 token。免费档包含 1,000 次请求且无需信用卡,足够用来构建并测试这个抓取器。把 token 当作密码对待,别把它放进版本控制。

搭建项目

创建一个虚拟环境,让项目依赖保持隔离,然后安装抓取器需要的两个库。crawlbase 是 Crawling API 的官方客户端,而 beautifulsoup4 解析返回的 HTML,让你能按 CSS 选择器从排名卡片中拉出每个字段。

bash
python --version

python -m venv amazon_env
source amazon_env/bin/activate

pip install crawlbase beautifulsoup4

在 Windows 上,用 amazon_env\Scripts\activate 来激活环境,而不是那条 source 行。两个库都装好后,创建本指南余下部分逐步搭建的脚本文件:

bash
touch amazon_best_sellers.py

理解畅销榜页面

每个 Amazon 畅销榜品类都位于一个稳定的 /zgbs/ URL。举例来说,电脑及配件列表是 https://www.amazon.com/Best-Sellers-Computers-Accessories/zgbs/pc,而电子产品是 https://www.amazon.com/Best-Sellers-Electronics/zgbs/electronics。页面铺开一张排好序的排名卡片网格,每件产品一张,每张都带着同样那几个字段:一个排名徽章、一个标题、一个价格、一个星级评分,以及一个通往该产品详情页的链接。

在写选择器之前,在你的浏览器里打开一个畅销榜页面,右键点击一张排名卡片,并选择"检查"。Amazon 把每件排名条目包在一个标有 id="gridItemRoot" 属性的容器里,在卡片顶部附近的一个编号徽章里显示排名,并在那个容器内部暴露标题、价格和评分。那些就是你要瞄准的元素。Amazon 的工具类名经常变,所以在可能的地方,依靠像 id="gridItemRoot" 这样更耐久的结构属性,而非一条脆弱的类链。

第 1 步:获取渲染后的畅销榜页面

先把完成的页面拿到手。导入 CrawlingAPI 类,用你的 token 初始化它,设置品类 URL,并请求它。在解析之前检查状态码,能让失败响亮而非无声。

python
from crawlbase import CrawlingAPI

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"})

def crawl(page_url):
    options = {"ajax_wait": "true", "page_wait": 4000}
    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__":
    bestsellers_url = "https://www.amazon.com/Best-Sellers-Computers-Accessories/zgbs/pc"
    html = crawl(bestsellers_url)
    print(html[:500] if html else "No HTML returned")

对于一个随页面加载而填入的列表,那两个等待选项很重要。ajax_wait 告诉 API 等待异步内容完成,而 page_wait 在加载后再保持固定的毫秒数,使迟到渲染的卡片在页面被捕获之前出现。正文以 latin1 解码,因为 Amazon 页面里混入了一些字符,严格的 UTF-8 解码可能会被卡住。运行脚本,你应当看到真实的排名标记,而不是一个机器人检查外壳。这在你写下任何一个选择器之前确认了渲染是有效的。

Crawlbase Amazon Scraper

畅销榜列表需要在一个受信任的 IP 背后的一个渲染后页面,一次调用搞定。Crawling API 接过你的 token,在一个真实浏览器里运行品类页面,在服务器端轮换住宅 IP,并处理 CAPTCHA 求解,然后把完成的 HTML 交给你。你省去自己运行一支无头浏览器机队和一个代理池。先在免费的 1,000 次请求档位上,把它指向一个 /zgbs/ 品类。

第 2 步:用 BeautifulSoup 解析排名卡片

手里有了渲染后的 HTML,就把它载入 BeautifulSoup,找到每一张排名卡片,并按选择器拉出每个字段。Amazon 把每件排名条目包在一个你据以选择的容器里,在一个编号徽章里显示排名,并在卡片内部暴露标题、价格和评分。从卡片的锚点读取产品链接。用一个 try/except 包住每张卡片,使一个畸形的列表项不会让整次运行崩溃。

python
from bs4 import BeautifulSoup

BASE = "https://www.amazon.com"

def text_of(card, selector):
    el = card.select_one(selector)
    return el.get_text(strip=True) if el else None

def parse_link(card):
    a = card.select_one("a.a-link-normal[href]")
    if not a:
        return None
    href = a["href"]
    return href if href.startswith("http") else BASE + href

def scrape_best_sellers(html):
    soup = BeautifulSoup(html, "html.parser")
    cards = soup.select("div#gridItemRoot")
    results = []
    for card in cards:
        try:
            rank = text_of(card, "span.zg-bdg-text")
            results.append({
                "rank": rank.lstrip("#") if rank else None,
                "title": text_of(card, "div._cDEzb_p13n-sc-css-line-clamp-3_g3dy1"),
                "price": text_of(card, "span._cDEzb_p13n-sc-price_3mJ9Z"),
                "rating": text_of(card, "span.a-icon-alt"),
                "link": parse_link(card),
            })
        except Exception as e:
            print(f"Skipped a card: {e}")
    return results

text_of 辅助函数查询一张卡片内部的一个元素,并在它缺失时返回 None,而不是在对空对象调用 .get_text() 时抛错。这让提取在某个字段缺失时保持健壮,而这很常见,因为并非每件排名条目都显示价格。排名来自那个编号徽章(span.zg-bdg-text),并剥掉前导的 #,使你存储一个干净的数字。评分字符串存在那个隐藏的 span.a-icon-alt 里,它保存着 Amazon 在星形图形背后渲染的"4.7 out of 5 stars"文本,而链接被规范化为一个绝对 URL,因为 Amazon 经常提供一个相对的 href

选择器会漂移

Amazon 的 p13n-sc 工具类名(标题截断、价格 span)是生成的,会不加通知地改变,而像 div#gridItemRootspan.zg-bdg-text 这样的结构标记更耐久。把上面那些基于类的选择器当作一个起步模板,而非一份合约。当某个字段对每张卡片都回来是 None 时,在你浏览器的开发者工具里重新检查实时的畅销榜页面并更新选择器。对任何生产抓取器而言,定期的选择器维护都是常态。

第 3 步:组装脚本并导出 JSON 和 CSV

现在把获取和解析接成一个可运行的脚本,然后把记录写成 JSON 和 CSV,使你能把它们载入一个笔记本或一张电子表格。获取渲染后的品类页面,把它交给解析器,并倾倒出结构化的行。

python
import csv
import json
from crawlbase import CrawlingAPI
from bs4 import BeautifulSoup

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"})
BASE = "https://www.amazon.com"
FIELDS = ["rank", "title", "price", "rating", "link"]

def crawl(page_url):
    options = {"ajax_wait": "true", "page_wait": 4000}
    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

def text_of(card, selector):
    el = card.select_one(selector)
    return el.get_text(strip=True) if el else None

def parse_link(card):
    a = card.select_one("a.a-link-normal[href]")
    if not a:
        return None
    href = a["href"]
    return href if href.startswith("http") else BASE + href

def scrape_best_sellers(html):
    soup = BeautifulSoup(html, "html.parser")
    cards = soup.select("div#gridItemRoot")
    results = []
    for card in cards:
        try:
            rank = text_of(card, "span.zg-bdg-text")
            results.append({
                "rank": rank.lstrip("#") if rank else None,
                "title": text_of(card, "div._cDEzb_p13n-sc-css-line-clamp-3_g3dy1"),
                "price": text_of(card, "span._cDEzb_p13n-sc-price_3mJ9Z"),
                "rating": text_of(card, "span.a-icon-alt"),
                "link": parse_link(card),
            })
        except Exception as e:
            print(f"Skipped a card: {e}")
    return results

def export(rows, name="amazon_best_sellers"):
    with open(f"{name}.json", "w", encoding="utf-8") as f:
        json.dump(rows, f, indent=2, ensure_ascii=False)
    with open(f"{name}.csv", "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=FIELDS)
        writer.writeheader()
        writer.writerows(rows)
    print(f"Saved {len(rows)} products to {name}.json and {name}.csv")

def main():
    url = "https://www.amazon.com/Best-Sellers-Computers-Accessories/zgbs/pc"
    html = crawl(url)
    if not html:
        return
    rows = scrape_best_sellers(html)
    export(rows)

if __name__ == "__main__":
    main()

python amazon_best_sellers.py 运行完整脚本。它获取渲染后的品类页面,为每件排名产品解析出一行,并写出 amazon_best_sellers.jsonamazon_best_sellers.csv 两者。那个共享的 FIELDS 列表让 CSV 的列顺序与字典键保持一致,所以两份导出永不偏离彼此。

输出长什么样

你会得到一份干净的排名记录列表,按列表顺序排列,可随时写入 JSON、CSV 或一个数据库。

json
[
  {
    "rank": "1",
    "title": "Amazon Basics High-Speed HDMI Cable, 6 Feet",
    "price": "$6.99",
    "rating": "4.7 out of 5 stars",
    "link": "https://www.amazon.com/dp/B014I8SSD0"
  },
  {
    "rank": "2",
    "title": "Apple AirTag",
    "price": "$24.99",
    "rating": "4.7 out of 5 stars",
    "link": "https://www.amazon.com/dp/B0D54JZTHY"
  }
]

更快的路径:内置的畅销榜抓取器

编写你自己的选择器给你完全的控制,但它也意味着要随着 Amazon 标记的变动去维护它们。如果你宁可不这样,Crawling API 自带一个内置的 amazon-best-sellers 抓取器,它直接把排名列表作为结构化 JSON 返回,不需要 BeautifulSoup。你传一个 scraper 选项,并读取已解析的 body

python
import json
from crawlbase import CrawlingAPI

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"})
url = "https://www.amazon.com/Best-Sellers-Electronics/zgbs/electronics"

response = api.get(url, {"scraper": "amazon-best-sellers"})

if response.get("status_code") == 200:
    data = json.loads(response["body"].decode("latin1"))
    result = data.get("body", {})
    with open("amazon_best_sellers.json", "w", encoding="utf-8") as f:
        json.dump(result, f, indent=4, ensure_ascii=False)
    print("Scraper response saved to amazon_best_sellers.json")
else:
    print(f"Request failed: {response.get('status_code', 0)}")

已解析的响应包含一个 pageTitle、一个 products 数组(每项带 titlepricecustomerReviewcustomerReviewCountasinimageurlposition)、同级 categories 列表,以及一个 pagination 块。它是拿到一个被维护的排名数据源的最快方式;当你需要内置抓取器不返回的字段,或想了解页面结构时,上面那个手写的解析器才是正确的选择。自动解析网络数据中记录的 Scraper API 驱动着同一套自动解析。

跨品类和跨页面扩展

一个品类是一个演示;一份真实的研究作业会跨越许多品类。Amazon 几乎为每一个部门和子品类都暴露一份畅销榜列表,每份都在它自己的 /zgbs/ URL 上,而每份列表都分页(通常前 50 名分摊在两页上,带一个 ?pg=2 参数)。走遍一组品类 URL,并控制请求节奏,使你不是在一个紧凑循环里猛敲 Amazon。

python
import time

CATEGORIES = {
    "computers": "https://www.amazon.com/Best-Sellers-Computers-Accessories/zgbs/pc",
    "electronics": "https://www.amazon.com/Best-Sellers-Electronics/zgbs/electronics",
}

def scrape_categories(categories):
    everything = {}
    for name, url in categories.items():
        rows = []
        for page in (1, 2):
            page_url = url if page == 1 else f"{url}?pg={page}"
            html = crawl(page_url)
            if not html:
                break
            found = scrape_best_sellers(html)
            if not found:
                break
            rows.extend(found)
            print(f"{name} page {page}: {len(found)} products")
            time.sleep(2)
        everything[name] = rows
    return everything

空结果的 break 在一个品类用完页面时让你提早停下,而请求之间的 time.sleep(2) 控制运行节奏,使你不会因连发的流量而被标记。按品类名给输出做键,让每份列表保持独立,这正是你在跨部门比较排名时所要的。要追踪随时间的趋势,按一个排程运行这份作业,给每次导出盖上日期戳,然后对相继的快照做差异比对,看看什么在列表里上升或下降。

守住不被封锁

即便渲染已被处理好,Amazon 仍然盯防形如抓取器的流量。几个习惯能让一次运行保持健康,它们适用于任何难缠的商业目标。

  • 控制你的请求节奏。把请求间隔开,在页面与品类之间加一个延迟,而不是全速爬取一切。把更重的作业排在非高峰时段,以减轻 Amazon 服务器的负载。
  • 依靠轮换。一个住宅 IP 池把请求分散到许多真实用户地址上,使任何单个地址都不触发限速。Crawling API 替你处理这一点;如果你自己搭技术栈,这就是要做对的部分。
  • 只保留你需要的。存储你项目用到的排名字段,丢弃其余。定期重新核对你的选择器,使抓取器跟得上标记的变化。

关于避免封锁的更宽泛打法,参见如何在不被封锁的情况下抓取网站,而要了解更多关于渲染为何在此重要,参见如何爬取 JavaScript 网站。如果你想深入 BeautifulSoup 这一侧,关于在 Python 中使用 BeautifulSoup的指南详细覆盖了这个解析库。

抓取 Amazon 合法吗?

抓取 Amazon 是否被允许,取决于 Amazon 的使用条件、你所在的司法辖区,以及你用这些数据做什么。Amazon 的条款限制自动化访问,所以无论你的工具多么谨慎,抓取都可能与那些条款相抵触。这里的代码没有一行改变这一点;它只是让技术部分能跑通。读 Amazon 的使用条件及其 robots.txt,并把两者都当作你所采集之物的边界。对于商业或竞争性用途,法律图景会更复杂,就你的具体案例咨询一位法律专家是明智之举。

有几条值得守住的底线。只采集公开数据:任何人无需账户就能在一个畅销榜页面上看到的排名、产品标题、价格、评分和列表链接。把你的请求量保持得足够低,使你不会给 Amazon 的服务器造成压力,并避免个人数据,包括任何超出公开列出范围、与可识别的购物者、评论者或卖家相关联的东西。如果你打算把数据用于商业再利用,去取得许可或一份官方协议,而不是假定沉默就是同意。

本指南被特意限定在公开的畅销榜排名页面,因为那是让这项工作站得住脚的那条线。它不涉及任何登录之后的内容、账户或订单数据、个人信息,或任何绕过认证或一个你无权通过的 CAPTCHA 的尝试。对于受许可或批量的访问,Amazon 提供 Product Advertising API 及其他官方项目,当你需要大体量、有保障的结构或商业权利时,那才是合适的工具。如果你的项目需要的不只是公开排名数据,一个官方 API 或一份数据协议才是正确的路径,而不是一个更聪明的抓取器。

回顾

核心要点

  • 畅销榜是一个实时的需求信号。每个品类的 /zgbs/ 页面为当前热销产品排名,这正是它对产品研究和趋势追踪如此有用的原因。
  • 你需要渲染与一个受信任的 IP 一并到位。Amazon 在客户端填入排名网格并封锁机器人流量,所以 Crawling API 在一个住宅 IP 背后一次调用渲染页面。
  • BeautifulSoup 做提取。遍历 div#gridItemRoot 卡片,并把排名、标题、价格、评分和链接映射到当前的选择器,并预期那些选择器会漂移。
  • 导出为 JSON 和 CSV。一个共享的字段列表让两份文件保持同步;当你想直接拿到结构化 JSON 时,内置的 amazon-best-sellers 抓取器是维护成本更低的替代。
  • 守在公开数据上。尊重 Amazon 的使用条件和 robots.txt,对于受许可或批量的数据优先用官方的 Product Advertising API,并绝不触碰账户、订单或个人信息。

常见问题

为什么普通请求从 Amazon 返回不了畅销榜?

两个原因。Amazon 在页面加载和滚动时于客户端填入排名网格的大部分,所以一次原始请求往往得到一个缺少排名靠后条目的外壳。在此之上,Amazon 会挑战或封锁看起来不像真实浏览器的流量。通过 Crawling API 在一个受信任的 IP 背后渲染页面,两者都得以解决,这正是这里的抓取器经它路由请求的原因。

我如何抓取一个特定品类的畅销榜?

每份畅销榜列表都有它自己稳定的 /zgbs/ URL,例如电脑及配件是 /Best-Sellers-Computers-Accessories/zgbs/pc,电子产品是 /Best-Sellers-Electronics/zgbs/electronics。把抓取器指向你想要的品类 URL。要覆盖许多品类,保存一个名称到 URL 的映射并循环遍历它,用一个短延迟控制请求节奏。

我能抓取任何产品品类的畅销榜吗?

你能抓取大多数品类,因为 Amazon 几乎为每个部门和许多子品类都发布畅销榜,每份都有它自己的列表。页面布局在各品类间是一致的,所以同样的选择器通常能沿用。当你向许多品类铺开时,保持合理的量,并尊重 Amazon 的条款和速率预期。

BeautifulSoup 解析器和内置抓取器有什么区别?

BeautifulSoup 解析器给你对提取哪些字段以及如何提取的完全控制,代价是随着 Amazon 标记变化去维护选择器。内置的 amazon-best-sellers 抓取器直接把排名列表作为结构化 JSON 返回,包括 ASIN、image 和 position,没有要维护的选择器。为了速度用内置抓取器,当你需要一个它不返回的字段时用手写的解析器。

我如何随时间追踪畅销榜趋势?

按一个排程运行抓取器,给每次导出盖上日期戳,并存储这些快照。比较相继的运行能显示哪些产品在排名里攀升或下滑,以及价格如何变化。那段时间序列正是把一份一次性列表变成你可用于产品研究、定价和竞争分析的趋势数据的东西。

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

把你的每 IP 请求速率保持得低,在页面与品类之间加一个延迟,并经由轮换住宅 IP 路由,使任何单个地址都不触发限速。Crawling API 替你管理轮换、一个受信任的 IP 池和 CAPTCHA 处理;如果你构建自己的技术栈,那就是要投入的部分。盯住状态码,并在你开始看到挑战时退避。

开始构建

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

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

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