搜索抓取

蜘蛛抓到的页面和用户看到的不一样:缓存层带来的差异

站点接入 CDN 或反向代理后,蜘蛛抓到的页面有时和用户看到的不一致:可能是边缘缓存里的旧副本,也可能是一张验证页。本文从请求链路、缓存头、条件请求和防护规则几个角度,说明怎么定位这种差异,以及哪些配置能让抓取路径更稳定。

搜索抓取

蜘蛛抓到的页面和用户看到的不一样:缓存层带来的差异

很多站点接入 CDN 或反向代理之后,会碰到一个不太直观的问题:自己用浏览器看页面是新的,蜘蛛抓到的却是旧内容;或者用户在页面上看到的内容正常,蜘蛛拿回来的却是一张验证页。这类差异通常不发生在内容本身,而在请求经过的那几层缓存与防护上。

蜘蛛的一次请求要经过哪些环节

把链路拆开看,问题会清楚很多:

  1. 蜘蛛解析域名,向 DNS 查询得到地址,这一步通常返回的是 CDN 边缘节点的 IP;
  2. 边缘节点检查本地是否有可用的缓存副本,命中就直接返回,不回源站;
  3. 未命中或副本已过期时,边缘节点回源站拉取;
  4. 源站或中间可能还有一层 WAF、负载均衡或页面缓存插件,它们同样能改写返回内容。

蜘蛛看到的 HTML,是这条链路上最后一次写入的结果。任何一环给了旧副本或替换了响应,蜘蛛拿到的就和你本机刷新看到的不同。

缓存命中时,蜘蛛拿到的是哪一份副本

HTTP 缓存头决定边缘节点把一份 HTML 保留多久。Cache-Control 的 max-age 与 s-maxage 是最常被混用的两个指令:前者对浏览器和共享缓存都生效,后者只针对 CDN 这类共享缓存。如果给 HTML 设了很长的 s-maxage,页面更新后蜘蛛再抓,很可能还是更新前的版本。

stale-while-revalidate 也值得留意。它允许边缘节点在副本过期后先返回旧内容,同时后台回源刷新。对静态资源没什么问题,对经常变动的详情页、活动页就可能让蜘蛛连续几天抓到旧版。

反过来,如果响应里带 Age 头,可以直接看出这份副本已经在边缘停留了多久。排查时这个字段比猜测有用得多。

条件请求与 304 的关系

蜘蛛再次抓取同一 URL 时,常会带上 If-None-Match 或 If-Modified-Since。如果服务端返回 304,蜘蛛就沿用本地已有版本,不重新解析正文。这本身是正常且高效的行为,但前提是 ETag 与 Last-Modified 能准确反映内容变化。

如果 ETag 是按机器、进程或时间戳生成的,每次回源都不一样,会出现两种情况:一边是缓存无法复用、回源量上升;另一边是内容其实没变,蜘蛛却每次都被当成新内容重新处理。多数情况下这不会直接造成收录问题,但会让抓取效率变差。

防护规则把蜘蛛当成普通流量

比缓存更棘手的是拦截。WAF、UA 黑名单、频率限制、JavaScript 挑战都可能作用在爬虫请求上。常见后果有三种:

  • 返回 403 或 503,蜘蛛这次抓取失败,稍后可能重试,也可能降低对该路径的访问频率;
  • 返回挑战页,但状态码是 200。这时代码会被当作页面正文处理,等于把一张空壳页当成了真实内容;
  • 返回精简过的 HTML,比如只给一部分内容,蜘蛛看到的页面结构和你预期的不同。

需要说明的是,让蜘蛛 UA 绕过某些规则只是一种技术选择,它并不等于对抓取行为不做任何约束,也不意味着一放行就会被正常收录。

怎么确认差异出在哪一层

  • 用命令行请求首页与重点页面,观察响应头里的 Cache-Control、Age、X-Cache 一类字段;
  • 对比源站访问日志:蜘蛛 UA 到达时返回的状态码、响应体大小、耗时;
  • 连续抓两次同一 URL,看第二次是否命中缓存、返回内容是否变化;
  • 如果页面内容依赖登录态或地区,确认这些分支是否影响蜘蛛看到的版本。

几个可以落地的调整

  • HTML 不要设置过长的强缓存,需要长期缓存的是图片、样式、脚本这类带指纹的静态资源;
  • 内容更新后主动刷新相关 URL 的边缘缓存,而不是等自然过期;
  • 保持 ETag 稳定,让相同的正文对应相同的标识;
  • 挑战页、限流页尽量返回明确的 503 并配合 Retry-After,避免用 200 包裹空内容;
  • 把蜘蛛的访问单独打点,一旦回源率或异常状态码上升,能第一时间看见。
缓存与防护本身都是必要的,问题往往不在这类组件是否存在,而在于蜘蛛请求经过它们时拿到的到底是不是你希望它读到的那份页面。把这一层的返回内容固定下来,抓取路径才谈得上稳定。