如何抓取 AliExpress 产品数据进行代发货研究
AliExpress 将其产品数据隐藏在一个 JavaScript 变量中,而不是在您看到的 HTML 中。获取该 JSON,您就可以在一次请求中获得价格、折扣、发货地和销售数量——以下是如何通过反封锁技术实现这一点。
AliExpress 是全球最大的市场,也是代发货研究的主要来源:价格和折扣历史、实际销售情况、发货地以及不同国家的报价变化。在 2026 年,它依赖于 JavaScript 渲染和随机化的类名,这让人们害怕启动一个完整的浏览器。通常您不需要这样做。产品数据已经在页面源代码中以 JavaScript 变量的形式存在——提取它,您就可以在一次请求中获得干净的结构化数据。本指南展示了隐藏的 JSON 方法、使 AliExpress 数据有价值的地理维度,以及保持抓取工具正常运行的代理技术。
数据在 window.runParams 中,而不是在 DOM 中
打开一个类别或搜索结果页面并查看源代码。所有产品预览都存储在一个名为 window.runParams 的 JavaScript 变量中,嵌在一个 <script> 标签内。这是一种常见的现代模式——服务器将数据作为 JSON 发送,前端在客户端渲染。对于抓取工具来说,这是一份礼物:您可以完全跳过脆弱的 CSS 选择器,使用正则表达式提取脚本标签,并像字典一样解析它。这种技术称为隐藏网页数据抓取,比追踪每次部署都会更改的类名要持久得多。
import re, json, httpx
HEADERS = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
"accept-language": "en-US,en;q=0.9",
}
PROXY = "http://USER:PASS@gate.quantumproxies.io:8000"
def get_run_params(html: str) -> dict:
# The product data lives in window.runParams = {...};
m = re.search(r"window\.runParams\s*=\s*(\{.+?\});", html, re.DOTALL)
if not m:
raise ValueError("runParams not found - page may be blocked or changed")
return json.loads(m.group(1))
url = "https://www.aliexpress.com/category/.../catName.html"
r = httpx.get(url, headers=HEADERS, proxy=PROXY, timeout=30)
data = get_run_params(r.text)
解析研究中重要的字段
runParams 数据块很大,因此只提取代发货和定价研究实际使用的字段:列表标题、原价与促销价、折扣百分比、销售数量,以及——至关重要的——发货国家,这决定了交货时间和买家的信任。嵌套看起来很复杂,但它是稳定的:
def parse_items(data: dict) -> list[dict]:
items = data.get("mods", {}).get("itemList", {}).get("content", [])
out = []
for it in items:
prices = it.get("prices", {})
sale = prices.get("salePrice", {})
orig = prices.get("originalPrice", {})
out.append({
"id": it.get("productId"),
"title": it.get("title", {}).get("displayTitle"),
"sale_price": sale.get("minPrice"),
"orig_price": orig.get("minPrice"),
"discount_pct": sale.get("discount"),
"currency": sale.get("currencyCode"),
"sold": it.get("trade", {}).get("tradeDesc"), # e.g. "1,000+ sold"
"store_id": it.get("store", {}).get("storeId"),
})
return out
for row in parse_items(data)[:5]:
print(row["title"][:40], row["sale_price"], f"-{row['discount_pct']}%", row["sold"])
这一单次调用为您提供了产品的经济数据:一个显示原价 $65.85 的列表降至 $23.29 的促销价是 64% 的折扣——这种利润率信号告诉您某个商品是否值得采购。“已售”数量是您的需求代理;在一个类别中综合起来,它可以在您不需要查看评论页面的情况下排名赢家。

搜索分页而不对网站造成负担
搜索页面使用已知长度的分页,因此高效的惯用方法是:抓取第一页,从响应中读取总页数,然后在限制下并发获取其余部分。不要一次性请求所有页面——AliExpress 会积极限制速率,并开始返回 403。保持适度的并发,并在每个请求时轮换出口 IP:
import asyncio, httpx
ROTATING = "http://USER:PASS@rotating.quantumproxies.io:8000"
async def fetch_page(client, query, page):
url = f"https://www.aliexpress.com/w/wholesale-{query}.html?page={page}"
for attempt in range(3):
r = await client.get(url, headers=HEADERS, timeout=30)
if r.status_code == 200:
return parse_items(get_run_params(r.text))
if r.status_code == 403: # blocked: back off, rotate exit
await asyncio.sleep(2 ** attempt)
return []
async def scrape_search(query, max_pages=5):
async with httpx.AsyncClient(proxy=ROTATING) as client:
sem = asyncio.Semaphore(3) # gentle concurrency
async def guarded(p):
async with sem:
return await fetch_page(client, query, p)
pages = await asyncio.gather(*[guarded(p) for p in range(1, max_pages + 1)])
return [row for page in pages for row in page]
results = asyncio.run(scrape_search("wireless earbuds"))
print(len(results), "products")
在这里,旋转网关很重要:一个 IP 连续请求页面是最快被封锁的方式,而在 住宅池 中进行每次请求的轮换可以分散负载,使没有单个出口看起来异常。对 403 进行指数退避,让被封锁的 IP 有时间轮换出去。
地理是重点
AliExpress 根据访问者的位置个性化价格、货币、促销和发货仓库——同一产品可以向美国 IP 显示美国本地卡,提供更快的运输,而向欧洲 IP 显示不同的价格和来源。对于代发货研究来说,这种地理维度就是数据:您想知道目标市场的客户实际看到的是什么。因此,故意设置出口国家。通过每个您销售的市场的 IP 路由,并记录差异;在一个地区便宜且本地发货的产品在另一个地区却慢且昂贵,这是一种非常不同的选择。我们关于 代发货研究代理 的指南深入探讨了如何将这些矩阵转化为采购决策。
# One product, three markets - compare price and ship-from per geo
MARKETS = {
"us": "http://USER:PASS-country-us@gate.quantumproxies.io:8000",
"de": "http://USER:PASS-country-de@gate.quantumproxies.io:8000",
"br": "http://USER:PASS-country-br@gate.quantumproxies.io:8000",
}
for market, proxy in MARKETS.items():
r = httpx.get(product_url, headers=HEADERS, proxy=proxy, timeout=30)
d = get_run_params(r.text)
# read price + shipFrom from the product detail runParams for this geo
print(market, "->", extract_price_and_origin(d))
评论和库存在单独的请求中
列表为您提供价格和需求,但两个高价值字段在它们自己的请求后面。客户评论不在产品页面的 runParams 中——它们从一个专用的反馈端点加载,分页,因此要收集评分和评论文本,您需要跟随每个产品的调用,而不是解析主页面。库存信号类似:列表上的“已售”数量是一个累计需求代理,而每个变体(颜色、尺寸、发货仓库)的精确可用性来自产品详细信息负载中的 SKU 数据。对于代发货研究来说,这很重要,因为一个产品在价格和销售上看起来像是赢家,而您想销售的特定变体在为您的市场服务的仓库中缺货。
将这两个视为第二次处理:首先抓取列表,根据折扣和已售数量对候选者进行排名,然后仅用评论页面和每个变体的库存丰富您的候选名单。这使请求量——以及您的封锁风险——与您对每个产品的重视程度成比例,而不是对每个列表进行大量请求以获取您不会使用的数据。
何时跳过 DIY 抓取工具
隐藏的 JSON 方法是持久的,但 AliExpress 确实会更改负载形状,将评论置于单独的端点后,并在大量请求下升级封锁。如果您不想维护正则表达式和重试逻辑,Scraper API 接受一个 URL 并返回解析后的 JSON——它处理渲染、轮换和反封锁层,您可以通过单个参数进行地理定位。一旦您计划定期收集跨市场的数千种产品,这是务实的选择。有关直接从客户端负载读取数据的一般模式,请参阅我们关于 抓取工具返回空页面的原因 的说明。

常见问题
可以抓取 AliExpress 产品数据吗?
可以。公开可见的列表数据——标题、价格、折扣、已售数量、图片和发货地——在类别、搜索和产品页面的 window.runParams JavaScript 变量中提供。您可以使用正则表达式提取它,并将其解析为 JSON,而无需为列表使用无头浏览器。请遵守 AliExpress 的条款和速率限制。
AliExpress 有公共 API 吗?
AliExpress 提供联盟和开放平台 API,但它们需要批准,覆盖范围有限,并与一个项目绑定。对于跨类别和市场的广泛研究,抓取公共页面可以为您提供更多字段和完整的地理控制,这就是为什么大多数代发货研究人员提取页面上的 JSON。
为什么抓取 AliExpress 时会出现 403 错误?
403 表示 AliExpress 标记了请求——通常是因为来自一个 IP 的请求过多、数据中心范围或头部设置过少。每次请求轮换住宅 IP,对 403 进行指数退避,发送真实的 User-Agent 和接受语言,并保持低并发。如果 HTML 中缺少 runParams,那是封锁,而不是布局更改。
如何获取特定国家的 AliExpress 价格?
通过目标国家的出口 IP 路由请求。AliExpress 读取位置以设置货币、价格、促销和发货仓库,因此美国 IP 和德国 IP 对同一产品看到的数据不同。根据市场对代理进行地理定位,并记录每个版本以建立按地理位置划分的价格矩阵。
获胜的方法是无聊且持久的:读取页面已经提供的 JSON,礼貌地分页,轮换干净的住宅 IP,并对每个您关心的市场进行地理定位。这样做,AliExpress 就成为价格、需求信号和运输经济的可靠来源,而不是 403 的墙。