整理抓取日志时,经常能看到同一个 URL 在短期内被反复请求,每次都是 200,每次都传输完整正文。出现这种情况,第一反应往往是“蜘蛛抓得太频繁”,但排查下去常常发现:问题出在服务器没有给爬虫留下“内容没变”的判断依据。
缓存头为什么会进入抓取排查范围
搜索蜘蛛再次访问一个 URL 时,通常会带上 If-None-Match 或 If-Modified-Since 请求头,相当于问一句“这个地址和上次相比有没有变化”。如果服务器能明确回答“没变”,返回 304 且不带正文,爬虫就省下了一次完整下载,抓取调度可以把时间用在别的 URL 上。
反过来,如果服务器每次都给不出有效回答,只能返回 200 和整页 HTML,那么同一批页面的重复请求就会持续消耗源站带宽和抓取额度。这属于服务器侧的配置问题,不是抓取频率本身的问题。
三类常见的配置失误
1. ETag 每次请求都变化
部分框架或中间层会把时间戳、随机数、进程 ID 一起参与 ETag 计算,导致同一份内容每次返回的 ETag 都不相同。爬虫带着上一次的 If-None-Match 过来,服务器永远判定不匹配,只能老老实实返回 200 全量正文。
2. Last-Modified 缺失或时间失真
动态拼接的页面,Last-Modified 有时取的是“当前渲染时间”,每次请求都在变;也有的站点干脆不输出这个头。两种情况下,条件请求都失去意义。
3. Cache-Control 一刀切
为了防止内容被错误缓存,有的站点对所有 HTML 统一加 no-store、no-cache。这类设置会让中间层和抓取侧都放弃复用判断,页面体积越大,重复下载带来的浪费越明显。
从日志里怎么确认
- 统计同一 URL 在一天内的请求次数,以及其中 304 的占比。如果几乎全是 200,说明条件请求基本没生效。
- 检查日志中是否出现 If-None-Match / If-Modified-Since 字段。如果爬虫压根没带,问题在响应头或中间层;如果带了但结果仍是 200,问题在服务端比对逻辑。
- 对比源站与 CDN 回源记录的响应头,确认 EDgE 节点没有把 ETag 或 Last-Modified 丢掉。
- 留意平均响应体大小。正文重复传输的体积,往往比请求次数更能说明浪费程度。
调整顺序建议
- 先固定一套稳定的 ETag 生成规则,建议基于文件指纹或内容摘要,而不是请求时间。
- 为 HTML 输出可用的 Last-Modified,取值来自内容真实更新时间,而不是渲染时间。
- 把 Cache-Control 按资源类型区分:静态资源可以较长缓存,HTML 用较短 max-age 配合 must-revalidate,避免整站 no-store。
- 确认服务器对 If-None-Match 的比对逻辑能正常返回 304,且 304 响应不带正文。
- 调整后观察一到两周的抓取日志,看同一 URL 的重复全量下载是否下降、抓取覆盖是否稳定。
需要留意的边界
- 内容变更时,ETag 与 Last-Modified 必须同步变化。否则爬虫会一直认为页面没更新,抓到的始终是旧版本。
- 不同搜索蜘蛛对缓存头的支持程度并不一致,304 只是减少冗余传输的手段,不能当成通用的抓取节流开关。
- 站点如果使用了多级缓存,任何一层改错都会让 304 失效,改完要在最终对外响应上再确认一遍。
缓存配置的目标不是让爬虫少来,而是让它在每次访问中拿到准确、有变化的信号。判断标准始终是:内容变了能及时被发现,内容没变不重复传输。
这类调整通常不会带来立竿见影的抓取量变化,但会体现在长期稳定性上:源站压力更平缓,抓取额度更多流向真正需要更新的页面。配合 Sitemap 的更新时间维护和内链入口检查一起做,效果会更完整。