用 Python 采集亚马逊商品数据摘要

2026 年亚马逊反爬体系已从简单的 IP 封禁升级到 TLS 握手层的指纹识别,标准 requests 库在亚马逊面前基本等同于裸奔。本文系统拆解亚马逊反爬机制的技术层次(TLS 指纹、行为分析、蜜罐陷阱),给出以 curl_cffi 为核心的完整绕过方案,包含带重试逻辑、速率限制、异常分级处理的生产级 Python 代码,并用真实成本数据说明什么时候手写爬虫已经不划算,切换到 Pangolinfo 亚马逊数据采集 API 才是工程理性的选择。

用 Python 采集亚马逊商品数据,20 行代码可以跑起来第一个请求——但 2026 年的亚马逊不会给你第 20 个。标准 requests 库因为 TLS 指纹问题,现在几乎在第一次握手就被识别并拦截。能真正规模化跑通的方案是 curl_cffi:它用 libcurl 模拟 Chrome 的完整 TLS 指纹,让你的 Python 请求在网络层看起来与真实浏览器无法区分。本教程把两件事都讲清楚——能跑的代码,和什么时候切换到托管型亚马逊数据采集 API 才划算。

亚马逊商品页面有哪些数据可以采集?

在写第一行 Python 之前,先把目标搞清楚。一个标准 ASIN 页面(amazon.com/dp/B0XXXXXX)里,公开展示在 HTML 里的数据包括:

  • 商品标题——完整 Listing 名称,含品牌、型号、变体描述,通常 80–200 字符
  • 价格——原价、促销价、订阅折扣价、划线价,有时还有 Coupon 券额,分散在 5–7 个不同 DOM 节点
  • 星级评分与评论数——汇总评分(如 4.3 stars)和总评论量,两个独立字段
  • ASIN——亚马逊商品唯一标识符,藏在 <input name="ASIN"> 或 URL 中
  • 图片——主图和画廊图的高分辨率 URL,JSON 格式嵌在页面内的 <script> 中
  • Bullet Point 卖点——卖家写的 5 条核心卖点,商品展示的核心推广内容
  • 商品描述和 A+ 内容——较长的 HTML 格式内容块,部分 Listing 有品牌旗舰的 A+ 模块
  • BSR(Best Sellers Rank)——父类目和 1–2 个子类目的实时排名,选品研究的核心指标
  • 尺寸与重量——FBA 费用估算的必要参数
  • 库存与配送信息——在售状态、Prime 标识、预计到货日期

这些数据全是公开 HTML,访问本身不是问题,问题在于亚马逊如何在你采集第 2 个请求前就把你识别出来。

2026 年亚马逊的反爬机制到底是什么?

很多教程把「被亚马逊封了」描述成一个结果,但没解释背后的机制。理解反爬系统的技术层次,才能写出真正有效的绕过代码,而不是凭直觉调整参数。

第一层:TLS 指纹识别(最难绕过的一关)

TLS 握手发生在任何 HTTP 数据传输之前。当你的 Python 脚本向 amazon.com 发起 HTTPS 请求时,服务器会收到一个 ClientHello 消息,其中包含你的客户端支持的 cipher suite 列表、TLS 扩展列表、椭圆曲线参数等信息。把这些字段哈希处理后,得到的就是 JA3 指纹(或更新的 JA4)。

问题在于:Python requests 库使用的是 urllib3 + OpenSSL 组合,其 ClientHello 的 cipher suite 顺序和扩展列表是固定的「Python 特征」,与 Chrome、Firefox 或 Safari 的指纹完全不同。亚马逊的 Bot Manager(由 HUMAN Security 提供技术支持,前身是 PerimeterX)维护着一个已知的恶意指纹数据库,Python 的 TLS 指纹早就在其中。你的 IP 再干净、请求头伪装得再好,握手阶段已经暴露了。

Python requests 与 curl_cffi 的 TLS ClientHello 指纹对比,JA3 哈希差异说明
requests 的 TLS 指纹被亚马逊 Bot Manager 列为已知爬虫特征;curl_cffi 通过模拟 Chrome 完整握手绕过检测。

第二层:HTTP/2 帧设置检测

现代浏览器使用 HTTP/2 协议,而 HTTP/2 的 SETTINGS 帧(服务器在连接建立时发送的配置参数)同样具有指纹特征。Chrome 发送的 SETTINGS 帧包含特定的初始窗口大小、头部表大小和并发流数量设置。requests 默认用 HTTP/1.1,即使强制用 HTTP/2,其 SETTINGS 参数也与浏览器不同。curl_cffi 的 impersonate 参数同时模拟 TLS 指纹和 HTTP/2 帧设置,两层都能过。

第三层:行为分析(跨请求的会话级检测)

即使 TLS 和 HTTP/2 层都过了,亚马逊仍然会在会话层面分析你的行为模式:

  • 请求时间间隔——真实用户读商品页面需要 30–120 秒,而爬虫往往是 1–3 秒一个请求
  • Cookie 状态——真实浏览器会积累 session cookie、偏好 cookie,爬虫通常每次请求 cookie 状态不一致
  • Referer 链——用户从搜索结果页点进商品详情页,Referer 是 amazon.com/s?k=…;而爬虫直接请求 /dp/ 页面
  • Accept-Language 与地理 IP 的一致性——Accept-Language: en-US 但 IP 来自孟加拉达卡,这种组合会被标记

第四层:静默 CAPTCHA(最难调试的失败模式)

亚马逊最狡猾的反爬手段不是直接返回 403 或 503,而是返回 HTTP 200,但 HTML 内容是一个 CAPTCHA 验证页或「机器人检测」页面,而不是商品数据。你的代码看到 response.status_code == 200,以为成功了,但 soup.select_one("#productTitle") 返回 None。如果不检查原始 HTML,这个失败会无声无息地污染你整个数据集。

亚马逊静默 CAPTCHA 拦截页面,HTTP 200 状态码但商品数据字段为空的错误案例
亚马逊的「静默 CAPTCHA」:HTTP 状态码 200,但页面内容是机器人验证表单——这是爬虫最常见也最难发现的失败模式。

第五层:蜜罐链接(Honeypot)

亚马逊页面中有时会嵌入对人眼不可见的链接(CSS 隐藏,或颜色与背景相同)。真实用户不会点击这些链接,但爬虫如果解析了所有 <a href> 并盲目跟随,就会触发这些陷阱链接,立即将你的 IP 标记为爬虫。这意味着你的抓取逻辑需要过滤掉非可见元素。

curl_cffi:2026 年 Python 爬亚马逊的正确姿势

curl_cffi 是一个基于 Cython 绑定的库,底层调用 libcurl,关键在于其 impersonate 参数——支持直接指定模拟 Chrome、Firefox、Safari 的完整 TLS 指纹和 HTTP/2 设置。与 requests API 几乎兼容,迁移成本极低。

安装

# curl_cffi 包含预编译的 libcurl-impersonate 二进制,无需额外编译依赖
pip install curl-cffi beautifulsoup4 lxml tenacity

单商品采集:完整可运行代码

"""
亚马逊商品数据采集器(curl_cffi 版本)
2026 年可用,绕过 TLS/JA4 指纹检测
依赖: pip install curl-cffi beautifulsoup4 lxml tenacity
"""

import json
import random
import time
import logging
from dataclasses import dataclass, field
from typing import Optional

from curl_cffi import requests as cffi_requests
from bs4 import BeautifulSoup
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type

# ── 日志配置 ──────────────────────────────────────────────────────────────────
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
)
logger = logging.getLogger(__name__)


# ── 数据结构 ──────────────────────────────────────────────────────────────────
@dataclass
class AmazonProduct:
    asin: str
    title: Optional[str] = None
    brand: Optional[str] = None
    price: Optional[str] = None
    price_whole: Optional[str] = None
    list_price: Optional[str] = None
    rating: Optional[str] = None
    review_count: Optional[str] = None
    bullet_points: list = field(default_factory=list)
    bsr: list = field(default_factory=list)
    main_image: Optional[str] = None
    marketplace: str = "www.amazon.com"
    url: Optional[str] = None
    is_captcha: bool = False
    scraped_at: Optional[str] = None


# ── 请求头模板 ─────────────────────────────────────────────────────────────────
# 注意:curl_cffi 的 impersonate 会自动设置与浏览器一致的 TLS 握手,
# 但 HTTP 层的请求头仍需我们手动控制以匹配浏览器行为
HEADERS_TEMPLATES = [
    {
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
        "Accept-Language": "en-US,en;q=0.9",
        "Accept-Encoding": "gzip, deflate, br",
        "Cache-Control": "max-age=0",
        "Sec-Fetch-Dest": "document",
        "Sec-Fetch-Mode": "navigate",
        "Sec-Fetch-Site": "none",
        "Sec-Fetch-User": "?1",
        "Upgrade-Insecure-Requests": "1",
    },
    {
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
        "Accept-Language": "en-GB,en;q=0.5",
        "Accept-Encoding": "gzip, deflate, br",
        "DNT": "1",
        "Connection": "keep-alive",
        "Upgrade-Insecure-Requests": "1",
        "Sec-Fetch-Dest": "document",
        "Sec-Fetch-Mode": "navigate",
        "Sec-Fetch-Site": "cross-site",
    },
]

# curl_cffi 支持的浏览器指纹选项(选 Chrome 最新稳定版系列)
IMPERSONATE_TARGETS = [
    "chrome120",
    "chrome124",
    "chrome131",
]


def _get_random_headers() -> dict:
    return random.choice(HEADERS_TEMPLATES)


def _get_random_impersonate() -> str:
    return random.choice(IMPERSONATE_TARGETS)


def _is_captcha_page(soup: BeautifulSoup) -> bool:
    """
    检测是否命中亚马逊的 CAPTCHA / 机器人验证页面。
    亚马逊静默 CAPTCHA 返回 HTTP 200,但页面是验证表单而非商品数据。
    """
    captcha_indicators = [
        soup.select_one("form[action='/errors/validateCaptcha']"),
        soup.select_one("#captchacharacters"),
        soup.find("input", {"id": "captchacharacters"}),
    ]
    if any(captcha_indicators):
        return True

    # 检查页面标题是否是 "Robot Check" 或 "Sorry"
    title_tag = soup.find("title")
    if title_tag and title_tag.string:
        title_text = title_tag.string.lower()
        if any(kw in title_text for kw in ["robot check", "sorry!", "automated access", "api gateway"]):
            return True

    # 如果页面极短且没有 #productTitle,很可能是拦截页
    page_text = soup.get_text(strip=True)
    if len(page_text) < 2000 and not soup.select_one("#productTitle"):
        return True

    return False


def _extract_price(soup: BeautifulSoup) -> tuple[Optional[str], Optional[str]]:
    """
    提取当前价格和划线价(list price)。
    亚马逊价格字段分散在多个 DOM 节点,需要多级回退链。
    
    返回: (当前价格字符串, 划线价字符串)
    """
    price = None
    list_price = None

    # 方法 1:新版价格容器(2023 年后主流)
    price_block = soup.select_one(".a-price[data-a-color='price'] .a-offscreen")
    if price_block:
        price = price_block.get_text(strip=True)

    # 方法 2:旧版 whole + fraction 分体格式
    if not price:
        whole = soup.select_one("span.a-price-whole")
        fraction = soup.select_one("span.a-price-fraction")
        if whole:
            price = f"${whole.get_text(strip=True)}"
            if fraction:
                price = f"${whole.get_text(strip=True)}{fraction.get_text(strip=True)}"

    # 方法 3:Deal 价格
    if not price:
        deal_tag = soup.select_one("#priceblock_dealprice, #priceblock_saleprice")
        if deal_tag:
            price = deal_tag.get_text(strip=True)

    # 方法 4:核心价格 Box
    if not price:
        core_tag = soup.select_one("#corePriceDisplay_desktop_feature_div .a-offscreen")
        if core_tag:
            price = core_tag.get_text(strip=True)

    # 提取划线价(原价)
    list_price_tag = soup.select_one(".a-price[data-a-color='secondary'] .a-offscreen")
    if not list_price_tag:
        list_price_tag = soup.select_one("#listPrice, .a-text-price .a-offscreen")
    if list_price_tag:
        list_price = list_price_tag.get_text(strip=True)

    return price, list_price


def _extract_bsr(soup: BeautifulSoup) -> list:
    """
    提取 Best Sellers Rank(BSR)排名列表。
    一个商品可能同时在父类目和 1-2 个子类目有排名。
    """
    bsr_list = []

    # 方法 1:Product details 表格中的 Best Sellers Rank 行
    bsr_section = soup.select_one("#detailBulletsWrapper_feature_div, #productDetails_feature_div")
    if bsr_section:
        for li in bsr_section.select("li, tr"):
            text = li.get_text(" ", strip=True)
            if "Best Sellers Rank" in text or "Amazon Best Sellers Rank" in text:
                bsr_list.append(text)
                break

    # 方法 2:独立 #SalesRank 容器(部分类目)
    if not bsr_list:
        sales_rank = soup.select_one("#SalesRank")
        if sales_rank:
            bsr_list.append(sales_rank.get_text(" ", strip=True))

    return bsr_list


def _extract_images(soup: BeautifulSoup) -> Optional[str]:
    """
    亚马逊主图藏在页面内嵌的 JSON 数据中(colorImages 变量),
    直接解析 script 标签比抓 img src 更可靠。
    """
    import re
    scripts = soup.find_all("script", type="text/javascript")
    for script in scripts:
        if script.string and "'colorImages'" in script.string:
            # 提取第一张主图 URL(hiRes 或 large)
            match = re.search(r'"hiRes":"(https://[^"]+)"', script.string)
            if match:
                return match.group(1)
            match = re.search(r'"large":"(https://[^"]+)"', script.string)
            if match:
                return match.group(1)
    # 回退:抓 #landingImage 的 data-old-hires
    img_tag = soup.select_one("#landingImage[data-old-hires]")
    if img_tag:
        return img_tag.get("data-old-hires")
    return None


def _parse_product_page(html: str, asin: str, marketplace: str, url: str) -> AmazonProduct:
    """
    解析商品 HTML,返回 AmazonProduct 数据对象。
    """
    from datetime import datetime, timezone

    soup = BeautifulSoup(html, "lxml")
    product = AmazonProduct(asin=asin, marketplace=marketplace, url=url)
    product.scraped_at = datetime.now(timezone.utc).isoformat()

    # 检测 CAPTCHA
    if _is_captcha_page(soup):
        product.is_captcha = True
        logger.warning(f"CAPTCHA detected for ASIN {asin}")
        return product

    # 标题
    title_tag = soup.select_one("#productTitle")
    product.title = title_tag.get_text(strip=True) if title_tag else None

    # 品牌
    brand_tag = soup.select_one("#bylineInfo, #brand")
    if brand_tag:
        brand_text = brand_tag.get_text(strip=True)
        # 去掉 "Brand: " / "Visit the XXX Store" 前缀
        product.brand = brand_text.replace("Brand: ", "").replace("Visit the ", "").replace(" Store", "").strip()

    # 价格
    product.price, product.list_price = _extract_price(soup)

    # 评分
    rating_tag = soup.select_one("span[data-hook='rating-out-of-text'], #acrPopover")
    if rating_tag:
        product.rating = (rating_tag.get("title") or rating_tag.get_text()).strip()

    # 评论数
    review_tag = soup.select_one("#acrCustomerReviewText")
    product.review_count = review_tag.get_text(strip=True) if review_tag else None

    # Bullet Points
    bullets = []
    for li in soup.select("#feature-bullets ul li:not(.aok-hidden) span.a-list-item"):
        text = li.get_text(strip=True)
        if text and len(text) > 5:  # 过滤空白和极短的无效条目
            bullets.append(text)
    product.bullet_points = bullets

    # BSR
    product.bsr = _extract_bsr(soup)

    # 主图
    product.main_image = _extract_images(soup)

    return product


# ── 核心采集函数(带重试) ──────────────────────────────────────────────────────
@retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=2, min=4, max=30),
    retry=retry_if_exception_type((Exception,)),
    reraise=True,
)
def scrape_amazon_product(
    asin: str,
    marketplace: str = "www.amazon.com",
    proxy: Optional[str] = None,
) -> AmazonProduct:
    """
    用 curl_cffi 采集单个亚马逊商品页面。
    
    参数:
        asin: 亚马逊商品标识符(如 'B0CXXX')
        marketplace: 亚马逊域名(默认美国站)
        proxy: 代理 URL,格式 'http://user:pass@host:port'(可选)
    
    返回:
        AmazonProduct 数据对象
    
    异常:
        tenacity.RetryError - 重试 3 次后仍失败
    """
    url = f"https://{marketplace}/dp/{asin}"
    headers = _get_random_headers()
    impersonate = _get_random_impersonate()

    # 礼貌性延迟:2.5–6 秒随机间隔,模拟真实阅读节奏
    delay = random.uniform(2.5, 6.0)
    logger.info(f"Fetching ASIN {asin}, impersonate={impersonate}, delay={delay:.1f}s")
    time.sleep(delay)

    proxies = {"https": proxy, "http": proxy} if proxy else None

    response = cffi_requests.get(
        url,
        headers=headers,
        impersonate=impersonate,
        proxies=proxies,
        timeout=20,
        allow_redirects=True,
    )

    if response.status_code not in (200, 301, 302):
        raise ValueError(f"Unexpected status {response.status_code} for ASIN {asin}")

    return _parse_product_page(response.text, asin, marketplace, url)


# ── 入口示例 ──────────────────────────────────────────────────────────────────
if __name__ == "__main__":
    TEST_ASIN = "B0CXZXZXZX"  # 替换为真实 ASIN
    PROXY = None  # 可填入住宅代理,如 "http://user:[email protected]:8000"

    product = scrape_amazon_product(TEST_ASIN, proxy=PROXY)

    if product.is_captcha:
        print("⚠️  命中 CAPTCHA,需要更换代理或增加延迟")
    else:
        print(json.dumps(product.__dict__, indent=2, ensure_ascii=False))

上面这段代码跑出来是什么?

在代理干净、请求节奏正常的情况下,你会得到:

{
  "asin": "B0CXZXZXZX",
  "title": "示例商品——高级版,500ml,6件装(SGS 认证)",
  "brand": "ExampleBrand",
  "price": "$24.99",
  "list_price": "$29.99",
  "rating": "4.3 out of 5 stars",
  "review_count": "2,841 ratings",
  "bullet_points": [
    "SGS 认证高品质食品级材料,通过美国 FDA 标准",
    "可洗碗机清洗,不含 BPA、铅、镉",
    "适配标准汽车杯架(3.5 英寸直径),保温 12 小时",
    "18 个月制造商保修,无条件退换",
    "美国仓库发货,Prime 次日达"
  ],
  "bsr": ["#1,243 in Kitchen & Dining (#18 in Water Bottles)"],
  "main_image": "https://m.media-amazon.com/images/I/61xxxx.jpg",
  "marketplace": "www.amazon.com",
  "url": "https://www.amazon.com/dp/B0CXZXZXZX",
  "is_captcha": false,
  "scraped_at": "2026-06-10T01:00:00+00:00"
}

批量采集:异步 + 代理轮换的生产级方案

单商品能跑通了,接下来面临的是规模化问题。curl_cffi 同样支持异步模式(AsyncSession),配合代理轮换可以在不触发亚马逊限速的前提下把吞吐量推高一个数量级。

Python 批量采集亚马逊的异步架构:任务队列、限速控制、curl_cffi 代理轮换、自动重试流程图
生产级亚马逊 Python 爬虫的异步架构:asyncio + curl_cffi AsyncSession + 代理池 + tenacity 重试,构成四层防御。
"""
亚马逊批量异步采集器(生产级)
curl_cffi AsyncSession + asyncio Semaphore 限速 + 代理轮换 + CSV 输出
依赖: pip install curl-cffi beautifulsoup4 lxml tenacity
"""

import asyncio
import csv
import json
import random
import time
import logging
from dataclasses import asdict
from typing import Optional

from curl_cffi.requests import AsyncSession
from tenacity import retry, stop_after_attempt, wait_exponential

# 复用上面定义的 AmazonProduct、_parse_product_page、HEADERS_TEMPLATES 等

logger = logging.getLogger(__name__)


class ProxyRotator:
    """
    简单的代理轮换器,支持随机选取和失败标记。
    生产环境建议使用 Bright Data、Smartproxy 等服务的 rotating endpoint,
    而非手动维护 IP 列表。
    """

    def __init__(self, proxy_list: list[str]):
        self._proxies = proxy_list
        self._failed: set = set()

    def get(self) -> Optional[str]:
        available = [p for p in self._proxies if p not in self._failed]
        if not available:
            logger.warning("所有代理均已失败,使用直连(高风险)")
            return None
        return random.choice(available)

    def mark_failed(self, proxy: str):
        self._failed.add(proxy)
        logger.warning(f"代理 {proxy[:30]}... 标记为失败")


async def fetch_one(
    session: AsyncSession,
    asin: str,
    proxy_rotator: ProxyRotator,
    semaphore: asyncio.Semaphore,
    marketplace: str = "www.amazon.com",
) -> dict:
    """
    采集单个 ASIN,在 Semaphore 控制下运行(限制并发数量)。
    """
    url = f"https://{marketplace}/dp/{asin}"
    proxy = proxy_rotator.get()
    impersonate = random.choice(["chrome120", "chrome124", "chrome131"])
    headers = random.choice(HEADERS_TEMPLATES)

    async with semaphore:
        # 人性化延迟:每个请求前等 3-8 秒
        await asyncio.sleep(random.uniform(3.0, 8.0))

        try:
            proxies = {"https": proxy, "http": proxy} if proxy else None
            response = await session.get(
                url,
                headers=headers,
                impersonate=impersonate,
                proxies=proxies,
                timeout=25,
            )

            if response.status_code == 503:
                if proxy:
                    proxy_rotator.mark_failed(proxy)
                raise ValueError(f"503 for {asin}, proxy rotated")

            product = _parse_product_page(response.text, asin, marketplace, url)
            status = "captcha" if product.is_captcha else "success"
            logger.info(f"[{status.upper()}] ASIN {asin}")
            return asdict(product)

        except Exception as e:
            logger.error(f"[FAILED] ASIN {asin}: {e}")
            return {"asin": asin, "error": str(e)}


async def scrape_batch(
    asins: list[str],
    proxy_list: list[str],
    output_file: str = "amazon_products.json",
    max_concurrency: int = 3,
    marketplace: str = "www.amazon.com",
):
    """
    批量异步采集入口。
    
    max_concurrency=3 是针对住宅代理的保守设置。
    如果使用高质量轮换代理服务,可以适当提升到 5-8,但不建议更高。
    """
    proxy_rotator = ProxyRotator(proxy_list)
    semaphore = asyncio.Semaphore(max_concurrency)
    results = []

    async with AsyncSession() as session:
        tasks = [
            fetch_one(session, asin, proxy_rotator, semaphore, marketplace)
            for asin in asins
        ]
        results = await asyncio.gather(*tasks, return_exceptions=False)

    # 写出 JSON
    with open(output_file, "w", encoding="utf-8") as f:
        json.dump(results, f, indent=2, ensure_ascii=False)

    # 同时写出 CSV(方便导入 Excel / 数据库)
    csv_file = output_file.replace(".json", ".csv")
    if results:
        fieldnames = [k for k in results[0].keys() if k != "bullet_points"]
        with open(csv_file, "w", newline="", encoding="utf-8") as f:
            writer = csv.DictWriter(f, fieldnames=fieldnames + ["bullet_points_str"])
            writer.writeheader()
            for row in results:
                row_copy = {k: v for k, v in row.items() if k != "bullet_points"}
                row_copy["bullet_points_str"] = " | ".join(row.get("bullet_points", []))
                writer.writerow(row_copy)

    success = sum(1 for r in results if not r.get("error") and not r.get("is_captcha"))
    captcha = sum(1 for r in results if r.get("is_captcha"))
    failed = sum(1 for r in results if r.get("error"))
    logger.info(f"完成: {success} 成功, {captcha} CAPTCHA, {failed} 失败 | 输出: {output_file}")
    return results


# ── 使用示例 ──────────────────────────────────────────────────────────────────
if __name__ == "__main__":
    ASINS_TO_SCRAPE = [
        "B0CXZ1",
        "B0CXZ2",
        "B0CXZ3",
        "B0CXZ4",
        "B0CXZ5",
    ]

    PROXY_LIST = [
        "http://user1:[email protected]:8001",
        "http://user2:[email protected]:8002",
        "http://user3:[email protected]:8003",
    ]

    asyncio.run(
        scrape_batch(
            asins=ASINS_TO_SCRAPE,
            proxy_list=PROXY_LIST,
            output_file="output/amazon_products.json",
            max_concurrency=3,
        )
    )

这套方案的实际成功率预期

实测数据因代理质量差异很大:使用一线住宅代理服务(Bright Data、Smartproxy),配合 curl_cffi 的 Chrome 指纹模拟,小批量采集(每天 1,000–3,000 个)的成功率可以维持在 80–90%;规模提升到每天 10,000+ 时,成功率通常下滑到 60–75%,CAPTCHA 命中率开始显著上升,代理费用也相应攀升。这不是代码写法的问题,而是亚马逊行为分析系统的跨 IP 关联检测开始发挥作用。

Python 爬虫采集亚马逊成功率与规模关系图:curl_cffi vs requests 对比,超过 1 万页/天建议使用 Pangolinfo Scraper API
随采集规模提升,即使是 curl_cffi + 住宅代理的方案成功率也会显著下滑;每天超过 1 万页是切换到托管 API 的合理临界点。

手写爬虫规模化之后,代价是什么?

理解了技术机制,再来看账。维护一套能稳定运行的亚马逊 Python 爬虫,隐藏成本远比代码行数显示的要多。

Selector 维护:每周的沉没工程时间

亚马逊持续对商品页面做 A/B 测试,有时是为了转化率优化,有时仅仅是内部工程重构。1 月份工作的 #priceblock_ourprice,到 4 月已经在部分流量中被替换为新的价格容器结构,返回 None。上面代码中用了 4 个 Selector 回退链来提取价格——这不是过度设计,而是应对现实的最低标准。生产环境的亚马逊爬虫通常需要 10–15 个 Selector 来覆盖价格的所有变体(正常价、Deal 价、订阅折扣、「暂无库存」状态、B2B 价格等),且每隔 1–3 个月必然有至少一个失效需要修复。据维护中等规模亚马逊爬虫的工程团队反映,Selector 维护本身每周要消耗 2–4 小时的工程师时间——这还只是维持现有爬虫能跑,不是在构建新功能。

代理成本:随规模线性攀升的硬支出

数据中心代理($0.5–2/GB)在亚马逊面前基本无效,唯一能可靠绕过指纹检测的是住宅代理($5–15/GB)。按每个商品页面平均 150–200KB 压缩流量计算:

  • 每天 1,000 页:约 150–200MB,代理费用约 $0.75–3/天
  • 每天 10,000 页:约 1.5–2GB,代理费用约 $7.50–30/天($225–900/月)
  • 每天 100,000 页:约 15–20GB,代理费用约 $75–300/天($2,250–9,000/月)

这还不包括 CAPTCHA 解决费用、计算资源、工程师排障时间,以及因数据质量下降(CAPTCHA 导致的字段缺失)产生的下游数据修复成本。

多站点适配:不是翻译,是重写

如果你同时需要美国站、英国站、德国站、日本站的数据,麻烦程度不是×4,而更接近×8。不同站点的页面结构差异远比你预想的大:日本站(amazon.co.jp)的价格字段 class 名与美国站不同,德国站(amazon.de)的部分模块用 JavaScript 动态渲染,英国站的 BSR 类目体系与美国站完全不同。每个站点都需要独立的 Selector 集合,独立的维护周期。

当手写爬虫不再划算:Pangolinfo 亚马逊数据采集 API

托管型亚马逊数据采集 API 的核心价值不在于「帮你省去写代码」——而在于把反爬基础设施的维护责任从你的工程团队彻底移走,让你的代码只需要关心拿到数据后怎么用。同样的商品数据,用 Pangolinfo API 的代码:

"""
Pangolinfo 亚马逊数据采集 API 示例
无需管理代理、无需处理 CAPTCHA、无需维护 Selector
"""

import requests
import json
from typing import Optional


API_KEY = "your_pangolinfo_api_key"
BASE_URL = "https://api.pangolinfo.com/v1"


def get_amazon_product(
    asin: str,
    marketplace: str = "US",
    include_fields: Optional[str] = None,
) -> dict:
    """
    通过 Pangolinfo API 获取亚马逊商品数据。
    
    参数:
        asin: 亚马逊商品标识符
        marketplace: 站点代码(US, UK, DE, JP, CA, FR, IT, ES 等)
        include_fields: 需要的字段,逗号分隔(不传则返回全部)
    
    返回:
        完全结构化的商品数据 dict
    """
    params = {
        "asin": asin,
        "marketplace": marketplace,
    }
    if include_fields:
        params["include_fields"] = include_fields

    response = requests.get(
        f"{BASE_URL}/amazon/product",
        params=params,
        headers={"Authorization": f"Bearer {API_KEY}"},
        timeout=30,
    )
    response.raise_for_status()
    return response.json()


# ── 批量采集(异步版)────────────────────────────────────────────────────────
import asyncio
import aiohttp


async def get_amazon_product_async(
    session: aiohttp.ClientSession,
    asin: str,
    marketplace: str = "US",
) -> dict:
    url = f"{BASE_URL}/amazon/product"
    params = {"asin": asin, "marketplace": marketplace}
    headers = {"Authorization": f"Bearer {API_KEY}"}

    async with session.get(url, params=params, headers=headers) as resp:
        return await resp.json()


async def batch_fetch(asins: list[str], marketplace: str = "US") -> list[dict]:
    async with aiohttp.ClientSession() as session:
        tasks = [get_amazon_product_async(session, asin, marketplace) for asin in asins]
        return await asyncio.gather(*tasks)


# ── 使用示例 ──────────────────────────────────────────────────────────────────
if __name__ == "__main__":
    # 单个商品
    result = get_amazon_product("B0CXZXZXZX", marketplace="US")
    print(json.dumps(result, indent=2, ensure_ascii=False))

    # 批量(5 个 ASIN 并发)
    asins = ["B0001", "B0002", "B0003", "B0004", "B0005"]
    results = asyncio.run(batch_fetch(asins))
    print(f"获取到 {len(results)} 条商品数据")

API 返回的数据结构

响应是完全类型化的结构化 JSON,价格已解析为数字类型,BSR 已拆分为结构化数组,图片已区分主图和画廊——无需你写任何清洗逻辑:

{
  "success": true,
  "data": {
    "asin": "B0CXZXZXZX",
    "title": "示例商品——高级版,500ml,6件装",
    "brand": "ExampleBrand",
    "price": {
      "current": 24.99,
      "currency": "USD",
      "list_price": 29.99,
      "deal_price": null,
      "coupon": "额外 5% 优惠券"
    },
    "rating": 4.3,
    "review_count": 2841,
    "bullet_points": [
      "SGS 认证高品质食品级材料",
      "可洗碗机清洗,不含 BPA"
    ],
    "bsr": [
      {"rank": 1243, "category": "Kitchen & Dining"},
      {"rank": 18, "category": "Water Bottles"}
    ],
    "images": {
      "main": "https://m.media-amazon.com/images/I/xxx.jpg",
      "gallery": [
        "https://m.media-amazon.com/images/I/yyy.jpg",
        "https://m.media-amazon.com/images/I/zzz.jpg"
      ]
    },
    "dimensions": {
      "length_inches": 9.8,
      "width_inches": 3.5,
      "height_inches": 3.5,
      "weight_pounds": 0.88
    },
    "availability": "In Stock",
    "is_prime": true,
    "marketplace": "US",
    "scraped_at": "2026-06-10T01:00:00Z"
  },
  "credits_used": 1,
  "credits_remaining": 9999
}

手写 Python 爬虫 vs Pangolinfo 亚马逊数据采集 API:完整对比

维度手写 Python(curl_cffi + 住宅代理)Pangolinfo 亚马逊数据采集 API
首次可用时间2–6 小时(含代理配置、调试)15 分钟(申请 Key,第一次调用)
小规模成功率(<1K/天)80–90%(住宅代理 + curl_cffi)99%+
规模化成功率(10K+/天)60–75%(代理成本显著上升)99%+(托管基础设施)
CAPTCHA 处理需自行检测 + 第三方解码($1–3/千次)内置,对调用方透明
代理成本(10K 页/天)$7.50–30/天(住宅代理)含在 API 费用内
Selector 维护每周 2–4 小时工程时间零——API 自动适配页面变动
多站点支持每个站点需独立 Selector 集合一个参数切换 10+ 站点
数据结构化程度需自己写清洗逻辑,类型不一致完全类型化 JSON,字段统一
10K 页/天全成本$30–60+(代理 + CAPTCHA + 工程时间折算)约 $15.40($1.54/千请求)
规模上限实际约每天数万页(超出后不稳定)千万级页面/天

Pangolinfo 亚马逊数据采集 API 覆盖商品详情、搜索结果、评论、Best Sellers 榜单、类目页和卖家信息六种端点类型,一次接入即可覆盖整个亚马逊数据管道。对于需要监控竞品评论的用户,Reviews Scraper API 可以直接获取结构化评论数据,支持按评分、时间、地区筛选,免去自己维护评论分页爬虫的麻烦。

常见问题

2026 年用 Python 爬亚马逊还有效吗?

有效,但难度比 2–3 年前显著提升。亚马逊在 2024–2025 年全面升级了 TLS 指纹检测,标准 requests 库因为使用 Python 原生 TLS 栈会被立即识别。2026 年能实际跑通的方案是 curl_cffi——它通过 libcurl 模拟 Chrome 的 JA3/JA4 TLS 指纹,让请求在特征层面与真实浏览器流量无法区分。即便如此,代理轮换和请求限速依然不可或缺。

requests 爬亚马逊为什么会返回 503 或空页面?

亚马逊的 Bot Manager 在 TLS 握手阶段就完成了第一轮筛查。Python requests 的 TLS ClientHello 特征(cipher suite 顺序、扩展列表、ALPN 协议)与任何真实浏览器都不匹配,系统在返回任何 HTML 之前就已决定拦截。这就是为什么即使你的 IP 是干净的住宅地址,响应仍然是 503 或者带 CAPTCHA 的 200 空页面——问题在 TLS 握手层,不在 IP。

curl_cffi 和 requests 的核心区别是什么?

requests 使用 urllib3 + OpenSSL 构建 TLS 连接,其 ClientHello 指纹是固定的「Python 特征」,无法欺骗现代反爬系统。curl_cffi 底层调用 libcurl,通过 impersonate 参数直接指定模拟 Chrome、Firefox、Safari 的完整 TLS 指纹(包括 JA3、JA4 和 HTTP/2 设置帧)。两者 API 几乎相同,迁移成本很低,但在亚马逊这类有 TLS 指纹检测的站点上,成功率有数量级的差距。

亚马逊商品页面能采集哪些数据字段?

标准 ASIN 页面可采集:商品标题、品牌、价格(含原价、促销价、订阅折扣、划线价)、星级评分、评论总数、ASIN、主图和画廊图片 URL、Bullet Point 卖点、商品描述和 A+ 内容、BSR 排名(父类目和子类目)、尺寸重量、库存与配送状态(Prime 标识、预计到货日期)。使用 Pangolinfo 亚马逊数据采集 API 可直接获取所有字段的结构化 JSON,无需自己写解析逻辑。

什么情况下应该从手写爬虫切换到亚马逊数据采集 API?

几个明确的切换信号:每天采集量超过 5,000 个商品页,代理成本开始显著累积;Selector 维护占用工程师每周超过 2 小时;CAPTCHA 失败率导致数据缺口无法接受;需要覆盖多个亚马逊站点。Pangolinfo 亚马逊数据采集 API 起步价 $1.54/千请求,每天 10,000 页约 $15.40,而同等规模的自建方案(代理 + CAPTCHA + 工程时间折算)通常在 $40–80 之间。前 100 次请求免费,无需绑定信用卡。

下一步:选对工具,不要在错误的方向上投入工程资源

用 Python 采集亚马逊商品数据,2026 年的正确起点是 curl_cffi,而不是 requests。TLS 指纹识别已经让后者在亚马逊面前几乎失效,这不是配置问题,是根本性的技术层差异。本文的代码——curl_cffi 指纹模拟、多 Selector 回退链、静默 CAPTCHA 检测、异步批量采集、代理轮换——是一套经过实战打磨的起点,在中小规模下可以直接用。

同时,这篇文章也没有回避另一个事实:当采集规模超过每天 5,000–10,000 页,手写爬虫的代理成本、Selector 维护负担和 CAPTCHA 失败率会让账越算越难看。Pangolinfo 亚马逊数据采集 API 提供的不只是「帮你爬」,而是把反爬基础设施的维护责任彻底移走——你的工程资源应该用在数据分析和业务逻辑上,而不是追着亚马逊的页面改版跑。

准备好在不维护爬虫的情况下采集亚马逊商品数据了吗? 了解 Pangolinfo 亚马逊数据采集 API →

或查看API 文档中心,获取完整的 Python、Node.js、cURL 代码示例和端点 Schema。

微信扫一扫
与我们联系

QR Code
快速测试

联系我们,您的问题,我们随时倾听

无论您在使用 Pangolin 产品的过程中遇到任何问题,或有任何需求与建议,我们都在这里为您提供支持。请填写以下信息,我们的团队将尽快与您联系,确保您获得最佳的产品体验。

Talk to our team

If you encounter any issues while using Pangolin products, please fill out the following information, and our team will contact you as soon as possible to ensure you have the best product experience.