蜘蛛第一次抓走页面之后,还会再来。它再来的时候,服务器其实有机会说一句:这个页面没变,别下载了。这套机制就是条件请求(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 是否可发现、服务器是否稳定、页面是否有更新价值。
可落地的自查顺序
- 正常抓一次目标页面,记录响应头里的 Last-Modified 与 ETag。
- 间隔几分钟再抓,手动带上 If-None-Match 或 If-Modified-Since,观察是否返回 304。
- 换一台服务器或换一个 CDN 节点重复同样的请求,比较 ETag 是否一致,这一步最容易暴露集群问题。
- 修改页面内容后立刻请求,确认返回的是 200 且带新正文,而不是 304。
- 检查中间层是否吞掉了条件请求头,可以对比源站直连与经过 CDN 后的响应差异。
条件请求是一项减少浪费的优化,不是抓取策略的核心。先保证 URL 能被发现、服务器能稳定响应,再考虑用 304 把重复传输压下来,顺序反了收益很有限。
如果你的站点页面更新频率很低,做对条件请求的收益会比较明显;如果内容频繁变动,重点则应该放在让 Last-Modified 反映真实更新,而不是为了凑 304 而保留过时的时间戳。