蜘蛛重复訪問同一個入口頁时,服務器並不總是把整頁内容重新發一遍。HTTP 协议里的條件請求机制,让蜘蛛可以先問一句“我上次拿到的那份還作數吗”,服務器用 304 回答“没變”,双方都省流量。這套机制原本是给浏览器设計的,但在蜘蛛池场景下,它同样在悄悄影响抓取节奏。
條件請求是怎么發生的
蜘蛛在第一次抓取入口頁後,會记錄响應头里的 Last-Modified 或 ETag。下次抓取时,它把這些值放進請求头的 If-Modified-Since 或 If-None-Match。服務器比對後返回两種结果:内容没變就返回 304,不带正文;變了就返回 200 加新正文。
對蜘蛛来说,304 不是坏事。它說明連結還活着、頁面结构稳定,只是内容没更新。真正需要担心的是另一種情况:内容明明改了,服務器却因為缓存配置错誤一直返回 304,蜘蛛就會長期停留在舊版本上。
Last-Modified 和 ETag 分別管什么
- Last-Modified 基于文件修改時間,精度到秒。静態頁面、直接由文件系統吐出的入口頁用它最省事。缺点是文件被重新生成但内容一样时,時間戳變了,蜘蛛會重新下载一份完全相同的内容。
- ETag 是内容的指纹,理论上更精确。但很多服務器預設用“inode + 修改時間 + 大小”生成 ETag,多台机器之間不互通,蜘蛛在负载均衡的站点上會看到 ETag 反复變化,结果每次都是 200 全量返回。
两者同时存在时,蜘蛛通常優先使用 ETag。如果 ETag 不稳定,不如直接關掉它,只留 Last-Modified。
Cache-Control 不是给蜘蛛看的,但蜘蛛會看
Cache-Control: max-age 主要约束浏览器和中間缓存。蜘蛛一般不完全遵守它,但如果設定成很長的 max-age 又配合 CDN,中間层可能把舊内容一直吐给蜘蛛。入口頁這類需要反映最新狀態的頁面,比較稳妥的做法是不给過長的缓存時間,或者让 CDN 對爬虫 User-Agent 回源。
缓存的目标是减少重复传輸,不是让蜘蛛看不到更新。這两件事经常被混為一谈。
入口頁内容频繁改動时怎么办
如果入口頁每隔几小时就換一次推荐位或時間戳,條件請求的意义就不大,蜘蛛几乎每次都會拿到 200。這種情况下更值得關注的是改動本身有没有價值:正文主体不變、只變頁脚年份,對蜘蛛来说等同于没變,反而浪費一次抓取。
真需要频繁更新的頁面,可以只更新局部内容並保持 Last-Modified 稳定,避免因為無意义的响應头抖動引發蜘蛛高频回訪。
几個常见誤区
- 给所有响應套用固定 ETag 模板,導致大量頁面指纹雷同,蜘蛛判断混乱。
- 反向代理和後端各自加一层缓存头,最终輸出的 Last-Modified 比實际内容還新。
- 入口頁返回 304 却同时带了一段說明文字,正文長度和狀態碼不一致。
- 為了让抓取日誌好看,人為让頁面永遠返回 200,抓取量上升了,重复内容比例也一起上升。
自查步骤
- 用 curl -I 连續請求两次入口頁,確認第二次返回的 Last-Modified 或 ETag 與第一次一致。
- 在請求头里带上 If-None-Match,確認服務器能正确返回 304。
- 改動頁面正文後再請求一次,確認返回 200 且 ETag 已更新。
- 換一台机器或換一個出口 IP 請求,检查 ETag 是否仍然相同。
- 對照抓取日誌,看蜘蛛對同一入口頁的狀態碼分布,判断是 200 偏多還是 304 偏多。
缓存配置属于那種平时不出問题、出問题就很难排查的环节。把入口頁的响應头保持稳定、可预测,比追求某種所谓的最優設定更實际。