搜索抓取

条件请求与蜘蛛抓取:Last-Modified、ETag 和 304 到底省了什么

蜘蛛重复抓取同一页面时,服务器可以通过 Last-Modified 与 ETag 告诉它内容有没有变化。做对了能减少传输与解析开销,做错了反而让蜘蛛反复下载同一份内容。本文说明条件请求的工作方式、常见配置坑,以及可落地的自查步骤。

搜索抓取

条件请求与蜘蛛抓取:Last-Modified、ETag 和 304 到底省了什么

蜘蛛第一次抓走页面之后,还会再来。它再来的时候,服务器其实有机会说一句:这个页面没变,别下载了。这套机制就是条件请求(Conditional Request)。用对了能减少传输和解析开销,用错了会让蜘蛛反复拉取同一份内容,甚至拿到过期的判断。

蜘蛛再来时的两次握手

蜘蛛第二次访问某个 URL 时,如果之前拿到过缓存信息,请求头里可能带上其中之一:

  • If-Modified-Since:值是上次响应里的 Last-Modified 时间。
  • If-None-Match:值是上次响应里的 ETag 标识。

服务器比对之后给出两种结果:内容没变,返回 304 Not Modified,不带响应体;内容变了,返回 200 和完整页面。对蜘蛛来说,304 意味着它不用重新下载和解析正文,可以直接沿用已有内容。

Last-Modified 常见的失真来源

Last-Modified 看起来最简单,也最容易写错。动态站点里常见的几种情况:

  • 每次请求都输出当前时间,等于告诉蜘蛛“刚刚改过”,蜘蛛只能每次完整下载。
  • 内容存在数据库里,文件本身没动,Last-Modified 停留在很早以前,蜘蛛会误判页面长期不变。
  • CDN 回源时间被当成资源修改时间,源站更新后缓存层没刷新,时间对不上。

如果站点用的是静态文件加 Nginx,默认的 Last-Modified 来自文件修改时间,通常比较可靠;一旦生成逻辑是动态拼接,就需要手动维护一个真实的“内容更新时间”。

ETag 在多节点环境里更容易出错

ETag 的理论效果比 Last-Modified 更精确,问题出在一致性上:

  • 多台后端各自生成 ETag,值里含 inode、进程号或本地时间戳,同一 URL 在不同机器上返回不同 ETag,蜘蛛会认为内容一直在变。
  • 反向代理、负载均衡或某些中间层会改写甚至丢弃 ETag,请求里的 If-None-Match 传不到后端。
  • 页面每次响应都带随机推荐位、时间戳或一次性 token,正文没变但字节变了,ETag 自然每次都不同。
  • 部分服务器对压缩前后的内容分别计算 ETag,gzip 与 br 之间切换时会来回变化。

304 能不能省下抓取预算

需要说清楚一点:从蜘蛛的视角看,304 依然算一次对 URL 的抓取。它节省的是带宽、服务器 CPU 和解析时间,并不直接换来更多抓取次数。所以不要指望靠 304 提升抓取频率,但把它做对,确实能减轻服务器压力,也让蜘蛛更快走完一轮 URL。真正决定蜘蛛来不来的,还是 URL 是否可发现、服务器是否稳定、页面是否有更新价值。

可落地的自查顺序

  1. 正常抓一次目标页面,记录响应头里的 Last-Modified 与 ETag。
  2. 间隔几分钟再抓,手动带上 If-None-Match 或 If-Modified-Since,观察是否返回 304。
  3. 换一台服务器或换一个 CDN 节点重复同样的请求,比较 ETag 是否一致,这一步最容易暴露集群问题。
  4. 修改页面内容后立刻请求,确认返回的是 200 且带新正文,而不是 304。
  5. 检查中间层是否吞掉了条件请求头,可以对比源站直连与经过 CDN 后的响应差异。
条件请求是一项减少浪费的优化,不是抓取策略的核心。先保证 URL 能被发现、服务器能稳定响应,再考虑用 304 把重复传输压下来,顺序反了收益很有限。

如果你的站点页面更新频率很低,做对条件请求的收益会比较明显;如果内容频繁变动,重点则应该放在让 Last-Modified 反映真实更新,而不是为了凑 304 而保留过时的时间戳。