市场上的价格持续变动,单次浏览只能告诉你某样东西现在的价格。价格情报是将这个移动目标转化为可追踪的结构化数据的学科:从公开商品页面提取竞争对手和市场价格,将其规范化为干净的行,随时间存储,并分析趋势。本指南端到端地展示如何利用网络爬取实现价格情报,附带可直接运行并指向真实商品列表的 Python 代码。
为保持诚实,整个演示均以公开商品数据为范围:任何人无需登录即可看到的商品名称、价格、货币和列表 URL。不涉及用户账户、需要登录的页面、结账操作或个人数据。文末有一段关于服务条款的说明,并非套话,请在大规模运行前仔细阅读。
价格情报真正需要什么
很容易将其理解为"抓取一个价格"。实际上,一个有用的价格情报系统有四项工作,而爬取只是其中第一项。
- 采集来自你关注的公开页面的价格,可靠程度足以按计划定期运行。
- 规范化杂乱的原始值(货币符号、千位分隔符、"起"价)为干净的数字。
- 存储每条观测数据及时间戳,以便形成历史记录而非仅是快照。
- 分析历史数据:跨来源比较、计算平均值,并标记值得关注的价格变动。
这与任何 电商网络爬取 任务的问题结构相同。价格情报的不同之处在于价值来自时间序列,因此采集必须可重复,数据必须落到可供后续查询的地方。
为什么采集是难点
如果你用裸 HTTP 客户端请求主要市场的搜索页面,通常会得到两种令人失望的结果之一:响应体中几乎没有商品数据的 200 响应,或者直接被封锁。有两件事对你不利。许多市场在浏览器中通过 JavaScript 渲染其商品列表,因此初始 HTML 是一个空壳,要等脚本运行后才会填充数据。而且它们会迅速标记自动化流量:来自数据中心 IP 和非浏览器请求模式的访问,在到达渲染内容之前就会被挑战或封锁。
因此,可靠的采集在单次请求中需要两样东西:一个能渲染客户端页面的浏览器,以及一个平台认为是真实访客的 IP。你可以自己组建无头浏览器加 住宅代理 池,但保持这套设施健康运行才是大部分工作。Crawling API 将两者合并为一次调用。对于主要市场,它还内置了现成的解析器,可以完全跳过编写选择器的步骤。
Crawling API 返回任意 URL 的原始渲染 HTML,然后由你自行解析。Crawling API 和 Crawling API 的内置解析器则更进一步:对于 Amazon 和 eBay 等受支持的网站,它们直接返回干净的 JSON,无需维护 HTML 解析代码。本指南对支持的网站使用内置解析器,对不支持的目标回退到原始 HTML。
搭建项目
你需要安装 Python 3。创建一个目录,然后安装本教程使用的四个库:requests 用于 HTTP,price_parser 用于规范化货币字符串,pandas 用于分析步骤。
mkdir price-intelligence && cd price-intelligence python -m venv .venv && source .venv/bin/activate pip install requests price_parser pandas
你还需要一个 Crawlbase 账户和 API token,注册后可从控制台获取。新账户附带免费请求,因此你可以在做任何承诺之前测试以下所有内容。将 token 替换到 YOUR_CRAWLBASE_TOKEN 的位置。
从市场采集价格
从你想追踪的搜索页面开始。以手机为例,对同一关键词分别在 Amazon 和 eBay 搜索,可以得到两个可对比的竞争来源。由于两者都受 Crawling API 内置解析器支持,你只需传入 scraper 参数,就能获得结构化的商品 JSON,而无需处理 HTML。
import requests import urllib.parse API_TOKEN = "YOUR_CRAWLBASE_TOKEN" API_ENDPOINT = "https://api.crawlbase.com/" def collect(url, scraper, country="US"): params = { "token": API_TOKEN, "url": url, "scraper": scraper, "country": country, } resp = requests.get(API_ENDPOINT, params=params, timeout=90) resp.raise_for_status() return resp.json()["body"]["products"] def search_url(host, path, query): q = urllib.parse.quote_plus(query) return f"https://www.{host}/{path}{q}"
scraper 参数承担了核心工作:amazon-serp 和 ebay-serp 告知 API 返回解析后的商品列表而非原始标记。country 参数将请求路由到该地区的 IP,这很重要,因为价格和供货情况是按地区本地化的。一个小型封装函数现在可以驱动两个来源。
def collect_amazon(query, country="US"): url = search_url("amazon.com", "s?k=", query) return collect(url, "amazon-serp", country) def collect_ebay(query, country="US"): url = search_url("ebay.com", "sch/i.html?_nkw=", query) return collect(url, "ebay-serp", country)
每次调用返回一个商品字典列表。不同来源的字段结构不同(Amazon 给你 name 和扁平的 price 字符串;eBay 将当前价格嵌套在 price.current.to 下),这正是下一步存在的原因。
规范化为统一格式
原始价格数据几乎从不是分析就绪的。你会得到货币符号、千位分隔符、"起"价区间,以及每个来源不同的字段布局。在采集时就进行规范化,让下游看到的都是相同的列:来源、商品名称、数值价格、货币和列表 URL。在这里一次性规范化是让存储和分析代码保持简洁的原因。
from price_parser import Price def to_row(source, name, raw_price, url): parsed = Price.fromstring(raw_price or "") if parsed.amount is None: return None return { "source": source, "product": name.strip(), "price": float(parsed.amount), "currency": parsed.currency or "", "url": url, } def normalize(query, country="US"): rows = [] for item in collect_amazon(query, country): row = to_row("Amazon", item["name"], item.get("price"), item["url"]) if row: rows.append(row) for item in collect_ebay(query, country): raw = item["price"]["current"]["to"] row = to_row("eBay", item["title"], raw, item["url"]) if row: rows.append(row) return rows
price_parser 为你处理货币解析:它读取"£1,138.00"或"$709.00"并返回干净的金额和货币代码,因此价格比较任务永远不需要关心来源使用了哪个货币符号。经过这一步,所有观测数据无论来自何处都呈现相同的格式。
[ { "source": "Amazon", "product": "Apple iPhone 15 Pro Max 256GB", "price": 1138.0, "currency": "USD", "url": "https://www.amazon.com/dp/B0DGTJ6Y1S" }, { "source": "eBay", "product": "Apple iPhone 15 Pro Max 256GB Blue Titanium", "price": 709.0, "currency": "USD", "url": "https://www.ebay.com/itm/236096139018" } ]
价格情报的成败取决于可靠的采集。Crawling API 在一次调用中通过轮换住宅 IP 渲染客户端页面,对于主要市场其内置解析器直接返回干净的商品 JSON,省去无头浏览器集群和大部分解析代码。先在免费套餐中将其指向公开搜索页面试试。
为每次运行存储时间戳
单次规范化的列表只是一个快照。价格情报关注的是趋势,因此每次运行都必须带时间戳落入存储。带有附加 captured_at 列的平面 CSV 已经足够起步,后续可直接加载到 pandas 或电子表格中。
import csv, os from datetime import datetime, timezone FIELDS = ["captured_at", "source", "product", "price", "currency", "url"] def store(rows, path="price_history.csv"): stamp = datetime.now(timezone.utc).isoformat() new_file = not os.path.exists(path) with open(path, "a", newline="") as f: writer = csv.DictWriter(f, fieldnames=FIELDS) if new_file: writer.writeheader() for row in rows: writer.writerow({"captured_at": stamp, **row}) if __name__ == "__main__": rows = normalize("Apple iPhone 15 Pro Max 256GB", country="US") store(rows) print(f"stored {len(rows)} rows")
按计划运行此脚本(每隔几小时一次 cron 任务,或按套餐允许每小时一次),price_history.csv 就会增长为真正的时间序列。当平面文件不够用时,将相同的行写入数据库表即可;规范化后的格式意味着其他任何内容都不需要改变。如果你要跨多个商品和地区采集,异步 Crawler 允许你推送大批量 URL 并通过 webhook 接收结果,而非阻塞等待每次请求。
分析:比较来源并发现价格变动
有了存储的历史数据,分析就变得简短。将 CSV 加载到 pandas,按来源分组并比较。这是价格情报的经典问题:对于给定商品,哪里现在更便宜,便宜多少?
import pandas as pd df = pd.read_csv("price_history.csv", parse_dates=["captured_at"]) # Latest run only, for a head-to-head comparison latest = df[df["captured_at"] == df["captured_at"].max()] by_source = latest.groupby("source")["price"].agg(["mean", "min", "count"]).round(2) print(by_source) # Day-over-day move per source, from the stored history daily = df.set_index("captured_at").groupby("source")["price"] trend = daily.resample("D").mean().round(2) print(trend.pct_change().round(3))
第一个代码块告诉你今天谁更便宜;第二个将你存储的历史数据转化为每日趋势和百分比变化,这才是你真正据以行动的信号。超过某个阈值的跌幅可以触发告警;持续上涨则告诉你市场正在变动,你自己的定价也许需要调整。这里的所有内容都是普通的 pandas,因为困难的工作已在上游的采集和规范化步骤中完成了。
可选:在顶层叠加 AI
做价格情报不需要机器学习,但一旦你在规模化采集,有两个问题使用机器学习会更容易解决。
第一个是商品匹配。同一商品在每个网站的标题都不同("iPhone 15 Pro Max 256GB" vs "Apple iPhone 15 Pro Max (256 GB) Blue Titanium"),因此同类比较意味着需要聚合指向同一商品的列表。对标题进行向量嵌入并按相似度分组,远优于字符串匹配,这是真正的比较与噪声的区别。
第二个是异常检测。在足够长的历史数据上,大多数价格变动是正常的季节性漂移。一个简单的滚动统计(标记任何超过商品近期均值若干个标准差的观测值)可以在不盯仪表板的情况下捕获真正的事件,即突然的低价竞争或定价错误。先从这条规则开始;只在简单版本不够用时才引入模型。
大规模保持不被封锁
即使渲染和 IP 由 API 处理,以下几个习惯可以让定期采集任务保持健康,适用于任何难攻克的商业目标。
- 控制请求频率。 Crawling API 的默认速率对电商已经很慷慨,但在紧密循环中连续请求同一搜索仍然会引发限流。分散运行时间,变化你的查询参数。如果开始看到 429,这就是速率限制的信号。
- 善用轮换。 住宅代理 池将请求分散到多个真实用户 IP,使没有单一地址触发限制。API 为你处理这些;如果你自建方案,这是最需要做对的部分。如果你更喜欢标准代理端点集成,Smart AI Proxy 以同样的方式公开轮换。
- 关注状态码。 失败的抓取不计费,因此重试被封锁的抓取成本低廉。开始返回挑战的运行正在告诉你当前的层级已经不够用了。
完整指南请参阅 如何抓取网站而不被封锁。如果你的采集规模正从几个商品增长到跨地区的数千个 SKU,大规模电商爬取 介绍了适用于该体量的架构。
诚实的部分:服务条款与公开数据
爬取大型商业市场处于法律灰色地带,是否被允许取决于平台的服务条款、你的司法管辖区以及你对数据的用途。大多数市场条款限制自动访问,因此无论你的工具多么谨慎,采集行为都可能违反这些条款。本文的代码不会改变这一点,它只是让技术部分能够运行。
以下几条值得坚守。只采集公开数据:任何人无需账户即可看到的商品名称、价格、货币和列表 URL。遵守每个网站的 robots.txt 及其声明的速率预期,保持请求量低到不会给任何人的服务器造成压力。绝不采集个人数据,包括任何与个人卖家或买家账户相关的内容。如果你打算以商业目的重用数据,请获得许可或官方数据协议,而非假设沉默即是同意。本指南特意将范围限定在公开列表数据上,因为这是让工作具有可辩护性的界限。
核心要点
- 价格情报是四项任务,而非一项。 采集、规范化、带时间戳存储,然后分析。爬取只是第一步。
- 可靠的采集需要渲染和可信 IP。 Crawling API 在一次调用中同时完成这两项,其内置解析器对受支持的市场返回干净的 JSON,无需维护 HTML 解析代码。
- 在采集时规范化。 将货币字符串一次性解析为数字,转为统一格式,存储和分析步骤就能保持简洁。
-
价值在时间序列中。 每次运行附上
captured_at时间戳,这样你才能读取趋势和日环比变化,而非仅仅是快照。 - AI 是可选的精化。 向量嵌入有助于跨网站匹配同一商品;滚动统计规则可以标记真正的价格异常。只有在简单版本无法扩展时才引入它们。
- 坚守公开数据。 遵守服务条款和 robots.txt;不涉及账户,不采集个人数据。
常见问题
什么是用于价格情报的网络爬取?
这是一种从公开商品页面自动采集价格、将其规范化为干净数字、并随时间追踪以便比较竞争对手和发现市场变动的实践。爬取收集原始观测数据;情报来自存储时间序列并分析趋势,而非仅读取单次快照。
采集价格必须解析 HTML 吗?
对于主要市场来说不必要。Crawling API 的内置解析器(以及 Scraper API)对 Amazon 和 eBay 等受支持网站返回解析后的商品 JSON,完全跳过选择器。只有当目标网站不在支持列表中时,才需要回退到解析原始 HTML,此时 API 仍会提供渲染完成的页面供你使用。
我应该多频繁地采集价格?
取决于你的市场变动速度和请求预算。对大多数商品目录来说每小时已经足够;快速变动的品类可能需要更高频率,缓慢变动的则可以更低。无论频率如何,每次运行都应附上时间戳以建立真实的历史记录。控制请求速率并变化查询参数,避免定期任务看起来像突发攻击。
如何比较不同网站上的同一商品?
各市场的标题不同,因此精确字符串匹配会失效。在采集时将每条列表规范化为相同的字段,然后按相似度而非完全一致的文本匹配商品。对于少量 SKU,手动映射可行;规模化时,对标题进行向量嵌入并按相似度聚类是可靠的方法。
大规模采集价格会被封锁吗?
如果你从单一 IP 发送爬虫特征明显的流量,可能会。保持每个 IP 的请求速率低,变化搜索参数,并通过轮换住宅 IP 路由请求,使没有单一地址触发限制。Crawling API 和 Smart AI Proxy 为你管理轮换和可信 IP 池;如果你自建方案,这是值得投入的部分。失败的请求不计费,因此重试被封锁的抓取成本低廉。
用于价格情报的价格爬取合法吗?
取决于目标网站的服务条款、你的司法管辖区以及你的用途,大多数市场条款限制自动访问。严格限于公开列表数据(商品名称、价格、货币、URL),遵守 robots.txt 和速率预期,绝不涉及账户或个人数据。商业用途的数据重用应获得许可或官方数据协议,而非依赖爬虫。
大规模爬取任何站点,无需与基础设施对抗。
Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。

