直接回答:增量的核心是三件事——判断哪些内容变了、只重算变化影响到的部分、单独处理内容消失的情况。做到这三点,采集任务才可能长期稳定运行;继续全量重跑,时间、调用成本和目标站点的压力都会迅速失控。

全量重跑为什么走不远

原型阶段全量重跑没问题:站点少、内容少、跑一次几分钟。但三个成本会随规模放大:

成本 表现形式
时间 线性增长,最终超过周期本身
计算与调用 向量化与模型调用的费用成倍上升
外部压力 对目标站点反复请求,容易被限速甚至封禁

第二项最容易被低估。向量化是按量计费的,如果每周把几千篇文章重新算一遍向量,而其中九成内容没有任何变化,这笔开销纯属浪费。

第三项是关系问题:对别人的站点保持礼貌,是长期运行的前提。这一点与《robots.txt 里的 AI 爬虫该怎么配》里的原则一致——频率控制既是技术问题,也是态度问题。

变化检测有三种信号,按成本从低到高用

信号一:HTTP 条件请求(最省)

带上上次请求返回的校验信息再要一次:

1
2
3
GET /posts/hello/ HTTP/1.1
If-None-Match: "a1b2c3d4"
If-Modified-Since: Tue, 15 Sep 2026 08:00:00 GMT

服务端如果判断没变化,会返回 304 Not Modified只有响应头、没有正文。这是成本最低的一层过滤。

两个注意点:不是所有站点都正确支持;304 只说明服务端认为没变,不代表内容真的没变(有些站点永远返回 200)。

信号二:正文哈希(最可靠)

把抓到的正文算一个哈希值,与上次比较,不同就说明内容变了。

关键是先剥离模板部分再哈希:导航、页脚、侧边栏里的"最新文章"列表每天都会变,如果把它们算进去,每次哈希都不一样,增量就失效了。

1
抓取 → 正文提取(同采集阶段)→ 归一化(去空白、去时间戳)→ 哈希 → 比对

信号三:站点地图与订阅源(最便宜地发现"新增")

sitemap.xml 里的 lastmod、RSS 里的 pubDate,都能直接告诉你哪些地址是新出现的。这一层适合用来发现新增内容,但用于判断"内容是否被修改"并不可靠——很多站点不更新 lastmod

实用的组合是:用 sitemap/RSS 发现新增,用条件请求降低请求量,用正文哈希做最终判定。

重算范围要按变化类型区分

发现内容变了之后,不是所有处理都要重做。区分清楚能省下大量算力:

变化类型 需要重做的事
正文文本变化 重新切分片段、重新向量化
标题或小标题变化 重新切分,正文片段向量可保留
仅发布时间/作者等元数据变化 只更新字段,不动向量
页面结构变化(模板调整) 重新提取正文并比对,可能不需要重算向量

只有正文文本真正变化时才需要重新向量化,这是最花钱的一步。把判断条件写严格一点,长期省下的开销很可观。

内容消失和地址变更必须单独处理

这是最容易被忽略的一类情况。页面上一次抓取还在,这次返回 404 或者跳到了别的地址。

处理原则:

  • 返回 404 时不要立刻删除,先标记为"疑似失效",连续几次确认后再置为失效状态
  • 返回 301/302 时记录新地址,把新旧地址关联起来,避免同一内容被当成两份
  • 保留历史版本:内容变了不代表旧版本没价值,检索到旧片段时至少要知道它已经过期

**为什么不能直接删?**因为目标站点可能只是临时故障,或者做了短时间的维护。直接删除会让检索结果出现空洞,而恢复成本远高于保留状态标记。

任务调度要限速,也要能中断续跑

增量任务的一个现实约束是:它会被中断。

可能的原因很多——网络波动、目标站点超时、服务器重启。所以调度设计要满足两点:

**第一,任务粒度足够小。**以"单个页面"为最小单位,而不是"整站"。中断后只需重跑未完成的部分。

**第二,状态要持久化。**每个页面的处理状态(待处理、已抓取、已提取、已向量化)写进数据库,而不是留在内存里。

1
2
3
pending → fetched → extracted → embedded → done
↓ 失败
retry(记录次数,超过阈值转 failed)

配合一个简单的并发上限和请求间隔,任务就能在无人值守的情况下长期运行。

一个最小可行的增量方案

把上面几点合起来,一个够用的方案是:

  1. sitemap.xml 和 RSS 拉取地址列表,与库里比对,找出新增地址
  2. 对已有地址发条件请求,拿到 200 的加入待检查队列(拿到 304 的直接跳过)
  3. 对待检查的页面提取正文、归一化、算哈希
  4. 哈希不同的,按变化类型决定是否重新切分与向量化
  5. 对未出现在新列表里的地址,标记为疑似失效,多次确认后再置失效
  6. 全程记录状态,支持中断续跑

这套流程不需要复杂调度框架,一张状态表加一个循环就能跑起来。先让它稳定运行,再考虑优化并发和分布式。

常见问题(FAQ)

增量带来的复杂度值得吗?

取决于规模和周期。内容在几百篇以内、每周跑一次,全量重跑完全够用。**一旦出现"跑不完一个周期"或者向量化开销明显上升,就该做增量了。**过早引入增量会让原型阶段的迭代变慢。

条件请求返回 304,还需要比对哈希吗?

通常不需要,304 已经说明服务端认为内容未变。两种情况例外:站点返回的 ETag 生成规则不稳定(例如每次都变),或者站点从不返回有效校验头。遇到这类站点就直接进入哈希比对,不要依赖条件请求。

内容变了但只想更新向量,不想重新切分可以吗?

不建议。正文变化后,片段边界通常也会变化——只在旧片段上重算向量,会让片段与内容不再对应,检索结果会出现错位。切分和向量化应该作为一组操作处理。

抓取频率怎么定比较合适?

三个约束取最小值:robots.txt 里的 Crawl-delay(如果有)、目标站点的响应时间、以及你自己的周期要求。**实践上从保守值开始,观察目标站点是否返回 429 或 503,再逐步调整。**宁可跑慢一点,也不要因为请求过密被拉黑。

小结

  • 全量重跑的时间、算力与外部压力都会随规模放大,增量是稳定运行的前提。
  • 三种变化信号按成本使用:条件请求、正文哈希、sitemap/RSS。
  • 哈希前必须剥离模板部分,否则每次都会被判定为"已变化"。
  • 只有正文真正变化时才重新向量化,这是最省钱的优化。
  • 内容消失要标记而非删除,任务状态要持久化以支持中断续跑。

上一篇是本系列的《内容采集与结构化流水线》,架构总览见《整体架构与技术选型》。如果你的增量方案有更省事的做法,欢迎到留言板交流。

站内搜索

没有找到内容!