蜘蛛第一次抓走頁面之後,還會再来。它再来的时候,服務器其實有机會说一句:這個頁面没變,別下载了。這套机制就是條件請求(Conditional Request)。用對了能减少传輸和解析開销,用错了會让蜘蛛反复拉取同一份内容,甚至拿到過期的判断。
蜘蛛再来时的两次握手
蜘蛛第二次訪問某個 URL 时,如果之前拿到過缓存信息,請求头里可能带上其中之一:
- If-Modified-Since:值是上次响應里的 Last-Modified 時間。
- If-None-Match:值是上次响應里的 ETag 标识。
服務器比對之後给出两種结果:内容没變,返回 304 Not Modified,不带响應体;内容變了,返回 200 和完整頁面。對蜘蛛来说,304 意味着它不用重新下载和解析正文,可以直接沿用已有内容。
Last-Modified 常见的失真来源
Last-Modified 看起来最简單,也最容易寫错。動態站点里常见的几種情况:
- 每次請求都輸出目前時間,等于告诉蜘蛛“刚刚改過”,蜘蛛只能每次完整下载。
- 内容存在資料库里,文件本身没動,Last-Modified 停留在很早以前,蜘蛛會誤判頁面長期不變。
- CDN 回源時間被当成资源修改時間,源站更新後缓存层没刷新,時間對不上。
如果站点用的是静態文件加 Nginx,預設的 Last-Modified 来自文件修改時間,通常比較可靠;一旦生成逻辑是動態拼接,就需要手動维護一個真實的“内容更新時間”。
ETag 在多节点环境里更容易出错
ETag 的理论效果比 Last-Modified 更精确,問题出在一致性上:
- 多台後端各自生成 ETag,值里含 inode、進程号或本地時間戳,同一 URL 在不同机器上返回不同 ETag,蜘蛛會認為内容一直在變。
- 反向代理、负载均衡或某些中間层會改寫甚至丢弃 ETag,請求里的 If-None-Match 传不到後端。
- 頁面每次响應都带随机推荐位、時間戳或一次性 token,正文没變但字节變了,ETag 自然每次都不同。
- 部分服務器對压缩前後的内容分別計算 ETag,gzip 與 br 之間切換时會来回變化。
304 能不能省下抓取预算
需要说清楚一点:從蜘蛛的视角看,304 依然算一次對 URL 的抓取。它节省的是带宽、服務器 CPU 和解析時間,並不直接換来更多抓取次數。所以不要指望靠 304 提升抓取频率,但把它做對,确實能减轻服務器压力,也让蜘蛛更快走完一轮 URL。真正决定蜘蛛来不来的,還是 URL 是否可發現、服務器是否稳定、頁面是否有更新價值。
可落地的自查顺序
- 正常抓一次目标頁面,记錄响應头里的 Last-Modified 與 ETag。
- 間隔几分钟再抓,手動带上 If-None-Match 或 If-Modified-Since,观察是否返回 304。
- 換一台服務器或換一個 CDN 节点重复同样的請求,比較 ETag 是否一致,這一步最容易暴露集群問题。
- 修改頁面内容後立刻請求,確認返回的是 200 且带新正文,而不是 304。
- 检查中間层是否吞掉了條件請求头,可以對比源站直连與经過 CDN 後的响應差异。
條件請求是一項减少浪費的優化,不是抓取策略的核心。先保證 URL 能被發現、服務器能稳定响應,再考虑用 304 把重复传輸压下来,顺序反了收益很有限。
如果你的站点頁面更新频率很低,做對條件請求的收益會比較明顯;如果内容频繁變動,重点則應该放在让 Last-Modified 反映真實更新,而不是為了凑 304 而保留過时的時間戳。