讨论抓取问题时,多数人只盯着源站的访问日志。但今天大部分站点前面都有一层或多层缓存:CDN 边缘节点、反向代理、页面缓存插件、对象存储。蜘蛛发出的请求,很多时候根本没有到达你查看日志的那台服务器,它拿到的是某一层缓存里的副本。理解这一点,才能解释一些看起来很矛盾的现象,比如“页面明明已经改过,蜘蛛抓到的还是旧内容”。
一次请求可能穿过几层缓存
从蜘蛛到你的源站,路径大致是这样:
- 蜘蛛发起请求,先做 DNS 解析,通常解析到离它较近的 CDN 节点。
- 边缘节点查自己的缓存,命中就直接返回,不会回源。
- 未命中时,向上一级缓存或父节点请求。
- 仍然未命中,才回到源站,源站生成页面并逐层写回缓存。
也就是说,蜘蛛的一次抓取可能完全没有碰到你的应用服务器。你看到的“零访问”不等于蜘蛛没来;你看到的“页面已更新”也不等于蜘蛛拿到的是新版。
缓存键决定了蜘蛛会拿到哪一份
缓存系统靠缓存键判断某个请求能不能复用一份已有的副本。缓存键通常包含域名、路径、查询字符串,有时还包含请求头。
Vary 与 User-Agent 分变体
如果配置里写了按 User-Agent 区分缓存,例如给移动端返回另一套模板,蜘蛛的 UA 就可能对应一个单独的变体。这个变体什么时候生成、什么时候过期,往往和普通用户的副本不同步。结果是普通用户看到新版,蜘蛛还在读旧版。
缓存过期时间与发布节奏
缓存 TTL 设得长,好处是回源压力小、响应快;代价是内容更新后,蜘蛛可能在一段时间内拿不到新版本。页面类 URL 的 TTL 一般不宜太长,尤其是详情页、列表页这类经常变动的地址;静态资源则相反,可以设得很长,靠文件名或版本号来换新。
命中缓存和未命中,对蜘蛛意味着什么
- 命中缓存:响应通常在几十毫秒内返回,蜘蛛的抓取节奏顺畅,单位时间内能走更多 URL。
- 未命中回源:如果源站生成一个页面要一两秒,蜘蛛就要等一两秒;同时回源请求会打到源站,遇到发布或批量更新时容易形成叠加。
- 错误页被缓存:这是最麻烦的一种。源站偶发 5xx 或超时,如果错误响应被缓存层保留下来,蜘蛛会在 TTL 内反复拿到同样的错误,抓取进度相当于被按下暂停键。
判断抓取异常时,先分清问题出在缓存层还是源站,再决定改配置还是改代码。
怎么确认蜘蛛看到的是哪一版
不用猜,用几个手段交叉验证:
- 看 CDN 或代理的访问日志,按蜘蛛 UA 过滤,观察状态码、缓存命中状态与响应时间。多数 CDN 会标注 HIT 或 MISS。
- 对比源站日志与 CDN 日志的请求量差异,估算有多少抓取被缓存层拦下、没有回源。
- 从不同网络位置请求同一个 URL,看返回内容是否一致;如果不同节点内容不同,说明缓存键或过期策略存在分歧。
- 检查响应头里的缓存相关字段,确认 TTL 与预期一致。
- 发布新内容后,观察蜘蛛下一次抓取拿到的版本,而不是只看自己浏览器里的结果。
几个容易踩的坑
- 只清源站缓存:改了内容却只重启应用,CDN 上的旧副本仍在。
- 缓存了带参数的页面:站内搜索、排序、筛选参数组合被缓存,等于给大量低价值 URL 也做了副本,既占缓存空间也消耗抓取预算。
- 错误响应进缓存:短暂故障被放大成持续故障,建议让 5xx 不缓存,或只缓存极短时间。
- 发布时批量失效:一次性把大量 URL 的缓存清掉,接下来蜘蛛集中回源,源站压力陡增,反而更容易出错。
小结
蜘蛛的抓取体验,是它从 DNS 到拿到内容整条链路的综合结果。缓存层既是加速器,也是信息差所在。把缓存键、TTL、错误响应策略这三件事理清楚,再回头看抓取日志,很多“蜘蛛不抓”“抓的是旧版”的疑问会自己消解。