搜尋抓取

蜘蛛重复抓取同一批 URL:Last-Modified、ETag 與 304 能省下什么

蜘蛛反复訪問同一批 URL 是常態,服務器其實有机會少干活。Last-Modified、ETag 與 304 响應决定了蜘蛛能否跳過重复下载,也影响它把有限的訪問留给新内容。本文讲清這几個头部的實际作用、站点常寫错的地方,以及 CDN 缓存插入後该怎么看日誌。

搜尋抓取

蜘蛛重复抓取同一批 URL:Last-Modified、ETag 與 304 能省下什么

重复抓取是常態,不是浪費

站点跑一段時間後,日誌里出現最多的一類請求,往往是蜘蛛對同一批 URL 的反复訪問。首頁、栏目頁、热门詳情頁,可能一天被走好几遍,而新發布的頁面反而迟迟没人来。這不代表蜘蛛在乱跑,它本来就需要定期回訪,確認内容有没有變化。真正的問题在于:這些回訪里有多少是必须整頁下载的,有多少其實只要回一句“没變”就够了。

站点能控制的部分就在這里。通過缓存校驗头部,让蜘蛛在内容未變时拿到一個很短的 304 响應,而不是重新下载整個 HTML,既能减轻服務器压力,也能让蜘蛛把有限的訪問次數更多分给新 URL。

Last-Modified 與 ETag 各管什么

当蜘蛛再次請求同一個 URL 时,通常會在請求头里带上两個信息:If-Modified-SinceIf-None-Match。前者對應站点上一次返回的 Last-Modified,後者對應上一次返回的 ETag。服務器拿到這两個值後自行判断:内容没變就返回 304,内容變了就正常返回 200 加新内容。

  • Last-Modified:一個時間戳,表示頁面最後一次改動的時間。粒度是秒,够用但不精确,同一秒内的多次修改可能看不出来。
  • ETag:一段标识,通常由文件内容或版本号算出。粒度更细,但必须是稳定的——同样的内容必须算出同样的值。
  • 304:不带响應体,只有头部。抓取工具拿到它就知道可以繼續用本地缓存版本。

两者可以一起用,也可以只用其中一個。只要逻辑自洽,蜘蛛都能處理。

304 與抓取节奏的關系

從站点侧看,304 省的是带宽和渲染開销;從蜘蛛侧看,省的是解析和比對成本。当一個頁面長期不變,反复返回 304,等于告诉蜘蛛“這里暂时不用细看”,它對這類地址的回訪频率往往也會自然調整。反過来,如果站点明明内容没變却每次都返回 200 加完整頁面,蜘蛛每次都得重新讀一遍,抓取资源就被消耗在原地踏步上。

要注意的是,返回 304 並不代表這個 URL 會被忽略。它仍然是一次有效的抓取记錄,只是没有新内容。所以不必担心“返回 304 會不會让蜘蛛觉得頁面不存在”。

容易寫错的地方

  • ETag 每次請求都變。有些框架把進程号、時間戳、随机數混進 ETag,導致每次算出来的值都不一样。這样蜘蛛永遠匹配不上,條件請求形同虚设,甚至可能被判断為頁面频繁變動。
  • Last-Modified 永遠等于目前時間。動態渲染的站点容易出現這個問题,頁面没改,時間戳却一直在跳,蜘蛛每次都會認為是新内容。
  • 時間格式不标准。Last-Modified 需要是标准的 HTTP 日期格式,用本地時間或自定义格式寫,蜘蛛無法解析,等于没给。
  • 返回 304 却带了响應体。這種情况容易引發解析異常,也可能被中間层改寫成 200。
  • 静態资源與 HTML 用了同一套規則。图片、CSS 這類带版本号的资源适合長缓存,HTML 則需要更及时地校驗,混在一起配置容易两头都不對。

缓存层插進来之後

如果站点前面挂了 CDN 或反向代理,蜘蛛看到的 304 可能来自缓存节点,而不是源站。這时候日誌分析會變得模糊:源站看到的是回源請求,节点日誌看到的才是蜘蛛請求。排查时建议分层看——先確認节点是否透传了 If-None-Match,再確認源站是否真的按内容比對。有些配置會把請求头里的校驗字段丢掉,结果就是每個蜘蛛請求都變成一次完整的回源。

另外,缓存节点如果返回的是自己拼的 304,ETag 可能與源站不一致。跨层校驗时,最好對比节点响應头與源站响應头,看两者是否指向同一份内容标识。

一段可落地的检查思路

  1. 挑三個長期不更新的頁面,手動請求一次,记錄返回的 ETag 和 Last-Modified。
  2. 带上這两個值再請求一次,看是否稳定返回 304。
  3. 隔一天再重复一次,確認 ETag 没有随時間漂移。
  4. 對比服務器訪問日誌與應用日誌,確認 304 不只在节点层出現。
  5. 發布新内容後,检查這几個 URL 是否及时從 304 變回 200。
304 的價值不在于让蜘蛛少来,而在于让蜘蛛每次来的时候,都不用為没變化的頁面付出完整代價。把头部寫稳,比一味追求抓取次數更實在。