搜索抓取

搜索蜘蛛抓取:ETag 与 Last-Modified 不一致造成的重复下载排查

蜘蛛回访同一个 URL 时会带上条件请求头,服务器若能正确返回 304,就能避免重复下载正文。本文说明 ETag 与 Last-Modified 在哪些情况下会失配,包括动态时间、多机算法不一致、CDN 边缘重写等常见成因,并给出用命令行验证、多节点比对、结合抓取日志观察的具体排查步骤。

搜索抓取

搜索蜘蛛抓取:ETag 与 Last-Modified 不一致造成的重复下载排查

蜘蛛再次访问一个已经抓过的 URL 时,通常会带上 If-None-MatchIf-Modified-Since。如果服务器能正确回应 304,蜘蛛就知道这个地址没有实质变化,不必重新下载正文;如果每次都被回以 200 和完整页面,同一份内容就会被反复拉取。单个 URL 看不出差别,但当站点存在大量列表页、归档页、标签页时,这类重复下载会持续占用抓取额度,也让日志里的字节数虚高。

条件请求靠什么判定

服务端判断内容有没有变化,主要依赖两组头部:ETagIf-None-Match 配对,Last-ModifiedIf-Modified-Since 配对。任意一组匹配,就可以返回 304。问题往往出在两组头各自漂移,结果谁都匹配不上。

  • Last-Modified 取了动态时间:有些框架默认把响应生成时间写进 Last-Modified,每次请求都不同,条件判断必然失败。
  • ETag 里带了不稳定字段:inode、进程号、部署序号、随机串参与计算时,多台机器算出来的值不一致。
  • 强、弱 ETag 混用:一端返回带 W/ 前缀的弱校验值,另一端返回强校验值,比较规则不同,容易被判为已变更。
  • CDN 在边缘重写:源站的头正常,但缓存层重新生成了 ETag,回源与边缘两套值来回切换。
  • 多机房不同步:负载均衡把同一 URL 分到不同节点,各节点返回的校验值互不相同。

几个容易被当成正常的现象

第一,响应头里写了 Cache-Control: no-store,却又期望蜘蛛命中 304。这两个意图本身冲突,中间层通常不保留副本,条件请求也就无从比较。

第二,用 200 加空响应体代替 304。状态码看起来正常,但蜘蛛仍完成了一次完整的下载流程,节省不了带宽,解析环节还容易拿到空正文。

第三,页面只改了一个无关紧要的模块,例如推荐位顺序变化,Last-Modified 就整体更新,蜘蛛被反复叫回来,而真正变化的内容并不多。

排查步骤

  1. 先用命令行带条件头请求一次:取出首次响应的 ETag 与 Last-Modified,再带上 If-None-MatchIf-Modified-Since 重发,看能否稳定返回 304。
  2. 连续请求同一 URL 多次,比对两次的响应头是否完全一致;不一致就先定位是哪一层在改写。
  3. 在多台源站上分别请求,确认所有节点返回同一组值。若不一致,大概率是校验算法里掺了机器相关字段。
  4. 翻抓取日志里同一 URL 的响应字节数,如果长期接近页面完整大小且状态码多为 200,说明 304 基本没生效。
  5. 绕过 CDN 直接回源再测一次,区分问题出在源站还是边缘节点。

调整方向

核心思路是让同一份内容在任意时间、任意节点上都给出稳定且一致的头。

  • ETag 建议只基于内容摘要计算,或干脆去掉自定义值,交给 Last-Modified 负责。
  • Last-Modified 使用内容真实的最后修改时间,不要用渲染时间。
  • 多机部署时统一算法,避免文件名、路径大小写差异参与计算。
  • 缓存层设置为透传这两个头,不要自行重写。
  • 确认没有变化就用 304 回应,不要用 200 空体兜底。

观察与验证

调整之后不要只看一次请求的结果。建议连续观察几天:看同一 URL 重复下载的次数是否下降,响应字节数分布是否向小体积偏移,日志里 304 的比例是否趋于稳定。数据有起伏是正常的,重要的是趋势,而不是某一天的数字。

条件请求不是抓取优化的开关,它只是把重复下载的成本降下来。真正决定抓取效率的,仍然是入口是否清晰、内容是否有稳定更新,以及服务器能否及时响应。