蜘蛛對站点的大部分訪問,不是第一次發現新 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 發現决定了蜘蛛能走到哪里,而重复訪問时的回應质量,决定了它愿不愿意经常回来。