TL;DR:自建亚马逊 Top 100 爬虫的核心成本不是 IP 和服务器,是工程师维护反爬对抗的隐性时间成本。接入专业 API(如 Pangolinfo Scrape API)可以把数据采集从一个持续消耗工程资源的运维问题,变成一个 30 分钟能跑起来的 REST 调用。本文分享完整的工程思路和代码。
为什么亚马逊类目 Top 100 数据值得系统化采集
亚马逊 Best Sellers 列表是电商数据里少见的"高信噪比公开数据"。它直接反映了消费者的真实购买行为,而不是问卷调研、广告点击或用户评分——是货真价实的成交数据排序结果。
从工程师视角来看,这份数据有几个特点让它很值得系统化处理:
- 结构化程度高:每个类目的 Top 100 页面结构基本一致,适合批量解析
- 时序性强:排名随时间变化本身就是信号——上升速度、位置稳定性都有分析价值
- 跨类目可比:不同类目间的榜单数据可以做标准化对比,识别横向机会
- 覆盖范围广:亚马逊全站超过 35,000 个子类目,都有对应的 Top 100 榜单
技术挑战:为什么这不是一个简单的爬虫任务
亚马逊 Best Sellers 页面虽然公开,但针对机器人访问的防御层数比大多数电商网站多得多:
IP 频率限制 → TLS 指纹检测 → 行为特征分析 → 动态 CAPTCHA 注入
实测数据:使用普通住宅代理(Residential Proxy)进行高频采集时,第四层 CAPTCHA 触发率达到 60–80%。一次触发意味着对应请求失败,如果你正在批量采集 200 个类目,中途的 CAPTCHA 会让数据集出现缺口,破坏时序完整性。
还有一个经常被低估的问题:解析器的脆弱性。亚马逊 2024 年对 Best Sellers 页面的 HTML 结构做了至少 11 次更新,其中 3 次是破坏性变更,导致依赖固定 CSS 选择器的爬虫完全失效,数据中断时间 6–48 小时。这种维护成本,按工程师时薪算,可能远超代理费用本身。
工程方案选型
我对比了四种方案的实际生产表现:
| 方案 | 初始成本 | 月维护成本 | 可扩展性 | 稳定性 |
|---|---|---|---|---|
| Python + Scrapy | 低(3–5天开发) | 高(持续反爬对抗) | 差(规模化需重构) | 不稳定 |
| Playwright 无头浏览器 | 中 | 高 | 很差(资源消耗大) | 不稳定 |
| SaaS 工具(Jungle Scout 等) | 无 | 中($279+/月 API) | 差(API 有并发限制) | 受制于服务商 |
| Pangolinfo Scrape API | 低(30min 集成) | 极低 | 好(按需扩展) | 稳定(99.7%) |
选 Pangolinfo 的核心逻辑:把反爬对抗、解析器维护、IP 池管理这些和业务无关的工程问题,交给专门做这件事的团队处理,自己团队专注于数据分析和产品逻辑。
完整代码实现
项目结构
amazon_top100/
├── config.py # 配置管理
├── collector.py # API 采集模块
├── storage.py # 数据存储模块
├── analyzer.py # 数据分析模块
├── scheduler.py # 定时任务
└── main.py # 入口
采集核心(collector.py)
import requests
import time
import random
from datetime import datetime, timezone
from loguru import logger
class PangolinTop100Client:
"""Pangolinfo Scrape API 封装客户端"""
BASE_URL = "https://api.pangolinfo.com/scrape"
BESTSELLER_FIELDS = [
"rank", "asin", "title", "price", "original_price",
"rating", "review_count", "brand", "is_prime",
"badge", # Best Seller / Amazon's Choice / None
"fulfillment", # FBA / FBM / Amazon
"variant_count",
"subcategory_path", # 完整面包屑路径
"image_url",
"customer_says", # AI 评论摘要(2024年新增字段)
"sp_ad_slot", # SP 广告位(Pangolinfo 98% 采集率)
]
def __init__(self, api_key: str):
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
})
def get_top100(
self,
url: str,
marketplace: str = "US",
output: str = "json", # json / markdown / html
retries: int = 3,
) -> list[dict]:
"""获取单个类目 Top 100 数据"""
payload = {
"url": url,
"marketplace": marketplace,
"output_format": output,
"parse_template": "amazon_bestsellers",
"include_fields": self.BESTSELLER_FIELDS,
}
for attempt in range(retries):
try:
resp = self.session.post(self.BASE_URL, json=payload, timeout=30)
resp.raise_for_status()
products = resp.json().get("products", [])
now = datetime.now(timezone.utc).isoformat()
for p in products:
p["_meta"] = {
"scraped_at": now,
"marketplace": marketplace,
"category_url": url,
}
logger.success(f"{marketplace} | {url} → {len(products)} products")
return products
except requests.Timeout:
logger.warning(f"Timeout (attempt {attempt+1}/{retries})")
except requests.HTTPError as e:
logger.error(f"HTTP {e.response.status_code}")
if e.response.status_code in (401, 403, 422):
break # 不可重试的错误
except Exception as e:
logger.error(f"Error: {e}")
time.sleep((attempt + 1) * 2 + random.uniform(0, 0.5))
return []
趋势分析(analyzer.py)
import pandas as pd
import sqlite3
def find_rising_products(
db_path: str,
days: int = 7,
min_rank_improvement: int = 15,
max_current_rank: int = 50, # 只关注当前在前 50 的商品
) -> pd.DataFrame:
"""
识别近期排名快速上升的潜力商品
筛选条件:
- 排名在 N 天内改善超过 min_rank_improvement 位
- 当前处于类目 Top max_current_rank
"""
query = f"""
WITH snapshots AS (
SELECT
asin, title, brand, price, rating, review_count,
rank, category_url, marketplace, scraped_at,
RANK() OVER (
PARTITION BY asin, category_url
ORDER BY scraped_at
) as time_seq,
RANK() OVER (
PARTITION BY asin, category_url
ORDER BY scraped_at DESC
) as rev_seq
FROM amazon_top100
WHERE scraped_at >= datetime('now', '-{days} days')
)
SELECT
asin, title, brand, category_url, marketplace,
MAX(CASE WHEN time_seq = 1 THEN rank END) as rank_first,
MIN(CASE WHEN rev_seq = 1 THEN rank END) as rank_latest,
MAX(CASE WHEN time_seq = 1 THEN rank END) -
MIN(CASE WHEN rev_seq = 1 THEN rank END) as improvement,
ROUND(AVG(price), 2) as avg_price,
MAX(review_count) as peak_reviews
FROM snapshots
GROUP BY asin, category_url
HAVING improvement >= {min_rank_improvement}
AND rank_latest <= {max_current_rank}
ORDER BY improvement DESC
"""
with sqlite3.connect(db_path) as conn:
df = pd.read_sql_query(query, conn)
return df
运行结果示例
2026-05-12 08:00:01 | INFO | Starting collection job (20 categories)
2026-05-12 08:00:03 | SUCCESS | US | Electronics → 100 products
2026-05-12 08:00:04 | SUCCESS | US | Home-Kitchen → 100 products
2026-05-12 08:00:05 | SUCCESS | US | Sports-Outdoors → 98 products
...
2026-05-12 08:00:41 | INFO | Total: 1963/2000 products collected
2026-05-12 08:00:42 | INFO | Saved 1963 records to DB
=== Rising Products (7d, improvement ≥ 15 ranks) ===
ASIN: B0XXXXXX | Air Fryer 5.8Qt | Rank: #67 → #12 (+55) | $48.99 | ⭐4.6
ASIN: B0YYYYYY | Silicone Spatula Set | Rank: #89 → #31 (+58) | $22.99 | ⭐4.8
ASIN: B0ZZZZZZ | Resistance Bands Set | Rank: #94 → #19 (+75) | $34.99 | ⭐4.5
延伸:接入 AI Agent 工作流
如果你在构建 AI 驱动的选品分析工具,可以使用 Pangolinfo Amazon Scraper Skill直接通过 MCP 协议让 AI Agent 调用亚马逊类目数据,省去中间的 API 封装层。
Skill 返回的默认格式是 Markdown,可以直接作为 LLM 的 context 输入,非常适合需要 AI 分析"这个类目现在的竞争格局如何"这类开放性问题的场景。
有问题欢迎评论区交流,点赞收藏是持续更新的动力 🙏
#API #Python #亚马逊 #电商数据 #爬虫