蜘蛛第二次来时,先问的不是“内容呢”
一个页面被发现之后,蜘蛛并不会每次都把 HTML 完整下载一遍。第二次、第三次访问同一个 URL 时,它往往先带上条件请求头,问服务器一句话:这个地址的内容,从我上次抓过之后有没有变化。服务器的回答决定它是拿到正文,还是只收到一段很短的 304 Not Modified 响应。
理解这个过程,能解释不少抓取里的现象:为什么某些页面抓取字节数很小,为什么更新迟迟没被感知,为什么同一个站的不同节点抓取结果不一样。
条件请求靠两个头
蜘蛛常用的两个请求头是 If-Modified-Since 和 If-None-Match,分别对应服务器返回过的 Last-Modified 和 ETag。
Last-Modified
服务器告诉蜘蛛“这个页面最后改于某个时间”。蜘蛛下次带着这个时间去问,如果服务器上的修改时间没有更晚,就回 304。它必须是规范格式的 HTTP 日期,带 GMT,不能是本地时区拼出来的字符串,也不能用页面渲染那一刻临时生成。
ETag
ETag 是内容版本的指纹,可以是内容哈希,也可以是版本号。关键在于同一个页面在不同服务器、不同节点上要给出同一个值。如果集群里每台机器按自己的文件 inode 生成 ETag,蜘蛛每次来都像看到“新内容”,304 就会全部落空。
几个把 304 变成 200 的常见写法
- 每次请求都用当前时间生成 Last-Modified,页面明明没改,时间戳却在往前走。
- ETag 里掺了随机数、进程号或未同步的本地文件信息。
- 缓存层把源站的响应头改写或删掉,蜘蛛拿不到可用的校验值。
- 内容更新后没有同步修改时间,蜘蛛带着旧时间问,服务器仍然回 304,更新被继续隐藏。
最后一条最容易出错:304 只有在内容确实没变时才应该返回。为了省流量而长期对已经更新的页面回答 304,等于让蜘蛛继续使用旧版本。
它和抓取预算的关系
304 不下载正文,但一次请求仍然算一次抓取。它的价值在于降低单次抓取的资源消耗,让蜘蛛在同样的时间里走更多 URL,或者把带宽留给真正变化的页面。它不会凭空提高抓取频次,也替代不了内链和 Sitemap 提供的发现路径。
也不要走到另一个极端:把所有页面都设成长期缓存,指望蜘蛛少来。抓取频次由多种因素共同决定,缓存头只是其中很小的一个变量。
和 Sitemap 的 lastmod 对得上吗
Sitemap 里的 lastmod 是站点主动声明的时间,响应头里的 Last-Modified 是服务器给出的时间。两者如果长期矛盾,信号就会变得混乱:Sitemap 说昨天更新,响应头说三年前。做法其实很简单,让它们来自同一个数据源,也就是内容真正发生变化的那个时间。
怎么从日志里看这件事
- 统计蜘蛛请求中 304 与 200 的比例,按目录分开看,找出 200 异常偏高的目录。
- 检查同一 URL 连续多次抓取时返回的状态码序列,判断是否存在“每次都是 200”。
- 对比抓取字节数与页面实际大小,若长期接近全量下载,条件请求可能没生效。
- 抽查几条 URL,用工具带上 If-Modified-Since 手动请求一次,看服务器真实反应。
304 是一句诚实的回答,不是省事的技巧。内容变了就返回 200,内容没变才返回 304,这条线不要越过。
把缓存头和内容发布流程绑在一起,比事后翻日志找原因省力得多。每次上线更新时顺手确认一下时间戳是否真的变了,抓取端的表现往往就会稳定不少。