大规模采集电商价格而不被封禁的实践方案
如何搭建一条能产出「可比数据」的价格监控管道:站点建模、观测点固定、采集节奏、成本分级,以及大多数团队会漏记的上下文字段。
价格监控项目很少因为爬虫坏了而失败。它们失败,是因为半年后有人问「竞品三月份是不是真的涨价了」,却没人答得上来——因为三月份的数据是从另一个国家、在另一个时点、在一场没人记录的促销期间采集的。
把数据拿到手是容易的那一半。让数据在一个季度之后依然有意义,才是值得设计的部分。
价格是一次测量,不是一个事实
同一个商品页面,会因为请求来自哪里、设备声称是什么、会话看起来是否像回访用户、当前有哪些促销,甚至仅仅因为现在几点,而显示不同的数字。
这意味着抓到的价格是「在某组条件下完成的一次测量」。如果你不记录条件,就无法比较两次测量。大多数管道只存 (sku, price, timestamp),然后在一年半之后发现整条序列无法解读。
最低限度的记录应该更接近这样:
{
"sku": "B08N5WRWNW",
"retailer": "example-marketplace",
"storefront": "DE",
"observed_at": "2026-04-09T06:00:00Z",
"exit_country": "DE",
"exit_city": "Munich",
"device_class": "desktop",
"session_state": "logged_out",
"currency": "EUR",
"price": 249.99,
"price_includes_tax": true,
"shipping_estimate": 0.0,
"promotion_label": "Spring deal",
"seller": "Example Retail GmbH",
"in_stock": true,
"raw_html_key": "s3://price-raw/2026/04/09/de/B08N5WRWNW.html.gz"
}这里的每一个字段,都曾经让某个人的分析付出过代价。
建模的单位是站点,不是商品
采集的基本单位不是 SKU,而是「零售商 × 国家」这一对——也就是一个国家站点。同一个平台的德国站和奥地利站是两份不同的数据集:卖家不同、配送承诺不同,有时商品结构也不同。
这直接决定了代理配置:德国站点必须用德国出口采集。用美国机房 IP 去采,要么被重定向到美国站,要么拿到一个默认版本,而两种情况下解析器都会心安理得地记下错误的数字。
用带国家定位的住宅代理按站点固定出口国家;对于国内价格存在地区差异的品类,连城市也一起固定。
固定观测点
写代码之前,先为每个站点写下观测点,并把它当作常量:
- 请求发起的国家与城市
- 设备类型——桌面还是移动,两者经常不同
- 会话状态——未登录是唯一可复现的选项;登录态会累积个性化
- 币种与区域设置请求头
然后保持全部不变。如果要改观测点,就把它当作一条新序列,而不是旧序列的延续。年中悄悄把出口从法兰克福换成柏林,正是那种会制造出「从未发生过的价格变动」的操作。
节奏比频率更重要
团队常问应该多久采集一次。更有用的问题是:多规律地采集。
带缺口的小时级序列,对趋势分析而言比干净的日级序列更糟。选一个你的基础设施能长期维持的间隔,然后一直维持:
- 每小时:波动大的品类,如促销期的电子产品、旅游,以及对方有主动调价算法的任何品类。
- 每六小时:一般零售。
- 每天:稳定品类,如家具、工业耗材、图书。
每天在相同的挂钟时间采集,使用固定时区,并以 UTC 存储时间戳。零售商会按计划批量更新价格;在一个漂移的时间点采样,会与这些批次产生混叠,制造出看起来像波动的伪影。
用分级出口控制成本
上了规模之后,价格监控会搬运大量字节。一个商品页通常在 150–400 KB,5 万个 SKU 每天采 6 次,大约是每天 45 到 120 GB。
把这些全部走住宅代理,是默认会犯的错误。有效的模式是:
- 发现阶段走数据中心。 分类页、sitemap 和搜索结果页的防护通常弱于商品详情页,用数据中心代理低成本枚举。
- 详情页走住宅代理,出口固定为站点所在国家。
- 只在分类判定为失败时才升级。 验证页返回的是 HTTP 200,必须检查响应体。可用的分类器见 Python 轮换代理完整设置指南。
- 每网关每天超过约 120 GB 时切换到无限流量。 超过这个量级,按天固定计费的无限流量住宅网关比按量计费更便宜,账单也不再随负载波动。
前两层之间的取舍,在住宅代理与数据中心代理该怎么选里有更详细的讨论。
保留原始载荷
把压缩后的 HTML 或 JSON 响应与解析结果一起存储。它大约会让存储成本变成三倍,而每一次它都值这个钱。
零售商会在不打招呼的情况下改版页面结构。当某个选择器失效时——它一定会失效——你有两个选择:有原始载荷,你用一个下午重新解析归档、把历史找回来;没有原始载荷,唯一的选择是重新采集已经不存在的数据,因为昨天的价格今天花多少钱都买不到。
压缩后的 HTML 大约每页 30–50 KB。每天 30 万个页面约 12 GB,对象存储每月成本不过几美元。拿它和「丢掉一个季度的价格历史」比一比。
防御式解析
几个能避免数据静默损坏的习惯:
不要相信裸数字。 从页面里解析币种,而不是从配置文件里读。平台确实会返回意料之外的币种。
与上一次观测做校验。 一夜之间变动超过 60% 的价格,更可能是解析错误而不是真实变化。标记待审,而不是直接写入。
区分「缺失」与「零」。 缺货商品是没有价格,不是价格为零。混为一谈会毁掉之后所有的均值计算。
记录卖家。 平台上的黄金购物车得主会轮换,价格变化往往意味着换了一个卖家,而不是同一个卖家在调价。
检查截断。 一个平时返回 48 条结果的列表页只返回 12 条,那是部分响应,不是品类在缩水。
尊重目标站
除了道德层面,克制也是成本最低的防封手段:
- 阅读并遵守你所采集路径的
robots.txt。 - 按主机名限制并发。从每秒 4 个请求起步,观察目标站自身的延迟;如果你加压时延迟上升,说明已经超过了它的舒适区间。
- 只采集可公开访问的页面。任何登录之后的内容都属于别人的账号。
- 遇到 429 和 503 立即退避,而不是继续往墙上撞。
- 如果站点提供了联系渠道,就如实表明你的爬虫身份。有些零售商会直接给你一份数据源,比抓取更快也更便宜。
对变化量告警,而不是对绝对值
最后一环是你拿这条序列做什么。对绝对价格水平告警只会制造噪音——竞品单纯比你便宜,这不算新闻。
要对变化告警:窗口内超过阈值的变动、购物车卖家更替、库存状态切换、促销出现或消失。这些才是会触发决策的事件,其余的应该留在没人需要盯着看的看板里。
而且无论什么告警,都要把观测点带上。「价格下降 14%」是一句不完整的话;「德国站、桌面端、未登录、UTC 06:00 观测,价格下降 14%,并出现新的促销标签」才是定价经理能据此行动的信息。
多久重采一次
不是每个 SKU 都值得同一频率。消费电子、时装发售、快消促销需要一天内多次观测;大家电、配件每天或每周两次就够。全局一个间隔,要么把流量浪费在从不变动的页面上,要么错过竞品真正调价的那一小时。
可落地的拆分:近七天产生过告警、以及你直接对打的 5–10% SKU 作为热集,一到四小时采一次;其余关心的店面作为温集,每天错峰采;长尾冷集每周两到三次。有告警就升级到热集,安静一周再降回去。这一条通常能少 30–40% 流量,同时不丢掉定价团队真正会行动的事件。
重采时必须沿用上次的观测点:同一国家、同一设备类型、同一登录状态。把移动端已登录的德国价混进桌面未登录序列,会看起来像一次从未发生过的 12% 波动。
第一次请求往往会按出口 IP 种一条区域 Cookie。如果随后跟上为另一个国家生成的 URL,Cookie 和路径会对不上,结果是跳转循环或错误语言页。要么每次观测都清空 Cookie、让出口国家决定区域;要么把 Cookie 罐和粘性会话绑在同一次店面访问里,访问结束一起丢掉。记录你实际落到的区域,而不是你打算落到的区域。
关于作者
UUIProxy 数据工程负责人
加入 UUIProxy 之前,Daniel 为一家零售集团在九个市场负责价格情报业务。现在他大部分时间在帮助团队重构那些「成本增速快过数据量增速」的爬虫——办法通常是给出口分级、对响应做分类,而不是简单地买更多住宅流量。