如何抓取eBay列表和已售数据进行定价研究

eBay的已售列表历史是最接近于自由市场价格数据库的东西——如果你知道解锁它的两个URL参数。以下是如何大规模抓取列表、变体和真实交易价格的方法。

eBay是互联网上最大的点对点市场之一,使其成为定价研究、竞争对手跟踪和转售估值的宝库。它有一个官方开发者SDK,但最有用的数据——商品实际售出的价格——最容易直接从公共搜索页面收集。本指南展示了如何抓取eBay列表、变体数据和真实已售价格,以及保持抓取器在前几百个请求后仍然活跃的代理设置。

搜索URL就是API

当你搜索eBay时,查询完全编码在URL中,少数参数控制返回的内容。掌握这五个参数,你就可以在不触碰浏览器的情况下编程驱动eBay的目录:

排序参数_sop值得一提:其值按最新上架、包括运费在内的最低总价或即将结束排序结果,这让你可以定制目标信息流——最新列表用于变更监控,最便宜的总价用于寻找优惠。使用_sacat限制可以将广泛的关键词缩小到正确的部门,在解析单行之前去除噪音。关键词、类别和排序结合在一起,让你可以将大多数研究查询表达为一个URL,这正是使eBay自动化愉快的原因。

import requests
from urllib.parse import urlencode

proxy = "http://USER:PASS@gate.quantumproxies.io:8000"
proxies = {"http": proxy, "https": proxy}

def search_url(keyword, page=1, per_page=60, category=0):
    params = {"_nkw": keyword, "_sacat": category,
              "_ipg": per_page, "_pgn": page}
    return "https://www.ebay.com/sch/i.html?" + urlencode(params)

r = requests.get(search_url("vintage seiko watch"),
                 proxies=proxies, timeout=20)
print(r.status_code)  # parse r.text with your HTML library of choice
eBay抓取管道示意图:从参数构建搜索URL,通过住宅代理获取,解析列表并存储行
eBay的公共搜索页面表现得像一个API——参数是查询,标记是响应。

已售和完成的列表:真实市场价格

这是对定价最重要的端点。活动列表只告诉你卖家希望得到的价格;很多从未售出。eBay通过两个额外参数在同一搜索页面上公开实际交易历史:LH_Complete=1显示所有完成的列表,LH_Sold=1缩小到真正售出的商品。在已售列表中,价格是最终销售价值,而不是要价——这使其成为最干净的免费来源,显示物品的实际价值:

def sold_url(keyword, page=1):
    # append the completed + sold filters to any search
    return ("https://www.ebay.com/sch/i.html?"
            f"_nkw={keyword.replace(' ', '+')}"
            "&LH_Complete=1&LH_Sold=1"
            f"&_ipg=60&_pgn={page}")

# every sold row = a real transaction: use median, not mean,
# to shrug off outliers and shill bids
r = requests.get(sold_url("nike dunk low panda 10"),
                 proxies=proxies, timeout=20)

因为已售页面使用与标准搜索相同的标记,所以你的列表解析器无需更改——只需附加两个过滤器。取最后30-60个已售价格的中位数而不是平均值,你就能得到一个稳健的市场价值,转售和竞争对手价格监控工作流程可以信任。

解析列表:价格、细节和变体

单一变体列表很简单:价格位于主要价格元素中,项目细节表(条件、品牌、型号、尺寸)是一组标签/值对,描述位于你可以单独获取的iframe中。注意eBay的区域价格转换——某些地区会显示转换后的价格和原价,因此要捕获两者。

多变体产品(如具有型号、存储和颜色选项的手机)更棘手,因为每个选项的价格由JavaScript更新。该数据不在可见的HTML中——它是隐藏的网页数据,存储在名为MSKU的JavaScript变量中,位于<script>标签内。提取该JSON,你就能在不渲染页面的情况下,一次性获取每个变体的价格和库存:

import re, json

def extract_msku(html):
    # the variant matrix lives in a JS object keyed 'MSKU'
    m = re.search(r'"MSKU"\s*:\s*(\{.*?\})\s*[,}]', html, re.S)
    if not m:
        return {}          # single-variant listing, no matrix
    data = json.loads(m.group(1))
    # data now holds per-variant price, currency and stock flags
    return data

variants = extract_msku(r.text)
比较显示活跃的eBay列表作为要价与已售列表作为真实最终交易价格
为了估值,总是抓取已售过滤器:活跃价格是愿望,已售价格是市场。

大规模保持不被封锁

一个列表是微不足道的。规模是eBay反击的地方——通过速率限制、CAPTCHA挑战和标记自动化模式的动态内容。数据中心IP在市场上很快就会被限制;可靠的解决方案是住宅代理,它们表现得像普通购物者,轮换使用,以免单个IP积累明显的突发。调整请求速度,尊重任何429并进行退避,当你需要特定地区的定价时本地化出口,因为eBay根据地理位置调整结果和货币。

当目标增加更重的机器人管理或你不想自己维护解析和轮换堆栈时,Scraper API在一个端点后处理IP轮换、JS渲染和重试,并返回干净的HTML、JSON或markdown。对于更广泛的市场攻略,我们的指南跨Amazon、eBay和Etsy的市场自动化涵盖了账户安全和地理定位。

使用干净的住宅代理抓取eBay

从抓取到数据集

一旦行流动起来,纪律就是去重和历史。用eBay项目ID键入每条记录,这样重新抓取时会更新现有行而不是重复,并在每次提取时存储价格、时间戳和项目条件——已售价格只有在其日期和项目是新还是旧的情况下才有意义。为每个产品保留一个最近已售数据的滚动窗口,你就拥有一个自我刷新活的估值,这是任何转售、重新定价或套利工具的基础。将其与突然价格变动的警报配对,数据集就开始为你监控。

抓取eBay是否合法?

这是一般信息,不是法律建议。以尊重的速度收集公开可用的列表数据——价格、条件、已售历史——被广泛视为合法。需要注意的界限是个人数据:卖家姓名、位置和联系方式在欧盟的GDPR和其他地方的类似法律下受到保护,因此在没有合法依据的情况下避免存储它们。抓取市场信号,而不是人。

常见问题解答

如何抓取eBay已售列表?

获取任何eBay搜索URL并附加LH_Complete=1&LH_Sold=1。这将结果切换为实际售出的完成列表,其中显示的价格是最终交易价值。页面使用与正常搜索相同的标记,因此相同的解析器提取标题、价格和销售日期以进行定价研究。

我可以在没有官方API的情况下抓取eBay价格吗?

可以。公共搜索和列表页面返回买家看到的所有内容,没有配额或OAuth。搜索参数(_nkw_sacat_pgn_ipg)驱动查询和分页,产品页面携带价格、项目细节和变体数据。你需要旋转住宅代理以在不被速率限制的情况下维持流量。

如何获取eBay变体价格?

多选项列表将其每个变体的价格和库存存储在嵌入在脚本标签中的名为MSKU的JavaScript对象中,而不是在可见HTML中。提取该JSON块,你就能在不渲染页面或点击每个选项的情况下获取每种组合的价格——比驱动浏览器更快更可靠。

为什么我的eBay抓取器被封锁?

eBay对看起来自动化的流量应用速率限制和CAPTCHA挑战,并且数据中心IP范围很快就会被限制。将请求分散到旋转的住宅IP上,使用延迟调整它们,在任何429上退避,并保持头信息一致。如果挑战持续存在,旋转IP和渲染JavaScript的Scraper API可以消除维护负担。

像API一样抓取搜索页面,用两个参数解锁已售历史,从嵌入的JSON中挖掘变体矩阵,并通过干净的住宅IP路由这一切。做到这一点,eBay就成为你控制的实时定价数据集。

在不管理堆栈的情况下提取eBay数据