蜘蛛重复访问同一个入口页时,服务器并不总是把整页内容重新发一遍。HTTP 协议里的条件请求机制,让蜘蛛可以先问一句“我上次拿到的那份还作数吗”,服务器用 304 回答“没变”,双方都省流量。这套机制原本是给浏览器设计的,但在蜘蛛池场景下,它同样在悄悄影响抓取节奏。
条件请求是怎么发生的
蜘蛛在第一次抓取入口页后,会记录响应头里的 Last-Modified 或 ETag。下次抓取时,它把这些值放进请求头的 If-Modified-Since 或 If-None-Match。服务器比对后返回两种结果:内容没变就返回 304,不带正文;变了就返回 200 加新正文。
对蜘蛛来说,304 不是坏事。它说明链接还活着、页面结构稳定,只是内容没更新。真正需要担心的是另一种情况:内容明明改了,服务器却因为缓存配置错误一直返回 304,蜘蛛就会长期停留在旧版本上。
Last-Modified 和 ETag 分别管什么
- Last-Modified 基于文件修改时间,精度到秒。静态页面、直接由文件系统吐出的入口页用它最省事。缺点是文件被重新生成但内容一样时,时间戳变了,蜘蛛会重新下载一份完全相同的内容。
- ETag 是内容的指纹,理论上更精确。但很多服务器默认用“inode + 修改时间 + 大小”生成 ETag,多台机器之间不互通,蜘蛛在负载均衡的站点上会看到 ETag 反复变化,结果每次都是 200 全量返回。
两者同时存在时,蜘蛛通常优先使用 ETag。如果 ETag 不稳定,不如直接关掉它,只留 Last-Modified。
Cache-Control 不是给蜘蛛看的,但蜘蛛会看
Cache-Control: max-age 主要约束浏览器和中间缓存。蜘蛛一般不完全遵守它,但如果设置成很长的 max-age 又配合 CDN,中间层可能把旧内容一直吐给蜘蛛。入口页这类需要反映最新状态的页面,比较稳妥的做法是不给过长的缓存时间,或者让 CDN 对爬虫 User-Agent 回源。
缓存的目标是减少重复传输,不是让蜘蛛看不到更新。这两件事经常被混为一谈。
入口页内容频繁改动时怎么办
如果入口页每隔几小时就换一次推荐位或时间戳,条件请求的意义就不大,蜘蛛几乎每次都会拿到 200。这种情况下更值得关注的是改动本身有没有价值:正文主体不变、只变页脚年份,对蜘蛛来说等同于没变,反而浪费一次抓取。
真需要频繁更新的页面,可以只更新局部内容并保持 Last-Modified 稳定,避免因为无意义的响应头抖动引发蜘蛛高频回访。
几个常见误区
- 给所有响应套用固定 ETag 模板,导致大量页面指纹雷同,蜘蛛判断混乱。
- 反向代理和后端各自加一层缓存头,最终输出的 Last-Modified 比实际内容还新。
- 入口页返回 304 却同时带了一段说明文字,正文长度和状态码不一致。
- 为了让抓取日志好看,人为让页面永远返回 200,抓取量上升了,重复内容比例也一起上升。
自查步骤
- 用 curl -I 连续请求两次入口页,确认第二次返回的 Last-Modified 或 ETag 与第一次一致。
- 在请求头里带上 If-None-Match,确认服务器能正确返回 304。
- 改动页面正文后再请求一次,确认返回 200 且 ETag 已更新。
- 换一台机器或换一个出口 IP 请求,检查 ETag 是否仍然相同。
- 对照抓取日志,看蜘蛛对同一入口页的状态码分布,判断是 200 偏多还是 304 偏多。
缓存配置属于那种平时不出问题、出问题就很难排查的环节。把入口页的响应头保持稳定、可预测,比追求某种所谓的最优设置更实际。