搜尋抓取

304 與缓存头:蜘蛛第二次抓取时怎么判断頁面變没變

蜘蛛重复訪問同一個 URL 时,往往先带上 If-Modified-Since 和 If-None-Match 這两個條件請求头,問一句内容變了没有。本文說明 Last-Modified 與 ETag 该怎么設定、哪些寫法會让 304 全部變成 200、304 與抓取预算的真實關系,以及如何從日誌里核查條件請求到底有没有生效。

搜尋抓取

304 與缓存头:蜘蛛第二次抓取时怎么判断頁面變没變

蜘蛛第二次来时,先問的不是“内容呢”

一個頁面被發現之後,蜘蛛並不會每次都把 HTML 完整下载一遍。第二次、第三次訪問同一個 URL 时,它往往先带上條件請求头,問服務器一句话:這個地址的内容,從我上次抓過之後有没有變化。服務器的回答决定它是拿到正文,還是只收到一段很短的 304 Not Modified 响應。

理解這個過程,能解释不少抓取里的現象:為什么某些頁面抓取字节數很小,為什么更新迟迟没被感知,為什么同一個站的不同节点抓取结果不一样。

條件請求靠两個头

蜘蛛常用的两個請求头是 If-Modified-SinceIf-None-Match,分別對應服務器返回過的 Last-ModifiedETag

Last-Modified

服務器告诉蜘蛛“這個頁面最後改于某個時間”。蜘蛛下次带着這個時間去問,如果服務器上的修改時間没有更晚,就回 304。它必须是規范格式的 HTTP 日期,带 GMT,不能是本地时区拼出来的字符串,也不能用頁面渲染那一刻临时生成。

ETag

ETag 是内容版本的指纹,可以是内容哈希,也可以是版本号。關键在于同一個頁面在不同服務器、不同节点上要给出同一個值。如果集群里每台机器按自己的文件 inode 生成 ETag,蜘蛛每次来都像看到“新内容”,304 就會全部落空。

几個把 304 變成 200 的常见寫法

  1. 每次請求都用目前時間生成 Last-Modified,頁面明明没改,時間戳却在往前走。
  2. ETag 里掺了随机數、進程号或未同步的本地文件信息。
  3. 缓存层把源站的响應头改寫或删掉,蜘蛛拿不到可用的校驗值。
  4. 内容更新後没有同步修改時間,蜘蛛带着舊時間問,服務器仍然回 304,更新被繼續隐藏。

最後一條最容易出错:304 只有在内容确實没變时才應该返回。為了省流量而長期對已经更新的頁面回答 304,等于让蜘蛛繼續使用舊版本。

它和抓取预算的關系

304 不下载正文,但一次請求仍然算一次抓取。它的價值在于降低單次抓取的资源消耗,让蜘蛛在同样的時間里走更多 URL,或者把带宽留给真正變化的頁面。它不會凭空提高抓取频次,也替代不了内鏈和 Sitemap 提供的發現路径。

也不要走到另一個极端:把所有頁面都设成長期缓存,指望蜘蛛少来。抓取频次由多種因素共同决定,缓存头只是其中很小的一個變量。

和 Sitemap 的 lastmod 對得上吗

Sitemap 里的 lastmod 是站点主動声明的時間,响應头里的 Last-Modified 是服務器给出的時間。两者如果長期矛盾,信号就會變得混乱:Sitemap 说昨天更新,响應头说三年前。做法其實很简單,让它們来自同一個資料源,也就是内容真正發生變化的那個時間。

怎么從日誌里看這件事

  • 統計蜘蛛請求中 304 與 200 的比例,按目錄分開看,找出 200 異常偏高的目錄。
  • 检查同一 URL 连續多次抓取时返回的狀態碼序列,判断是否存在“每次都是 200”。
  • 對比抓取字节數與頁面實际大小,若長期接近全量下载,條件請求可能没生效。
  • 抽查几條 URL,用工具带上 If-Modified-Since 手動請求一次,看服務器真實反應。
304 是一句诚實的回答,不是省事的技巧。内容變了就返回 200,内容没變才返回 304,這條线不要越過。

把缓存头和内容發布流程绑在一起,比事後翻日誌找原因省力得多。每次上线更新时顺手確認一下時間戳是否真的變了,抓取端的表現往往就會稳定不少。