蜘蛛重复抓取时,带的是一個“我上次见過”的凭證
搜尋引擎爬虫在第二次、第三次抓取同一個 URL 时,通常會在請求头里带上 If-Modified-Since 或 If-None-Match。前者對應上一次响應里的 Last-Modified,後者對應 ETag。如果服務端判断“内容没變”,就返回 304 Not Modified,不带响應体。這套机制原本是為了省流量,但放在蜘蛛池入口頁的场景里,它同时在向蜘蛛传递一個信号:這個頁面還是老样子。
問题就在這里。带宽是省了,但如果你的入口頁其實換過内容、加過連結,而服務端仍然回 304,蜘蛛不會重新解析頁面,新加的 URL 也就失去了被發現的机會。所以條件請求不是一個“開了就好”的預設項,需要按入口頁的性质来决定。
ETag 與 Last-Modified 各自代表什么
- Last-Modified:一個時間戳,表示资源最後修改時間。取值應该来自内容真正變更的时刻,而不是本次請求的时刻。
- ETag:一個标识串,代表资源的某個版本。可以是内容摘要、版本号,也可以是文件元信息,只要保證“内容變則值變、内容不變則值不變”。
- 两個都提供时,多數服務端和客戶端會優先比對 ETag。只提供其中一個也能工作,但要注意一致性。
入口頁常见的几個配置坑
1. Last-Modified 用了目前時間
有些程序在每次輸出頁面时把 Last-Modified 设為目前時間,结果蜘蛛每次带回来的 If-Modified-Since 都“過期”,永遠拿不到 304。表面看是抓取很勤快,實际上是靠無意义的全量响應換来的,带宽和维護成本都上去了。更糟的是,如果反向代理把這個時間缓存下来,時間戳會變得毫無參考價值。
2. ETag 里带上了机器指纹
常见的預設實現是把文件的 inode、大小、修改時間拼成 ETag。在多机部署的蜘蛛池里,不同机器上同一份文件的 inode 往往不同,于是同一個 URL 在不同节点返回不同 ETag。蜘蛛拿到 A 节点的 ETag,下次請求落到 B 节点,就會一直判定為“有變化”,條件請求形同虚设,還可能让抓取预算浪費在重复内容上。稳妥的做法是基于内容摘要生成 ETag,或者干脆在多台机器上關掉自動 ETag,改用统一的版本号。
3. 動態渲染導致 ETag 每次都不同
入口頁里如果混入了随机广告位、實时時間、随机排序的推荐列表,内容摘要每次都不一致,ETag 也就每次都變。這類元素對蜘蛛没有價值,却會让 304 永遠不出現。處理办法是把這些動態片段剥离出去,让 spider 看到的入口頁正文部分保持稳定。
304 不是越多越好,也不是越少越好
入口頁大致可以分两類来看。一類是長期不變、只做連結中轉的静態頁,這類頁面适合正确啟用條件請求,让蜘蛛用最低成本確認“還活着”,把带宽留给真正需要抓取的内容。另一類是经常調整連結和文案的运营型入口頁,更新时一定要让 Last-Modified 和 ETag 跟着變,否則蜘蛛根本没有理由重新解析。
另外要提醒一点:條件請求影响的是抓取阶段的带宽和判断依據,和頁面最终是否被索引、能否获得排名没有直接因果關系。回 304 不代表頁面健康,回 200 也不代表會被收錄,两件事不要混在一起看。
一份可执行的检查清單
- 用 curl -I 看响應头,確認 Last-Modified、ETag、Cache-Control 是否都存在,值是否合理。
- 带上 If-None-Match 再請求一次,確認内容未變时确實返回 304。
- 多节点各請求一次,比對头部是否一致;不一致就改用内容摘要計算 ETag。
- 手動改一次入口頁内容,再带舊 ETag 請求,確認返回 200 而不是 304。
- 检查頁面里是否存在随机時間、随机排序等會让内容摘要抖動的内容,有就剥离。
- 把入口頁的响應头配置寫進部署脚本或模板,避免換服務器时配置漂移。
條件請求的正确用法不是“让蜘蛛少下载”,而是“让蜘蛛下载得有意义”。带宽省下来的部分如果換来的是更新信号丢失,那這筆帳是亏的。