搜尋抓取

304 與條件請求:让蜘蛛別重复下载没變過的頁面

蜘蛛每次抓取都完整下载頁面,大部分時間其實是重复劳動。本文說明條件請求(ETag、Last-Modified、If-None-Match)如何让服務器用 304 告知内容未變,减少传輸量與服務器压力;也列出 ETag 每次都變、Last-Modified 永遠取目前時間、CDN 不透传等常见問题,並给出用 curl 與日誌自查的方法。

搜尋抓取

304 與條件請求:让蜘蛛別重复下载没變過的頁面

蜘蛛每天都會来,但你的頁面大部分時間並没有變化。如果每次訪問都完整返回一遍 HTML,带宽、服務器處理時間和抓取時間就花在了重复内容上。HTTP 的條件請求机制可以把這部分省下来:服務器只回一句“没變”,蜘蛛就不用再下载正文。

條件請求是怎么工作的

蜘蛛第一次抓取某個 URL 时,服務器可以在响應头里给出两個标识之一:ETag(内容的指纹)或 Last-Modified(最後修改時間)。下次再来,蜘蛛會带上 If-None-Match 或 If-Modified-Since,把上次拿到的值發回来。服務器比對後如果認為内容没變,就返回 304 Not Modified,响應里不带正文。

  • 200:内容有變化,或服務器無法判断,返回完整頁面。
  • 304:内容没變,蜘蛛沿用本地已有的版本。

它對抓取的實际影响

304 不會让蜘蛛“少来”,它仍然會訪問這個 URL,仍然占用一次請求。省下的是這次請求的传輸量,以及服務器重新渲染、序列化頁面的成本。当站点 URL 數量大、更新频率低时,這個差別會累积:服務器响應更轻,蜘蛛在同样的抓取時間预算里走得更顺。

反過来说,如果每個頁面每次都要重新生成、重新传一遍完整 HTML,响應時間和带宽都压在服務器上,抓取高峰期更容易出現超时和 5xx。

几個容易踩的坑

  • ETag 每次都變。有些框架用進程 ID、時間戳或文件 inode 生成 ETag,同一個文件两次請求得到两個不同值。蜘蛛每次都判定内容變了,永遠拿 200,條件請求形同虚设,還多了一次無效比對。
  • Last-Modified 永遠是目前時間。模板里直接輸出 now(),蜘蛛看到“刚刚修改過”,于是反复重抓。
  • 頁面里有易變内容。頁脚時間、随机推荐位、訪問計數器,會让内容指纹每次都不同。這類元素要么改為异步加载,要么在服務器端缓存固定。
  • CDN 或反向代理把头剥掉。源站给了 ETag,邊缘节点没有透传,蜘蛛拿到的响應里就没有可用标识。
  • 错誤的 304。内容确實更新了却返回 304,蜘蛛會繼續用舊版本,新内容迟迟進不了索引。這類問题排查起来很費時間。

怎么检查和配置

  1. 用 curl 连續請求同一個 URL 两次,第二次带上第一次返回的 ETag 或 Last-Modified,看是否返回 304。
  2. 翻服務器日誌,統計全部請求與蜘蛛請求里 304 的占比。占比長期接近零,說明條件請求基本没生效。
  3. 確認源站配置正确後,再检查 CDN 是否透传 ETag 和 Last-Modified。
  4. 對确實频繁變動的頁面,不要硬造 304,保持 200 更安全。
條件請求省的是重复传輸,不是抓取次數。把它当成服務器侧的减负手段,而不是用来控制蜘蛛来不来的開關。

和 Sitemap 里的 lastmod 不是一回事

Sitemap 中的 lastmod 是你在清單里声明的時間,属于主動匯报;响應头里的 ETag 與 Last-Modified 是每次請求时的現场比對,属于實时確認。两者可以配合,但不能互相替代:lastmod 寫得不准,最多是判断參考失真;304 判断错了,蜘蛛可能長期停留在舊版本上。

小结一下:把 ETag 和 Last-Modified 配對好,让没變的頁面老實返回 304,让變了的頁面老老實實返回 200,是抓取效率里成本很低、收益却比較稳的一环。