很多人在日誌里會看到入口頁的抓取记錄,狀態碼是 304,响應字节數很小,于是會疑惑:蜘蛛到底有没有讀到頁面里的目标連結。這篇就围绕 304 這個狀態碼,说清楚它和 URL 發現之間的關系,以及该检查哪些地方。
304 是怎么产生的
搜尋蜘蛛重复抓取一個已经抓過的頁面时,往往不是直接要一份完整頁面,而是先做條件請求:带上 If-Modified-Since(基于上次响應的 Last-Modified)或 If-None-Match(基于上次响應的 ETag)。服務器比對之後,如果認為内容没有變化,就返回 304 Not Modified,响應体通常是空的。
對服務器来说這是省流量的设計;對爬虫来说,它也确實省下了一次下载。但代價是:蜘蛛手里這份頁面,仍然是上一次抓取时的那一份。
為什么這會影响到目标 URL 的發現
搜尋蜘蛛發現新連結,靠的是解析 HTML 里真實的 a 标簽。如果這次抓取拿到的只是 304,没有新的 HTML,解析這一步就不會重新做一遍。结果就是:
- 入口頁里新增的目标連結,這次抓取不會進入待抓队列;
- 入口頁里已经删掉的目标連結,也不會因為這次抓取而被移除;
- 從日誌上看入口頁“被抓了”,但目标 URL 的抓取记錄並没有随之增加。
需要說明的是,這不代表連結永遠發現不了,只是這一轮抓取没有带来新信息。下一次頁面真的有變化並返回 200 时,才有机會被重新解析。
哪些情况會让入口頁“该變却返回 304”
正常的 304 是内容真的没變。容易出問题的是下面几類:
- 入口頁是動態生成的,但 ETag 或 Last-Modified 寫死了,内容換了校驗值却没換;
- 前面挂了 CDN 或反向代理,缓存規則把更新挡住,蜘蛛拿到的還是舊响應头;
- 程序里為了让爬虫“少抓几次”而手動返回 304,但頁面里的連結列表其實已经更新;
- 多個入口頁共用同一套静態文件,只改了其中一部分,校驗值却没有重新生成。
怎么確認問题出在 304 上
可以按顺序排查:
- 先记錄一次正常抓取的响應头,拿到 Last-Modified 和 ETag 的原值;
- 用命令行带上 If-None-Match 或 If-Modified-Since 重新請求,看返回的是 304 還是 200;
- 改一處入口頁的連結,再重复上一步,如果仍然返回 304,說明校驗值没有跟着内容變;
- 對照服務器日誌里的狀態碼、响應字节數和抓取時間,確認蜘蛛拿到的是空响應還是完整頁面。
更稳妥的處理方式
- 让校驗值跟着内容走:内容變了,ETag 和 Last-Modified 就應该變,不要手工固定;
- 連結列表有增删时,優先保證這一次返回 200 與完整 HTML,而不是返回 304 省流量;
- 入口頁本身更新频率不高的话,不必為了發現新 URL 频繁改動,新 URL 也可以通過其他入口頁或站点地图补上;
- 別把 304 当成優化手段去刷,返回 304 不等于蜘蛛會顺着缓存里的連結繼續抓。
304 的意思是“和上次一样”,它省的是流量,不會帮入口頁传递新的連結信息。
最後提醒一点:入口頁被抓取、目标 URL 被重新解析,都不等于目标頁一定會被收錄。抓取、解析、入索引是三個环节,能調整的只是前面這些技術细节,後面的判断仍然在搜尋引擎手里。