蜘蛛对站点的大部分访问,不是第一次发现新 URL,而是回来确认旧 URL 有没有变化。一个持续更新的栏目页可能一天被访问多次,一篇半年前的文章也可能隔几周被回访。这些重复访问里,服务器怎么回应,会直接影响蜘蛛对页面更新频率的判断,也会影响它对整个站点的抓取节奏。
重复访问是常态,不是异常
很多人看抓取日志时,注意力都放在“新 URL 有没有被发现”,忽略了另一条线:同一个 URL 被反复请求。蜘蛛需要维护一份本地副本,用来判断页面是否变化、要不要更新。它没有别的办法,只能定期回来问一次。所以日志里大量重复的 GET 请求本身是正常现象,关键在服务器给出的答案是否有效。
条件请求:蜘蛛带着上次的版本信息来问
HTTP 提供了协商机制。蜘蛛第一次抓取某页时,服务器通常会在响应里给出 Last-Modified 和 ETag。下次再来,蜘蛛可以把它们分别放进请求头:If-Modified-Since 和 If-None-Match。服务器比对之后,如果内容没变,返回 304 Not Modified,不带正文;如果变了,返回 200 和完整内容。这套机制叫条件请求。
304 省掉的不只是流量
- 正文体积:一个几十 KB 的 HTML 不必重复传输,出口带宽压力小很多。
- 蜘蛛侧的解析成本:不用重新解析、对比、入库。
- 抓取节奏:重复 URL 占用的时间少一点,分配给新 URL 和深层页面的机会就多一点。
需要说明的是,304 并不等于这个 URL 被重新收录或排名发生变化,它只是告诉蜘蛛“内容还是你手里那份”。收录与展示是另一套流程在决定的事。
三种常见的配置坑
Last-Modified 每次请求都变
一些 CMS、模板引擎或中间件会在渲染时把 Last-Modified 设成当前时间。结果蜘蛛每次都带着上次的时间来,服务器每次都判定“已经更新”,永远返回 200。更麻烦的是,页面明明没改,蜘蛛却把它记录成刚刚更新过,长期如此容易让更新信号失真。
ETag 里混入了机器变量
某些服务器生成的 ETag 包含进程号、请求时间戳或节点标识。同一份内容,在多台后端或不同 CDN 边缘节点上算出的值不一致,蜘蛛带来的 If-None-Match 自然匹配不上,于是每次都拿到 200。如果站点同时用了多机部署和缓存层,这个问题出现的概率会明显上升。
动态页面完全忽略条件头
有些程序对静态资源做了条件请求处理,对列表页、详情页这类动态输出则直接跳过。而这类页面往往恰好是蜘蛛最常回访的,忽略条件头相当于把最该节省的地方放过了。
自查可以这样做
- 挑一个近期没有改动的页面,请求一次,记录响应里的 Last-Modified 和 ETag。
- 把这两个值分别放进 If-Modified-Since 和 If-None-Match,再请求一次,观察是否返回 304。
- 间隔几分钟重复第二步。如果偶尔返回 200,多半是时间戳或 ETag 不稳定。
- 对同一 URL 分别走直连源站和 CDN,比较两次拿到的 ETag 是否一致。
- 回到服务端日志,确认 304 在重复请求里占多大比例。比例很低,就值得往上查一层。
别把 304 当成偷懒手段
还有一类反向的做法:内容明明变了,却因为缓存或程序判断错误,一直返回 304。蜘蛛会继续使用旧副本,页面上新的标题、价格、库存都不会被看到。条件请求的前提是判断准确,而不是尽量少返回 200。
分清楚两种情况:内容确实没变,可以放心回 304;不确定有没有变,或者页面本来就是实时输出的,就老老实实回 200。前者省资源,后者省麻烦。
回到抓取的整体视角,服务器稳定性、响应头和状态码,是蜘蛛理解一个站点的底层语言。URL 发现决定了蜘蛛能走到哪里,而重复访问时的回应质量,决定了它愿不愿意经常回来。