為什么同一個 URL 會被反复抓取
蜘蛛不是抓一次就记住頁面内容。為了確認頁面有没有更新,它會在一段時間後再次請求同一批 URL。如果一個站点有几萬個頁面,蜘蛛每天来几轮,服務器就要把這批頁面重复發送好几遍,而真正發生變化的可能只是一小部分,剩下的带宽和响應時間都花在了没變的内容上。
减少這種浪費的办法不止一種,條件請求是最容易被忽略、又最容易實現的一種。
條件請求是怎么工作的
蜘蛛第一次抓到頁面时,服務器會在响應头里带上 Last-Modified 或 ETag。下次再来,蜘蛛會把這两個值分別放進請求头 If-Modified-Since 和 If-None-Match,相当于問一句:這個版本和上次一样吗?
Last-Modified 與 If-Modified-Since
服務器把請求头里的時間與文件目前的修改時間做比較。如果文件没變,返回 304 Not Modified,並且不带正文;如果變了,返回正常的 200 和完整内容。
ETag 與 If-None-Match
ETag 是内容版本的标识,可以是文件指纹,也可以由内容計算得出。它比時間更精确,适合修改時間不可靠、或者同一秒内多次寫入的场景。两者可以同时存在,服務器一般優先參考 ETag。
304 带来的實际好處
- 响應体為空,传輸的資料量明顯下降,带宽占用减少。
- 服務器不必重新渲染整個頁面,响應時間通常比完整請求短。
- 蜘蛛用更少的時間確認一批 URL 没有變化,省下的抓取額度可以分给新頁面和更新頁面。
- 源站压力下降,抓取高峰时更不容易出現超时。
需要說明的是,304 只是让重复抓取變便宜,它不會直接让新頁面更快被發現。URL 發現仍然要靠内鏈、Sitemap 這些入口。
几種让 304 失效的常见情形
- Last-Modified 每次請求都變。有些程序直接拿目前時間当修改時間,蜘蛛带回来的時間永遠對不上,于是每次都拿到 200。
- ETag 里含進程号或時間戳。同一份内容在不同机器、不同時間生成的 ETag 不一样,條件請求永遠判定為已修改。
- CDN 或反向代理覆盖了头部。源站設定正确,但邊缘节点重新生成了 ETag,或者强制返回完整响應。
- 動態頁面没有做内容比對。由資料库查询拼装出来的頁面,如果不額外計算版本标识,就只能一直返回 200。
- 對首次請求返回 304。没有歷史版本的客戶端拿到 304,頁面内容就完全拿不到了。
在日誌里怎么核對效果
服務器日誌里的狀態碼一栏可以直接看出 304 的比例。做法很简單:把最近的蜘蛛請求按狀態碼分组,看看 200 與 304 各占多少。如果同一批 URL 连續多天全是 200,而頁面内容並没有變化,就說明條件請求没有生效。
顺便留意日誌里 304 的响應字节數是不是接近 0。如果狀態碼是 304 但字节數很大,說明中間层仍在传輸完整正文,节省的效果並没有落到實處。
配置时的几点建议
- 让 ETag 基于内容本身生成,不要插入時間或随机值。
- Last-Modified 使用文件或資料的真實修改時間,不要用請求時間。
- 静態资源交给 Web 服務器處理條件請求,動態頁面在應用层做一次内容摘要比對。
- 检查 CDN 配置,確認邊缘节点不會替換源站的 ETag,也不會把 304 轉成 200。
- 改動後用日誌复核一段時間,確認 304 比例上升,同时没有出現内容错乱。
條件請求不是抓取優化的全部,但它把重复確認這件事的成本压得很低。在抓取額度有限的情况下,省下来的那部分,可以留给真正需要抓的頁面。