打开网易新闻 查看精彩图片

凌晨3点,你的冲浪预警系统突然哑火。某个海浪数据源的CSS选择器失效,用户收不到警报,工程师被Slack叫醒——这种剧本每月重演。修复代码只要5分钟,但找问题、拉日志、走部署流程,2小时没了。

这不是算力不够,是摩擦成本太高。一个团队给这套流程起了个名字:Self-Healer。它的核心洞察很朴素——修爬虫这件事,本身就该自动化

选择器是定时炸弹,只是你不知道倒计时

生产环境的爬虫有个隐蔽的脆弱点:它们依赖的CSS选择器和XPath表达式,是针对第三方网站某一时刻的DOM快照写的。对方改个布局、换个类名、重构表格结构,你的选择器要么返回空,要么返回错的数据。

更麻烦的是这类故障的沉默性。系统不会报错崩溃,只是 quietly 返回 nil。等到用户投诉或数据异常被察觉,故障已经持续数小时。

文中提到的冲浪预警系统要监控几十个预报源。一个真实案例:上游网站把 tr.forecast-table__row[data-row-name="wave-heigth"] 里的拼写错误修正了——原来他们把 height 写成 heigth,现在改对了。你的选择器反而失效。

这种"对方修bug你踩雷"的场景,在依赖第三方数据的系统里几乎无法避免。

Self-Healer的闭环:从故障到修复,无人值守

Self-Healer的闭环:从故障到修复,无人值守

系统的触发点很直接:Ruby 爬虫提取字段失败(比如 wave_height 返回 nil),就往 Redis 队列扔一个修复任务。

Python sidecar 消费这个任务,拉取当前 HTML,裁剪到符合 token 预算,然后向本地运行的 MLX 大模型发送定向提示词。模型输出新的 CSS/XPath 候选选择器,附带置信度和推理过程。

关键的安全闸在这里:每个候选选择器都要经过 BeautifulSoup 和 lxml 的实机测试。提取出的值还要过类型校验——比如浪高必须是 0.1-20.0 之间的浮点数。通不过就扔掉,通过就直接写入 PostgreSQL 的 data_sources.selector_overrides 表。

不需要重新部署。下次爬虫运行读取更新后的配置,当作什么都没发生继续跑。

整个闭环的设计哲学是:模型负责提议,代码负责裁决。LLM 的输出被当作"实习生提交的 PR"——值得一看,但合并前必须 review。

为什么必须本地跑模型,为什么还要类型校验

为什么必须本地跑模型,为什么还要类型校验

用 LLM 修爬虫听起来诱人,但有两个坑不得不防。

第一,模型的自信不等于正确。它能生成看起来合理的 XPath,实际匹配的却是表格的错误列——比如提取到表头文字"Wave Height (m)",而不是数值 1.8。类型校验在这里就是最后一道防线:字符串过不了 float:0.1-20.0,候选直接出局。

第二,没有沙箱环境,没有影子流量,没有 A/B 灰度。候选选择器的验证就在进程内完成,用和爬虫相同的解析库,对同一份 fetched HTML 跑测试。能过就生效,不能过就丢弃。这种"现场验货"的粗暴,反而规避了环境不一致带来的风险。

选择器覆盖表的设计也值得玩味。它不是修改代码仓库,而是运行时配置层的热更新。这让修复动作本身变得可观测、可回滚——出问题了直接删表里的记录,系统回退到原始选择器。

文中没提但隐含的一个细节:这套系统对 LLM 的调用成本极低。MLX 本地运行,没有 API 费用;HTML 裁剪控制 token 量;候选选择器通常 1-3 个就能命中。对比工程师 2 小时的夜间加班,硬件折旧几乎可以忽略。

Self-Healer 目前处理的是结构化数据的提取故障——表格、列表、特定字段。对于更复杂的场景,比如需要跨页面跳转、处理动态渲染、或者理解语义上下文的抓取任务,这套架构还够用吗?如果上游网站把关键数据从 HTML 搬到 JavaScript 渲染,甚至加上反爬验证,sidecar 的修复策略要怎么升级?