数据抓取

监控一条抓取管线:那些在你的数据崩坏之前就告诉你它正在崩的指标

抓取器会无声地失败:cron 在跑,日志写着 200,数据却悄悄坏掉。那些在管线里、而不是在董事会里抓住它的指标与告警。

Chris Collins

Chris Collins

2026年8月4日 · 1 分钟阅读

一条抓取管线很少轰然倒下。cron 继续触发,进程继续以零退出,日志里满是 200 OK,一切看起来都很健康——直到三个团队之外的某人注意到,一份报告里的数字一周前就走偏了。到那时你已经丢了好几天的数据,而且大部分无法重新采集,因为那些页面早已翻篇。

这是”把采集跑在规模上”的可观测性那条腿。如果说 Kubernetes 给你编排逐响应的校验抓住单个坏页面,那么监控就是那一层——它随时间观察整支机队,并在腐坏抵达你的数据仓库之前,告诉你某个目标翻脸了。下面是那些要紧的指标、如何给它们建立基线、以及如何在不被噪声淹没的前提下告警。

日志不是监控

第一件要接受的事,是日志和退出码几乎不会告诉你抓取到底有没有在工作。一个以零退出、并记了一条 200 的进程,只告诉了你它发了一个请求、收到了一个响应,而不是它收到了正确的响应。正如检测被封或伪造的内容里所讲,一张封锁页、一具被剥离的空壳、和真实数据,回来时全都是 200。监控意味着指标:按请求发出的数字,随时间聚合,按维度拆分,并对着一条基线比较。那是一门不同于日志的学问,也正是那门抓住无声失败的学问。

那些要紧的指标

按请求发出这些,用目标主机和出口地理打标,并把它们聚合起来。这些没有一样需要机器学习,只要计数器和直方图。

成功率。 头号指标,也是人人都定义错的那个。成功不是 HTTP 200,而是通过了校验:响应里含着一个真页面永远拥有的字段或元素。按目标主机,追踪通过你内容断言的请求百分比。某个主机上的下跌、而别的保持平稳,就是那个主机在改变对你的行为,而它是你面板上最重要的数字。

封锁率。 你的检测层标记为软封锁或挑战的响应所占的比例。这是你的早期预警——某个目标变得更激进了,或你的池信誉滑坡了。一个爬升的封锁率,正是硬封禁的前奏,所以盯住它的斜率、而不只是它的数值,并把它和 IP 信誉以及你在设法避免的封锁关联起来。

延迟分位数。 追踪 p50、p95 和 p99,绝不用平均值——平均值会藏起那条真正问题所栖身的慢尾。一个上升的 p95 意味着某个目标在给你限速、某个池在劣化、或某条路由拥塞了。这是请求超时延迟调优背后同一信号的机队级视图。

字段填充率。 对你抽取的每个字段,实际被填充的记录百分比。这是你的数据质量金丝雀:一个昨天 98%、今天 20% 填充的字段,并不是变得更罕见了——是你开始拿到被剥离或不完整的页面了。填充率能抓住成功率可能漏掉的劣化。

吞吐对积压。 每分钟的请求数和记录数,对着你工作队列的深度来看。吞吐上升、积压缩小,是健康的。吞吐平坦、积压增长,意味着你落后了、需要横向扩容——正是那个喂给按队列深度自动扩缩的信号。

新鲜度。 每个来源最新那条记录有多旧。一个新鲜度不再往前走的来源,已经无声地停止产出了——哪怕其他每个指标看起来都没问题。这一个,能抓住一个没报错就死掉的数据流。

每条记录成本。 带宽与花费,除以采集到的可用记录数。除了效率之外,这是一个异常探测器:每条记录字节数的骤然跳升,往往意味着你在下载封锁页或臃肿的垃圾、而不是数据,所以一个健康问题会先以一个成本尖峰的形式出现。它也让带宽账单保持诚实。

重试率与错误分类学。 每条成功记录所需的尝试次数,按失败类型拆分:超时、429、连接重置、DNS、被检测到的封锁。你错误的形状就是一份诊断。429 的尖峰意味着你把某个目标推得太狠;连接重置的尖峰指向网络或代理层;被检测封锁的尖峰指向信誉。单个聚合的错误计数告诉你”有东西不对劲”;分类学告诉你”是什么不对劲”。

按目标建基线,按偏差告警

让监控变得无用的那个错误,是对着绝对阈值告警。3% 的封锁率对一个站点完全正常,对另一个则是五级火警。两秒的 p95 对一个重页面没问题,对一个轻量 API 则很糟糕。绝对阈值要么漏掉真问题、要么不停地喊狼来了,而告警疲劳意味着那一个真正的页面最终会被无视。

给每个指标按目标主机建立基线,然后对着偏离那条基线来告警:成功率的持续下跌、比正常带子爬得更快的封锁率、相较上周翻倍的 p95。变化率与按目标的偏差,抓住那些要紧的事,并在一个站点只是”与另一个不同”时保持安静。对着持续的偏差告警、而不是单独一个糟糕的分钟,并把”呼叫一个人”留给真正需要的事——其余一切可以是一个面板或一份摘要。

# 按响应发出;聚合进一条时间序列,按 host + geo 维度拆分。
def record(metrics, host, geo, resp, validation):
tags = {"host": host, "geo": geo}
metrics.incr("requests", tags)
metrics.incr("success" if validation.ok else "failure", tags)
if validation.blocked:
metrics.incr("blocked", tags) # 封锁率 = blocked / requests
metrics.observe("latency_ms", resp.elapsed_ms, tags) # 直方图 -> p50/p95/p99
metrics.observe("bytes", resp.size, tags) # -> 每条记录的成本/字节
for field, present in validation.fields.items():
metrics.incr(f"field.{field}." + ("filled" if present else "empty"), tags)

金丝雀与代理这一维

有两件事能让上面这一切更锋利。第一,跑金丝雀:按一个时间表、并按地理,抓取一个你已经知道其正确内容的页面,并断言它仍然吻合。一个金丝雀会在一个目标改变的那一刻从健康翻成损坏,无需等一个聚合指标的缓慢下滑变得显眼;而按地理去做,能抓住一个封掉某国出口 IP、却对另一国毫不动手的站点。

第二,把每个指标都按出口地理和池来拆维,而不只是按目标。问题常常是局部的:某个国家的 IP 被挑战,而其余顺畅通过;或一个池的某个片段劣化了。没有那份按地理和按池的拆分,它就会表现为全局平均值里一个温和而令人困惑的下陷,而不是它本该是的那个锐利、可行动的信号。因为池质量是封锁率的先行指标,按池观察成功率和封锁率,会在一个住宅池把整轮拖垮之前就告诉你它在劣化,并给你在用户察觉之前挪动负载或做故障切换的选择。

底线

一条不被监控的抓取管线,是一条无声且昂贵地失败的管线。把成功当作”通过校验”、而不是 HTTP 200 来度量,并追踪封锁率、延迟分位数、字段填充率、吞吐对积压、新鲜度、每条记录成本,以及一份像样的错误分类学——每一项都按目标主机和出口地理拆分。给每个指标按目标建立基线,并对着偏离那条基线、而不是对着绝对数字来告警,好让你抓住真正的断裂、而不被虚假的淹没。加上金丝雀,得到即时的早期预警。做到这些,那个无声的失败就不再无声:你会在管线里、在几分钟内知道,而不是在一份下游报告里、在几周之后。

因为封锁率和延迟二者都追溯到你出口所用 IP 的质量,一个干净的住宅池正是那个从一开始就让这些指标保持健康的东西,而按 GB 计价让你如今在盯着的”每条记录成本”,成了一件你真的能去优化的事。

准备好开始了吗?

试用 Shifter 住宅代理,205M+ 个 IP,195+ 个国家,低至 $0.75/GB。

立即开始