蜘蛛第二次来时,先問的不是“内容呢”
一個頁面被發現之後,蜘蛛並不會每次都把 HTML 完整下载一遍。第二次、第三次訪問同一個 URL 时,它往往先带上條件請求头,問服務器一句话:這個地址的内容,從我上次抓過之後有没有變化。服務器的回答决定它是拿到正文,還是只收到一段很短的 304 Not Modified 响應。
理解這個過程,能解释不少抓取里的現象:為什么某些頁面抓取字节數很小,為什么更新迟迟没被感知,為什么同一個站的不同节点抓取结果不一样。
條件請求靠两個头
蜘蛛常用的两個請求头是 If-Modified-Since 和 If-None-Match,分別對應服務器返回過的 Last-Modified 和 ETag。
Last-Modified
服務器告诉蜘蛛“這個頁面最後改于某個時間”。蜘蛛下次带着這個時間去問,如果服務器上的修改時間没有更晚,就回 304。它必须是規范格式的 HTTP 日期,带 GMT,不能是本地时区拼出来的字符串,也不能用頁面渲染那一刻临时生成。
ETag
ETag 是内容版本的指纹,可以是内容哈希,也可以是版本号。關键在于同一個頁面在不同服務器、不同节点上要给出同一個值。如果集群里每台机器按自己的文件 inode 生成 ETag,蜘蛛每次来都像看到“新内容”,304 就會全部落空。
几個把 304 變成 200 的常见寫法
- 每次請求都用目前時間生成 Last-Modified,頁面明明没改,時間戳却在往前走。
- ETag 里掺了随机數、進程号或未同步的本地文件信息。
- 缓存层把源站的响應头改寫或删掉,蜘蛛拿不到可用的校驗值。
- 内容更新後没有同步修改時間,蜘蛛带着舊時間問,服務器仍然回 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,這條线不要越過。
把缓存头和内容發布流程绑在一起,比事後翻日誌找原因省力得多。每次上线更新时顺手確認一下時間戳是否真的變了,抓取端的表現往往就會稳定不少。