抓取Temu商品与价格数据:API端点 vs HTML解析的核心权衡
Temu是目前全球增长最快的电商平台之一,但它没有提供公开的商品或价格API。对于电商数据工程师和价格情报团队来说,这意味着你必须在两条路径之间做出选择:解析易变的HTML商品卡片,或者直接请求Temu内部的JSON端点(如 /api/poppy/v1/search 和商品详情接口)。这两条路径各有利弊,而无论选哪条,你都必须应对Cloudflare反爬体系、签名 anti_content 头以及TLS/HTTP2指纹检测。本指南将系统讲解如何在2026年抓取Temu商品与价格数据,涵盖技术上下文、实战代码、代理配置和合规边界。
核心权衡如下:HTML解析简单但脆弱——Temu频繁更新前端,CSS类名经过哈希处理,今天能用的选择器明天就可能失效;而内部JSON端点返回结构化数据(goods_id、价格、SKU列表等),稳定性更高,但需要构造正确的请求头和签名参数,且更容易触发反爬检测。对于构建Temu价格抓取管道的团队,JSON端点通常是更优选择——前提是你能正确处理反爬挑战。
技术背景:Temu的反爬技术栈
Temu的反爬体系是多层次的,理解每一层是成功抓取的前提。
Cloudflare Turnstile 与 JS Challenge
Temu前端由 Cloudflare Turnstile 保护,这是一个无需用户交互的CAPTCHA替代方案。Turnstile会在页面加载时注入JS挑战脚本,收集浏览器环境信号(Canvas指纹、WebGL参数、navigator属性等),生成一个token。如果你的请求缺少有效token或浏览器指纹不匹配,Cloudflare会返回403或503状态码,并要求完成交互式验证。
签名 anti_content 头
Temu的内部API请求需要携带一个 anti_content 参数(有时也叫 anti_token)。这是一个由前端JS生成的签名令牌,包含时间戳、设备指纹和请求参数的哈希。它的生成逻辑混淆在webpack打包的JS bundle中,逆向难度较高。没有有效的 anti_content,JSON端点会直接返回错误码而非数据。
TLS/HTTP2 指纹检测
这是最容易被忽视的一层。Temu后端会检查TLS握手特征(JA3/JA4指纹)和HTTP2设置帧参数。标准的Python requests 库使用urllib3的TLS栈,其指纹与真实Chrome浏览器差异巨大,即使IP干净也会被识别。根据 RFC 8446 (TLS 1.3) 规范,不同TLS实现在扩展顺序、密码套件列表和椭圆曲线上存在可检测的差异。这就是为什么许多团队用 requests 发请求时,即使配了住宅代理也会被秒封——问题不在IP,而在指纹。
数据中心IP即时拦截
Temu对数据中心IP段的封锁几乎是即时的。AWS、GCP、Azure、DigitalOcean等云厂商的IP段在Cloudflare的IP信誉数据库中标记为高风险。用数据中心代理抓取Temu,首次请求可能成功,但第二次就会被challenge或直接403。住宅代理是强制要求,而非可选优化。
定位数据:嵌入状态与JSON端点
Temu的商品页面和搜索结果页有两种数据获取方式。
方式一:解析嵌入的 __NEXT_DATA__ 状态块
Temu使用Next.js框架,页面HTML中嵌入一个 <script id="__NEXT_DATA__" type="application/json"> 标签,包含序列化的服务端状态。你可以用正则或BeautifulSoup提取这个JSON块,然后解析其中的商品数据。这种方式只需一次HTML请求,不需要构造 anti_content,适合快速原型。
import re, json, requests
html = response.text
match = re.search(r'<script id="__NEXT_DATA__" type="application/json">(.*?)</script>', html)
if match:
data = json.loads(match.group(1))
goods = data['props']['pageProps']['goods'] # 路径可能随版本变化
缺点:__NEXT_DATA__ 的结构会随Next.js版本和页面类型变化,且并非所有数据都包含在内(SKU级别的库存信息经常缺失)。
方式二:直接请求内部JSON端点
Temu的搜索和商品详情数据背后是以下端点(路径可能随版本微调):
- 搜索:
/api/poppy/v1/search(POST,返回商品列表含goods_id、标题、价格、图片URL) - 商品详情:
/api/poppy/v1/goods/detail(POST,返回完整SKU列表、规格、价格层级、库存状态)
这些端点返回结构化JSON,比HTML解析稳定得多,但需要正确的请求头组合和 anti_content 签名。在浏览器DevTools的Network面板中,你可以观察到这些请求的实际参数格式。
哈希CSS类与 data-uniqid 属性
如果不得不解析HTML,注意Temu的CSS类名是哈希生成的(如 r-1qfo8i5),不能依赖语义化类名。更可靠的选择器锚点是 data-uniqid 属性和 data-pid(商品ID)属性。例如:
# XPath 示例
# 商品卡片
//div[@data-uniqid and contains(@class, 'goods')]
# 价格元素
//span[contains(@class,'price')]/span[@data-uniqid]
速率限制阈值与住宅代理的必要性
根据实际测试和社区反馈,Temu对单个IP的请求频率限制大约在每分钟20-40次请求的范围(具体阈值因端点和IP信誉而异)。超过这个阈值会触发Cloudflare的rate-limit challenge,返回429状态码或要求完成Turnstile验证。连续触发会导致IP被临时封禁约10-30分钟。
更关键的是:Temu的价格和运费因地区而异。同一商品在美国和中国、甚至美国不同州的展示价格、运费和促销信息都可能不同。这意味着:
- 你需要使用带有国家/城市级地理定位的住宅代理,例如
-country-US或-country-US-city-newyork。 - 数据中心代理不仅会被反爬拦截,即使偶尔成功也无法获取真实的目标市场价格。
- 移动代理(4G/5G)在某些场景下比住宅代理更不易被封,但延迟更高(通常200-500ms vs 住宅的50-150ms)。
对于构建Temu商品数据API替代方案的团队,建议的架构是:住宅代理池 + 城市级地理定位 + 每IP每分钟控制在15次请求以下 + 自动轮换IP。
| 代理类型 | Temu成功率 | 地理定位精度 | 延迟 | 适用场景 |
|---|---|---|---|---|
| 数据中心代理 | <5% | 无 | 10-50ms | 不推荐 |
| 住宅代理(国家级) | 70-85% | 国家 | 50-150ms | 基础价格抓取 |
| 住宅代理(城市级) | 80-92% | 城市 | 50-150ms | 区域价格对比 |
| 移动代理(4G/5G) | 85-95% | 国家/运营商 | 200-500ms | 高难度反爬场景 |
实战:用 curl_cffi + ProxyHat 抓取Temu商品页
由于Temu检测TLS指纹,标准 requests 库不可用。我们使用 curl_cffi,它基于libcurl并支持模拟Chrome的TLS/HTTP2指纹。配合ProxyHat住宅代理,完整示例如下:
安装依赖
pip install curl_cffi selectolax
Python抓取代码
from curl_cffi import requests as cffi_requests
import json, re, time, random
# ProxyHat 住宅代理配置 — 城市级地理定位
PROXY = "http://user-country-US-city-newyork:YOUR_PASSWORD@gate.proxyhat.com:8080"
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/124.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
"Referer": "https://www.temu.com/",
}
def fetch_product_page(goods_id: str, session_id: str = None):
"""抓取Temu商品页面并提取 __NEXT_DATA__ JSON"""
# 使用 sticky session 保证同一IP获取一致的运费/价格
if session_id:
proxy = f"http://user-country-US-city-newyork-session-{session_id}:YOUR_PASSWORD@gate.proxyhat.com:8080"
else:
proxy = PROXY
url = f"https://www.temu.com/goods.html?goods_id={goods_id}"
for attempt in range(3):
try:
resp = cffi_requests.get(
url,
headers=HEADERS,
proxies={"http": proxy, "https": proxy},
impersonate="chrome124",
timeout=20,
)
if resp.status_code == 200:
return parse_next_data(resp.text)
elif resp.status_code in (403, 429):
wait = 2 ** attempt + random.uniform(0, 1)
print(f"Blocked ({resp.status_code}), retrying in {wait:.1f}s")
time.sleep(wait)
else:
print(f"Unexpected status: {resp.status_code}")
time.sleep(1)
except Exception as e:
print(f"Request error: {e}")
time.sleep(2 ** attempt)
return None
def parse_next_data(html: str):
"""从HTML中提取 __NEXT_DATA__ JSON 块"""
match = re.search(
r'<script id="__NEXT_DATA__" type="application/json">(.*?)</script>',
html, re.DOTALL
)
if not match:
return None
return json.loads(match.group(1))
# 执行抓取
data = fetch_product_page("60109951327777", session_id="temu-cart-001")
if data:
# 提取关键字段(路径因版本而异,需根据实际响应调整)
store = data.get("props", {}).get("pageProps", {})
goods = store.get("goods", store.get("product", {}))
print(json.dumps({
"goods_id": goods.get("goodsId", goods.get("goods_id")),
"title": goods.get("goodsName", goods.get("title")),
"price": goods.get("minPrice", goods.get("price", {}).get("min")),
"sku_list": goods.get("skuList", [])[:3], # 截断示例
}, ensure_ascii=False, indent=2))
截断的示例JSON响应
{
"goods_id": "60109951327777",
"title": "Wireless Bluetooth Earbuds Pro Max",
"price": {
"min": "12.98",
"max": "19.99",
"currency": "USD",
"original": "29.99"
},
"sku_list": [
{
"sku_id": "SKU001",
"spec": "Black",
"price": "12.98",
"stock": 3421,
"thumb_url": "https://img.temu.com/.../black.jpg"
},
{
"sku_id": "SKU002",
"spec": "White",
"price": "13.49",
"stock": 2180,
"thumb_url": "https://img.temu.com/.../white.jpg"
},
{
"sku_id": "SKU003",
"spec": "Blue",
"price": "14.99",
"stock": 0,
"thumb_url": "https://img.temu.com/.../blue.jpg"
}
]
}
Sticky Session、重试与分页策略
粘性会话(Sticky Sessions)
当抓取涉及购物车或运费计算时,你需要保证一系列请求来自同一IP(同一地理位置)。ProxyHat支持在用户名中嵌入 -session- 标志来实现粘性会话:
# 粘性会话 — 同一 session ID 在有效期内保持同一出口IP
http://user-country-US-session-temu-cart-001:PASSWORD@gate.proxyhat.com:8080
典型应用场景:先抓取商品详情页,再请求运费接口,最后检查库存——这三步必须来自同一IP同一地区,否则运费和库存数据会不一致。
重试与指数退避
遇到403/429时,采用指数退避重试。基础等待时间为 2^attempt 秒,加上0-1秒随机抖动,避免雷同的请求模式。最多重试3次,超过则记录失败并切换IP。
分页策略
Temu搜索结果采用无限滚动加载,分页参数通常为 page 或 offset/limit。建议:
- 每页请求间隔2-5秒随机延迟。
- 每抓取10-15页后轮换IP(更换session ID)。
- 设置最大页数限制(如50页),避免触发深层反爬。
- 使用 SERP追踪类似的分页管理逻辑来跟踪抓取进度。
ProxyHat代理配置详解
ProxyHat提供住宅、移动和数据中心代理。对于Temu抓取,只推荐住宅代理(城市级地理定位)。以下是关键配置参数:
| 参数 | 值 | 说明 |
|---|---|---|
| 网关 | gate.proxyhat.com | 统一入口 |
| HTTP端口 | 8080 | 默认HTTP代理端口 |
| SOCKS5端口 | 1080 | SOCKS5代理端口 |
| 国家级定位 | -country-US | 指定出口国家 |
| 城市级定位 | -country-US-city-newyork | 指定出口城市 |
| 粘性会话 | -session-abc123 | 保持同一出口IP |
更多代理位置和定价信息,请访问 代理位置页面 和 定价页面。完整API文档请参考 ProxyHat官方文档。
对于大规模Temu价格抓取项目,建议结合 网页抓取用例中的架构模式,使用并发管理器控制同时活跃的会话数在50-100个并发会话左右,配合自动IP轮换。
curl 命令行快速测试
在写Python脚本之前,可以先用curl快速验证代理连通性和页面可访问性:
curl -x "http://user-country-US-city-newyork:PASSWORD@gate.proxyhat.com:8080" \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
-H "Accept: text/html,application/xhtml+xml" \
-H "Accept-Language: en-US,en;q=0.9" \
"https://www.temu.com/goods.html?goods_id=60109951327777" \
-o temu_product.html -w "%{http_code} %{time_total}s\n"
注意:curl的TLS指纹与Chrome不同,成功率可能低于curl_cffi。curl仅用于快速验证代理连通性。
常见错误与边界情况
- 403/503状态码:通常是TLS指纹不匹配或IP被标记。确认使用
impersonate="chrome124"参数,并检查代理是否为住宅类型。 - 价格不匹配:同一商品在不同地区显示不同价格。确保代理地理定位与目标市场一致,使用粘性会话避免中途IP切换。
- __NEXT_DATA__ 缺失:某些页面类型(如登录后的个性化页面)可能不嵌入
__NEXT_DATA__。回退到JSON端点或HTML解析。 - SKU库存为0:可能是地区性缺货,而非全站缺货。用不同城市的代理交叉验证。
- 反爬升级:Temu可能随时更新反爬策略。建立监控告警,当成功率下降到低于70%时触发检查流程。
- 请求超时:住宅代理延迟波动较大。设置20-30秒超时,并实现重试逻辑。
合规与道德边界
抓取Temu数据时必须遵守以下原则:
- 仅抓取公开目录数据:商品标题、价格、图片URL、SKU规格等公开可见信息。不抓取用户评论中的个人数据、不抓取需要登录才能访问的页面。
- 遵守robots.txt:检查
https://www.temu.com/robots.txt的规则,尊重禁止抓取的路径。 - 遵守服务条款:Temu的服务条款可能禁止自动化数据采集。违反ToS可能导致法律风险,特别是在美国 CFAA(计算机欺诈和滥用法案) 框架下。
- GDPR/CCPA注意:如果抓取的数据涉及欧盟或加州用户的个人信息(如评论中的用户名),需遵守GDPR和CCPA的数据处理规定。
- 不抓取账户数据:不访问订单历史、支付信息、收货地址等敏感数据。
- 控制请求频率:不对Temu服务器造成实质性负担,合理控制并发和速率。
如果你是Temu平台商家,需要获取自己店铺的数据,优先使用Temu官方的商家后台数据导出功能或商家API(如有),而非抓取。官方渠道的数据更准确、更及时,且无法律风险。
关键要点总结
核心结论:
- Temu无公开API,需在HTML解析和内部JSON端点之间选择——JSON端点更稳定但需处理
anti_content签名。- 反爬栈包括Cloudflare Turnstile、签名token、TLS/HTTP2指纹——必须用
curl_cffi模拟Chrome指纹。- 数据中心IP会被秒封,住宅代理(城市级地理定位)是强制要求。
- 价格和运费因地区而异,粘性会话保证数据一致性。
- 仅抓取公开目录数据,遵守ToS、CFAA和GDPR/CCPA。






