为什么你需要抓取eBay——以及为什么它越来越难
无论你是做转售情报、竞品定价研究,还是构建电商数据管道,scrape eBay的需求始终存在。eBay拥有超过13亿活跃商品列表,是全球最大的二手与新品交易市场之一。但eBay的反爬体系同样成熟——数据中心IP几乎秒封,高频请求触发验证码,区域定价差异让全球视角的数据采集变得复杂。
本文从实践角度出发,先厘清API与HTML抓取的边界,再深入页面结构、代理选型、拍卖数据处理和卖家分析,最后给出可运行的Python代码。面向转售情报团队和市场研究人员,不废话。
eBay API vs HTML抓取:何时用哪个?
eBay官方提供两套核心API:Finding API(搜索与筛选)和Browse API(商品详情与分类浏览)。听起来很美好,但实际使用中有硬性限制。
API的硬限制
- Finding API:每日调用上限约5,000次(标准App),每次返回最多100条结果,分页上限100页——即单次搜索最多10,000条。
- Browse API:需要OAuth token,速率限制约每秒5次请求;部分字段(如卖家详细反馈、跨类目关联)不返回。
- API申请门槛:需要eBay开发者账号、App Key审批,生产环境需额外审核。
- 数据新鲜度:API缓存通常5-15分钟,拍卖倒计时和实时竞价数据存在延迟。
当你需要超量采集、实时拍卖数据、卖家深度画像或区域差异化定价时,API力不从心,HTML抓取成为必要的补充方案。
API vs 抓取对比
| 维度 | Finding/Browse API | HTML抓取 |
|---|---|---|
| 每日数据量 | ~5,000次调用 × 100条 | 取决于代理池规模 |
| 实时性 | 5-15分钟缓存 | 页面即所得 |
| 字段丰富度 | 结构化但有限 | 完整页面数据 |
| 反爬风险 | 无 | 高(需代理) |
| 维护成本 | 低(结构化) | 中(CSS选择器可能变动) |
| 区域定价 | 需指定siteid参数 | 直接访问区域站点 |
| 卖家深度数据 | 部分缺失 | 可完整采集 |
最佳实践:API做基础批量查询,抓取做深度补充。先用API拿到商品ID列表,再用eBay listing scraper逐页抓取详情和卖家数据。
目标页面HTML结构详解
eBay的页面结构经历过多次改版,但核心选择器相对稳定。以下基于2024-2025年的页面版本。
搜索结果页:.s-item 结构
搜索结果URL格式:https://www.ebay.com/sch/i.html?_nkw=KEYWORD&_pgn=PAGE
每个商品卡片在.s-item容器内,关键字段选择器:
- 商品标题:
.s-item__title - 价格:
.s-item__price - 运费:
.s-item__shipping - 商品链接:
.s-item__link(href属性) - 图片:
.s-item__image-img(src属性) - 拍卖信息:
.s-item__bid-count、.s-item__time-left - Buy It Now标记:
.s-item__purchase-option-with-icon - 卖家名:
.s-item__seller-info
商品详情页
详情页URL格式:https://www.ebay.com/itm/ITEM_ID
核心选择器:
- 标题:
#itemTitle或.x-item-title__mainTitle - 价格区域:
.x-price-primary - 商品描述:iframe嵌套
#desc_ifr,需二次请求 - Item Specifics:
.x-about-this-item .ux-labels-values__content - 卖家信息区块:
.x-seller-info
卖家Profile页
URL格式:https://www.ebay.com/str/SELLER_NAME或https://www.ebay.com/usr/SELLER_NAME
关键字段:
- Feedback Score:
.feedback-score - Positive Rating:
.positive-feedback - 注册时间:
.member-since - 活跃类目:
.seller-store-category
代理选择:为什么eBay必须用住宅代理
eBay的反爬系统对数据中心IP极为敏感。实测数据:同一DC IP段连续请求3-5次搜索页后即触发CAPTCHA或返回空结果页。这不是猜测——eBay长期与反欺诈厂商合作,DC IP库标记非常精准。
代理类型对比
| 代理类型 | eBay兼容性 | 适用场景 |
|---|---|---|
| 数据中心代理 | 极差(秒封) | 仅用于API调用 |
| 住宅代理 | 优秀 | 大规模搜索与详情页抓取 |
| 移动代理 | 优秀 | 模拟移动端用户行为 |
对于eBay proxy策略,推荐配置:
- 大规模搜索结果采集:住宅代理 + 轮转模式(每次请求换IP)
- 详情页批量抓取:住宅代理 + sticky session(同一会话保持IP 5-10分钟)
- 区域站点采集:geo-targeted住宅代理,指定目标国家
区域化采集的必要性
eBay在不同区域站点(eBay.de、eBay.co.uk、eBay.com.au)展示的价格、运费、可见商品列表可能完全不同。一个德国买家看到的搜索结果和一个美国买家看到的截然不同。要获取区域真实数据,必须使用对应国家的住宅IP。
通过ProxyHat的geo-targeting功能,可以在用户名中指定国家和城市:
# 德国IP - 访问eBay.de获取区域定价
http://user-country-DE:password@gate.proxyhat.com:8080
# 英国伦敦IP - 访问eBay.co.uk
http://user-country-GB-city-london:password@gate.proxyhat.com:8080
# 美国纽约IP - 访问eBay.com
http://user-country-US-city-new_york:password@gate.proxyhat.com:8080实战:Python抓取eBay搜索结果
以下代码使用requests + BeautifulSoup,通过ProxyHat住宅代理抓取搜索结果并解析.s-item为结构化记录。
import requests
from bs4 import BeautifulSoup
import json
import time
import random
# ProxyHat residential proxy configuration
PROXY_URL = "http://user-country-US:YOUR_PASSWORD@gate.proxyhat.com:8080"
PROXIES = {"http": PROXY_URL, "https": PROXY_URL}
HEADERS = {
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/125.0.0.0 Safari/537.36"
),
"Accept-Language": "en-US,en;q=0.9",
"Accept": "text/html,application/xhtml+xml",
}
def fetch_ebay_search(keyword, max_pages=3):
"""Fetch eBay search results and parse .s-item cards."""
all_items = []
for page in range(1, max_pages + 1):
url = f"https://www.ebay.com/sch/i.html?_nkw={keyword}&_pgn={page}"
try:
resp = requests.get(url, headers=HEADERS, proxies=PROXIES, timeout=15)
resp.raise_for_status()
except requests.RequestException as e:
print(f"Page {page} failed: {e}")
continue
soup = BeautifulSoup(resp.text, "html.parser")
items = soup.select(".s-item")
for item in items:
title_el = item.select_one(".s-item__title")
price_el = item.select_one(".s-item__price")
link_el = item.select_one(".s-item__link")
shipping_el = item.select_one(".s-item__shipping")
bid_el = item.select_one(".s-item__bid-count")
time_el = item.select_one(".s-item__time-left")
bin_el = item.select_one(".s-item__purchase-option-with-icon")
record = {
"title": title_el.get_text(strip=True) if title_el else None,
"price": price_el.get_text(strip=True) if price_el else None,
"url": link_el["href"] if link_el and link_el.get("href") else None,
"shipping": shipping_el.get_text(strip=True) if shipping_el else None,
"bid_count": bid_el.get_text(strip=True) if bid_el else None,
"time_left": time_el.get_text(strip=True) if time_el else None,
"buy_it_now": bool(bin_el),
"is_auction": bool(bid_el),
}
# Skip ad placeholders ("Shop on eBay")
if record["title"] and "Shop on eBay" not in record["title"]:
all_items.append(record)
# Polite delay between pages
time.sleep(random.uniform(2, 4))
return all_items
if __name__ == "__main__":
results = fetch_ebay_search("vintage camera", max_pages=3)
print(json.dumps(results[:2], indent=2, ensure_ascii=False))
print(f"\nTotal items: {len(results)}")示例输出(截断):
[
{
"title": "Vintage Canon AE-1 35mm Film Camera SLR Body Only",
"price": "$89.99",
"url": "https://www.ebay.com/itm/1234567890",
"shipping": "+$12.30 shipping",
"bid_count": null,
"time_left": null,
"buy_it_now": true,
"is_auction": false
},
{
"title": "Nikon FM2 35mm Film Camera Black Body EXC+",
"price": "$185.00",
"url": "https://www.ebay.com/itm/0987654321",
"shipping": "Free shipping",
"bid_count": "12 bids",
"time_left": "1h 23m left",
"buy_it_now": false,
"is_auction": true
}
]
Total items: 144处理拍卖数据:倒计时、竞价次数与Buy It Now
拍卖商品是eBay区别于Amazon的核心特征,也是转售情报团队最关心的数据类型。抓取拍卖数据需要注意几个关键点:
时间敏感性与刷新策略
拍卖的.s-item__time-left字段返回的是相对时间字符串(如1h 23m left、6d 5h left)。详情页的倒计时更精确,但也是JavaScript动态渲染的。
对于即将结束的拍卖(<24小时),建议:
- 使用sticky session代理,避免频繁换IP触发验证
- 缩短刷新间隔至5-10分钟(而非搜索页的2-4秒)
- 记录每次抓取的时间戳,将相对时间换算为绝对截止时间
# Sticky session proxy for auction monitoring
# The session flag keeps the same IP for the duration
AUCTION_PROXY = "http://user-session-auction_monitor_1:YOUR_PASSWORD@gate.proxyhat.com:8080"
AUCTION_PROXIES = {"http": AUCTION_PROXY, "https": AUCTION_PROXY}
def parse_time_left(time_str):
"""Convert eBay time-left string to estimated minutes remaining."""
if not time_str:
return None
total_minutes = 0
import re
days = re.search(r'(\d+)d', time_str)
hours = re.search(r'(\d+)h', time_str)
mins = re.search(r'(\d+)m', time_str)
if days:
total_minutes += int(days.group(1)) * 1440
if hours:
total_minutes += int(hours.group(1)) * 60
if mins:
total_minutes += int(mins.group(1))
return total_minutes
def monitor_auction(item_url, check_interval=600, max_checks=60):
"""Monitor a single auction listing at regular intervals."""
import datetime
checks = []
for i in range(max_checks):
resp = requests.get(item_url, headers=HEADERS, proxies=AUCTION_PROXIES, timeout=15)
soup = BeautifulSoup(resp.text, "html.parser")
# Detail page selectors
price_el = soup.select_one(".x-price-primary")
bid_el = soup.select_one(".x-bid-count")
time_el = soup.select_one(".x-time-left")
snapshot = {
"timestamp": datetime.datetime.utcnow().isoformat(),
"price": price_el.get_text(strip=True) if price_el else None,
"bids": bid_el.get_text(strip=True) if bid_el else None,
"time_left_raw": time_el.get_text(strip=True) if time_el else None,
"time_left_minutes": parse_time_left(
time_el.get_text(strip=True) if time_el else None
),
}
checks.append(snapshot)
print(f"Check {i+1}: {snapshot['price']} | {snapshot['bids']} | {snapshot['time_left_raw']}")
if snapshot["time_left_minutes"] is not None and snapshot["time_left_minutes"] <= 0:
print("Auction ended.")
break
time.sleep(check_interval)
return checksBuy It Now标志的识别
在搜索结果中,.s-item__purchase-option-with-icon存在即表示有BIN选项。但注意:部分商品同时支持拍卖和BIN——即"或立购"。在详情页中,BIN价格和起拍价分别在不同元素中,需同时抓取两个价格字段做比较。
卖家分析:反馈评分、类目分布与跨平台关联
对于市场研究团队,卖家画像比单品数据更有战略价值。一个卖家的反馈趋势、主营类目变化、上架节奏,都能揭示供应链信号。
核心指标采集
- Feedback Score:累计正面反馈数,反映历史交易规模
- Positive Rate:近12个月正面评价百分比
- 注册时长:结合Feedback Score可算日均交易量
- 活跃类目:卖家店铺的类目树,识别主营业务
- 在架商品数:当前active listing数量
def scrape_seller_profile(seller_name):
"""Scrape seller profile page for analytics data."""
url = f"https://www.ebay.com/str/{seller_name}"
resp = requests.get(url, headers=HEADERS, proxies=PROXIES, timeout=15)
soup = BeautifulSoup(resp.text, "html.parser")
feedback_el = soup.select_one(".feedback-score")
positive_el = soup.select_one(".positive-feedback")
since_el = soup.select_one(".member-since")
# Active listings count from store page
count_el = soup.select_one(".srp-controls__count-heading")
# Store categories
categories = []
for cat in soup.select(".seller-store-category a"):
categories.append(cat.get_text(strip=True))
profile = {
"seller": seller_name,
"feedback_score": feedback_el.get_text(strip=True) if feedback_el else None,
"positive_rate": positive_el.get_text(strip=True) if positive_el else None,
"member_since": since_el.get_text(strip=True) if since_el else None,
"active_listings_count": count_el.get_text(strip=True) if count_el else None,
"store_categories": categories[:20], # Cap at 20
}
return profile
# Batch seller analysis
sellers = ["camera_vault", "vintage_watches_uk", "retro_tech_deals"]
for s in sellers:
data = scrape_seller_profile(s)
print(json.dumps(data, indent=2, ensure_ascii=False))
time.sleep(random.uniform(3, 6))跨平台关联与交叉分析
进阶用法:将eBay卖家数据与其他平台(如Amazon Seller Central、Etsy店铺)交叉对比,识别同一卖家在不同平台的定价策略差异。这需要:
- 从eBay卖家Profile中提取品牌名或店铺名
- 在其他平台搜索相同标识
- 对比同SKU的跨平台价格与库存状态
这类分析对套利发现和品牌保护团队非常有价值。
反爬对抗:eBay的防御体系与应对
eBay使用多层反爬技术:
- IP信誉库:数据中心IP段标记精准,住宅IP信誉度更高
- 请求频率检测:同一IP短时间内高频请求触发CAPTCHA(Arkose Labs / FunCaptcha)
- 浏览器指纹:TLS指纹、JS环境检测
- 行为分析:请求路径模式(只搜不点、无图片加载等)
应对策略:
- 使用住宅代理轮转,每次搜索请求换IP
- 模拟完整浏览行为:加载图片、随机点击详情页后再返回
- 控制请求速率:搜索页间隔2-4秒,详情页间隔3-6秒
- 使用真实浏览器UA和合理的Accept头
- 遇到CAPTCHA时立即停止该IP,切换新IP重试
法律与合规边界
抓取eBay数据前,务必了解:
- eBayrobots.txt:允许部分路径的爬取,但限制
/sch/等搜索路径的速率 - eBay用户协议:禁止自动化数据采集用于竞争目的,需评估法律风险
- GDPR/CCPA:涉及欧盟/加州用户数据时需合规处理
- 版权:商品图片和描述受版权保护,大规模复制需授权
建议:仅采集公开可见的结构化数据(价格、标题、卖家评分),不存储个人身份信息,数据仅用于内部分析而非再分发。
关键原则:抓取是为了获取市场洞察,而非复制eBay平台。保持克制,遵守速率限制,尊重robots.txt声明。
关键要点
- API优先,抓取补充:eBay Finding/Browse API适合结构化批量查询,但有5,000次/日调用上限和字段缺失;HTML抓取用于深度数据和实时拍卖。
- 必须使用住宅代理:eBay对数据中心IP封禁极快,住宅代理(尤其是geo-targeted)是大规模采集的前提。
- .s-item是搜索页核心:标题、价格、运费、拍卖信息、BIN标记都在这个容器内。
- 拍卖数据需sticky session:监控即将结束的拍卖时,保持同一IP避免触发验证。
- 卖家分析比单品更有价值:反馈评分、类目分布、上架节奏揭示供应链信号。
- 合规先行:遵守robots.txt、用户协议和隐私法规,数据仅用于内部分析。
准备好开始eBay数据采集?查看ProxyHat代理套餐,获取覆盖195+国家的住宅代理池,或了解网页抓取用例中的更多实战方案。需要SERP级别的结构化数据?参考我们的SERP追踪方案。






