搜尋抓取

搜尋蜘蛛抓取:ETag 與 Last-Modified 不一致造成的重复下载排查

蜘蛛回訪同一個 URL 时會带上條件請求头,服務器若能正确返回 304,就能避免重复下载正文。本文說明 ETag 與 Last-Modified 在哪些情况下會失配,包括動態時間、多机算法不一致、CDN 邊缘重寫等常见成因,並给出用命令行驗證、多节点比對、结合抓取日誌观察的具体排查步骤。

搜尋抓取

搜尋蜘蛛抓取:ETag 與 Last-Modified 不一致造成的重复下载排查

蜘蛛再次訪問一個已经抓過的 URL 时,通常會带上 If-None-MatchIf-Modified-Since。如果服務器能正确回應 304,蜘蛛就知道這個地址没有實质變化,不必重新下载正文;如果每次都被回以 200 和完整頁面,同一份内容就會被反复拉取。單個 URL 看不出差別,但当站点存在大量列表頁、归档頁、标簽頁时,這類重复下载會持續占用抓取額度,也让日誌里的字节數虚高。

條件請求靠什么判定

服務端判断内容有没有變化,主要依赖两组头部:ETagIf-None-Match 配對,Last-ModifiedIf-Modified-Since 配對。任意一组匹配,就可以返回 304。問题往往出在两组头各自漂移,结果谁都匹配不上。

  • Last-Modified 取了動態時間:有些框架預設把响應生成時間寫進 Last-Modified,每次請求都不同,條件判断必然失敗。
  • ETag 里带了不稳定字段:inode、進程号、部署序号、随机串參與計算时,多台机器算出来的值不一致。
  • 强、弱 ETag 混用:一端返回带 W/ 前缀的弱校驗值,另一端返回强校驗值,比較規則不同,容易被判為已變更。
  • CDN 在邊缘重寫:源站的头正常,但缓存层重新生成了 ETag,回源與邊缘两套值来回切換。
  • 多机房不同步:负载均衡把同一 URL 分到不同节点,各节点返回的校驗值互不相同。

几個容易被当成正常的現象

第一,响應头里寫了 Cache-Control: no-store,却又期望蜘蛛命中 304。這两個意图本身冲突,中間层通常不保留副本,條件請求也就無從比較。

第二,用 200 加空响應体代替 304。狀態碼看起来正常,但蜘蛛仍完成了一次完整的下载流程,节省不了带宽,解析环节還容易拿到空正文。

第三,頁面只改了一個無關紧要的模块,例如推荐位顺序變化,Last-Modified 就整体更新,蜘蛛被反复叫回来,而真正變化的内容並不多。

排查步骤

  1. 先用命令行带條件头請求一次:取出首次响應的 ETag 與 Last-Modified,再带上 If-None-MatchIf-Modified-Since 重發,看能否稳定返回 304。
  2. 连續請求同一 URL 多次,比對两次的响應头是否完全一致;不一致就先定位是哪一层在改寫。
  3. 在多台源站上分別請求,確認所有节点返回同一组值。若不一致,大概率是校驗算法里掺了机器相關字段。
  4. 翻抓取日誌里同一 URL 的响應字节數,如果長期接近頁面完整大小且狀態碼多為 200,說明 304 基本没生效。
  5. 绕過 CDN 直接回源再测一次,区分問题出在源站還是邊缘节点。

調整方向

核心思路是让同一份内容在任意時間、任意节点上都给出稳定且一致的头。

  • ETag 建议只基于内容摘要計算,或干脆去掉自定义值,交给 Last-Modified 负责。
  • Last-Modified 使用内容真實的最後修改時間,不要用渲染時間。
  • 多机部署时统一算法,避免文件名、路径大小寫差异參與計算。
  • 缓存层設定為透传這两個头,不要自行重寫。
  • 確認没有變化就用 304 回應,不要用 200 空体兜底。

观察與驗證

調整之後不要只看一次請求的结果。建议连續观察几天:看同一 URL 重复下载的次數是否下降,响應字节數分布是否向小体积偏移,日誌里 304 的比例是否趋于稳定。資料有起伏是正常的,重要的是趋势,而不是某一天的數字。

條件請求不是抓取優化的開關,它只是把重复下载的成本降下来。真正决定抓取效率的,仍然是入口是否清晰、内容是否有稳定更新,以及服務器能否及时响應。