昨天还正常的管道,今天早上却空了
如果是内部数据源,这种事不太容易发生。要改 schema 会先协商,删除列之前会有预告,出了问题也有明确的负责人。但像竞品价格、评论、市场数据这类来自外部网站的数据源则不同。
某一天,目标网站把评论区域改成 React,调整价格标注的位置,或隐藏原本登录后可见的项目。没有任何预告。那一刻,昨天还运行正常的解析开始返回空值,dbt 模型照常让它通过,仪表盘上昨天的数据留下空洞。业务团队会问“为什么数据对不上”,数据团队则在凌晨修解析器。
本文将从数据契约(Data Contract)的角度梳理:为什么这种情况会反复发生,以及应当如何以“必然会损坏”为前提设计责任。
为什么唯独网站数据总是出问题
数据契约是数据源与消费者之间的一项明确约定:“这份数据会以这样的结构、含义和频率提供。”它不只是简单的 schema 定义,还包括发生变更时谁该在何时做什么。
在内部系统中,这份契约作为组织规范发挥作用。数据源团队要修改 schema 时需要通知,删除列之前会保留 deprecation 期,发生故障时组织架构中也有负责修复的人。关键在于,数据源是可以协作的主体:可以沟通、可以协调日程,也可以追究责任。
外部网站数据之所以困难,是因为这一前提不成立。目标网站不是我们契约的当事方。它不会顾及我们的管道情况,不会发送通知,我们也无法要求它回滚。而且,网页界面本来就不是为管道输入而设计的。我们是在将为人类浏览而打造的界面反向解析为数据,因此以下特性会动摇契约。
- 毫无预告的改版:营销改版、框架替换会让 DOM 结构和渲染方式(SSR→CSR)在一夜之间发生变化。
- A/B 测试与按条件展示的页面:同一 URL 每次访问可能返回不同页面,展示内容也会因地区、登录状态、会员等级和设备而不同。如果不一并记录数据是在什么条件下看到的,同一个字段的值含义就会悄然改变。
- 因反爬与封锁导致的部分缺失:遇到封锁、验证码或限流时,并不是所有数据都无法获取,而是只缺少一部分。这类“看似正常但被缩减的数据”会让下游悄然产出错误的汇总结果。
共同点在于:内部数据源所假设的 schema 稳定性,在外部网站上并不成立。因此,外部网站的数据契约不应承诺“不会损坏”,而应围绕发生损坏时由谁负责检测、修复和重新采集来设计。
为什么一次小小的改版会成为整个组织的问题
一个网站的小改版会向下游扩散。当解析规则与实际 DOM 不一致时,采集阶段会出现字段缺失或值被变形;这些问题未能被加载表拦截,进入 dbt 转换后产生静默的 NULL 传播和错误汇总,最终导致仪表盘出现空洞与错误决策。
有两个因素会扩大爆炸半径。第一是 静默失败。如果解析抛出异常并停止,反而还好。危险的是它将空字符串或 NULL 当作正常值返回,下游也原样通过,最终管道亮着绿灯,数字却是错的。第二是 字段契约定义得过宽。如果下游模型和仪表盘直接引用所有采集字段,那么网站只改动一个元素,所有依赖它的产出都会一起受到影响。
缓冲设计——即使损坏,下游也不会崩溃
无法消除外部网站的不稳定性。但可以在中间设置缓冲层,防止不稳定性蔓延到下游。
保留 raw 数据。 将采集到的原始内容(HTML 快照、原始响应、OCR 前图片等)与解析结果分开保留。一旦发现解析有误,只要原始内容还在,就可以无需重新采集而通过重新解析恢复历史数据。丢弃原始内容,就意味着解析 bug 会直接变成永久性数据损失。保留 raw 数据是重新采集、审计和 lineage 的共同基础。
最小化字段契约。 只保留真正被下游依赖的核心字段,并以较窄范围进行契约化。网站经常修改的附属元素不纳入契约,只保留在 raw 数据中。对于契约化的核心字段,验证其类型、是否必填和取值范围;一旦违规,就不让其流入下游。契约越窄,改版的爆炸半径也越小。
变更检测。 只看“解析是否抛出异常”,会遗漏静默失败。因此应当监控结果本身的形态:记录数是否相较平时剧烈变化、核心字段缺失率是否突然上升、值分布是否超出正常范围(例如价格为 0 或异常偏高)。只要命中其中一项,就通知数据团队频道。目标是在人员发现改版之前,先通过数据形态的变化捕捉到问题。
在这一缓冲层之上,我们与数据团队的协作方式如下。schema 从一开始就与数据团队共同定义:确定哪些字段属于核心契约,统一每个字段的类型、是否允许 NULL 及其语义,并让 dbt 和仪表盘等下游引用这份契约。即使需要变更,也不会单方面替换 schema。我们会提前通知变更、保留 deprecation 期,并在此期间同时提供旧版与新版,为下游迁移留出时间,然后再下线旧版本。这样一来,即便网站发生变化,下游也只需在预先通知的窗口内完成迁移,改版不会直接导致仪表盘出现空洞。
以损坏为前提的责任设计
仅有技术缓冲还不够。真正让实际工作陷入混乱的,是契约中没有规定损坏后该由谁修复。因此,数据契约中除了 schema,还应明确检测、修复和重新采集的所有者。
| 所有权 | 应在契约中明确的内容 |
|---|---|
| 检测 | 由谁运营异常信号、通过哪个渠道通知、恢复目标时间截至何时。检测主体不应是消费者,而应是负责运行采集的一方 |
| 修复 | 因网站改版导致解析器损坏时,由谁负责修复规则。若已委托,则由受托方负责 |
| 重新采集(backfill) | 是否存在填补损坏期间数据空洞的标准流程 |
将修复责任置于受托方,是委托模式的核心价值。这样,反爬应对、网站变更追踪、解析器修复等持续性维护工作就不会由数据团队负责 DAG 运行的人员承担;数据团队可以专注于数据加载、建模和使用。
重新采集不应作为例外,而应被当作标准流程处理。按指定期间和条件进行回填,以幂等加载为前提,确保同一个键重复加载多次也不会累积重复数据;再通过 watermark 和增量游标,准确填补需要回填的区间。同时,一并交付可直接用于加载后验证(DQ)、审计和 lineage 的元数据。采集时间与采集条件(地区、登录状态)是解释值语义的依据;源 URL 与采集周期可用于 catalog 和 lineage 登记;记录数、缺失/异常标记则可进入自动验证门禁。此外,还需留下完成标记(manifest/_SUCCESS),使下游能够判断加载是否完整结束,防止编排系统在部分加载的数据之上运行转换任务。只有这样,面对审计时“这个数字是从哪里来的”的问题,才能给出答案。
总而言之,外部网站的数据契约不是“不会损坏”的承诺,而是“损坏后由谁负责检测、修复和重新采集,并在何时之前恢复”的承诺。只有将这一所有权写入契约,才能像管理良好的内部数据源系统一样管理外部网站数据源。
总结
外部网站数据不断损坏的根本原因在于:与内部数据源不同,数据源不是能够协作的契约当事方。毫无预告的改版、A/B 测试和按条件展示的页面,破坏了 schema 稳定性的前提。因此,外部网站的数据契约不应是“不会损坏”,而应明确以损坏为前提的检测、修复和重新采集所有者。
Hashscraper 正是以这种方式与数据团队合作。我们共同定义 schema,通过提前通知与版本并行完成变更交接,以基于幂等加载和 watermark 的标准流程处理重新采集,并一同交付可直接用于 catalog、lineage 和 DQ 的元数据及完成标记。开发、维护、反爬应对和采集监控均包含在月度固定费用委托中,因此负责 DAG 运行的人员无需被外部采集维护工作消耗。就规模而言,我们也拥有将某监管机构的大规模在线监测作为定期管道进行委托运营的经验。
将外部网站的采集与维护委托出去,像管理良好的数据源系统一样运营;数据团队则专注于加载、建模和应用——这就是在网站改版频繁的时代,稳定地将外部数据呈现在仪表盘上的现实路径。
推荐阅读
- 从爬取数据到成为决策依据
- 接收 Excel 数据 vs 在仪表盘查看数据——交付形式改变的是什么
- 各部门各自爬取数据的公司——终结重复投入的全公司数据采集治理
立即开始
对于当前接入管道的外部网站数据源,如果不希望一次改版就让仪表盘变空,我们可以与您一起检查需要什么样的契约。从 schema 定义到变更通知、重新采集责任和交付规范,均可根据数据团队的管道进行设计。




