搜索抓取

304 与条件请求:没变的页面,怎么让蜘蛛少走一趟

页面内容没变时,蜘蛛仍可能带着 If-Modified-Since 或 If-None-Match 再请求一次。本文讲条件请求在抓取中的实际作用:ETag 与 Last-Modified 怎么生成才稳定,CDN 与缓存层会带来哪些干扰,哪些页面适合用 304、哪些不必强求,以及如何从抓取日志判断它是否真的生效。

搜索抓取

304 与条件请求:没变的页面,怎么让蜘蛛少走一趟

蜘蛛抓取一个页面,服务端要解析、查询、拼装 HTML、传输;如果这份内容一个月没变,这些工作就重复做了。HTTP 协议里有一套机制专门处理这种情况:条件请求。站点把它用对,可以让蜘蛛对没变化的页面只拿到一个 304 响应,省下的是双方的资源。

条件请求是怎么跑的

当蜘蛛第二次请求同一个 URL 时,会在请求头里带上 If-Modified-Since 或 If-None-Match,取值分别来自上一次响应里的 Last-Modified 和 ETag。服务端比对之后,如果内容没变,就返回 304 Not Modified,不带正文。蜘蛛据此判断“还是老样子”,不必重新解析页面。

有两点容易被误解:304 同样算一次抓取请求,省掉的是带宽和解析工作量,不是访问次数;另外并非所有抓取器都稳定使用条件请求,是否发送这两个头取决于抓取实现与策略,所以不要把它当成调节抓取频次的主要手段。

让 ETag 稳定下来

最常见的失效原因是 ETag 不稳定。有些框架默认用进程 ID 加时间戳、或者随机值来生成 ETag,同一份内容每次请求都得到不同的值,蜘蛛永远拿不到 304,这套机制等于白做。

  • 把 ETag 基于内容来算:文件哈希、内容长度加更新时间戳的组合都可以,只要同一内容结果一致。
  • 不要把随机数、请求 ID、会话标识放进 ETag。
  • 页面里插入了实时数据(倒计时、随机推荐位)时,要么固定这部分输出,要么接受它一直显示为“已更新”。

Last-Modified 要给真实的修改时间

Last-Modified 应该反映正文最后一次实质修改的时间,而不是本次请求时间或整体部署时间。如果每次发版都刷新全站页面的时间戳,蜘蛛会以为整站都变了,结果白抓一遍。尽量按内容维度记录更新时间,而不是按构建批次统一写入。

缓存层与 CDN 会改变结果

站点前面挂了 CDN 或反向代理时,条件请求的判定可能发生在那一层。常见的情况有:

  • 带不同 query、cookie、User-Agent 的请求被分开缓存,命中率下降;
  • 边缘节点改写了 ETag 或 Last-Modified,回源比对因此失效;
  • 缓存过期时间设得太短,蜘蛛拿到的仍然是带正文的完整响应。

排查时可以看抓取日志里的状态码分布:如果 200 占绝大多数、304 几乎没有,说明条件请求很可能没有生效,值得回头查响应头和缓存配置。

哪些页面不必强求 304

内容高频变化的首页、列表页、资讯流,本来就在持续更新,直接用 200 返回新内容更清楚。真正适合做条件请求的,是那些长期稳定又需要被反复确认的页面,比如商品详情、文档、帮助中心、历史文章。对已经确定下线的页面,则应该用 404 或 410 明确告知,而不是靠 304 拖着。

把条件请求当成“减少无效传输”的手段,而不是“减少抓取”的手段,判断标准会清晰很多:内容没变就少传,内容变了就如实返回。

落地检查清单

  1. 抽查几个稳定页面的响应头,确认 ETag 或 Last-Modified 存在,且不是每次请求都变化。
  2. 用同一份请求头连续请求两次,确认第二次返回 304。
  3. 检查 CDN 是否透传或正确生成这两个头。
  4. 观察抓取日志中 304 的比例,结合栏目更新频率判断是否合理。
  5. 对高频更新的栏目不强求 304,把精力放在稳定内容的页面上。

这些调整本身不会让蜘蛛来得更多,但能减少它在没变化的页面上重复消耗的时间,把抓取资源更多地留给真正有更新、需要被重新处理的 URL。