做蜘蛛池的人,注意力大多放在入口頁的内容差异、互鏈结构和數量規划上,却容易忽略一個更底层的問题:爬虫請求那個 URL 时,返回的 HTML 到底是谁生成的。如果入口頁前面挂了 CDN、反向代理或者頁面缓存,爬虫拿到的很可能不是你以為的那一版。
為什么缓存會改變抓取结果
爬虫是按 URL 取頁面的。中間只要存在缓存层,返回内容就可能来自缓存副本,而不是源站實时渲染的结果。對蜘蛛池来说,入口頁通常是批量生成、批量調整的,模板、锚文本、内鏈指向可能一天改好几轮。一旦缓存 TTL 设得很長,爬虫今天、明天、一周之後看到的都是同一份快照,你在源站做的更新動作等于打折执行。
更麻烦的是,缓存本身不是错誤配置,它在正常场景下就是提升响應速度的手段。問题在于入口頁的更新节奏,和缓存的有效期不匹配。
三種缓存形態,影响程度不一样
- 浏览器缓存:主要影响真實用戶,對爬虫的影响相對小,因為爬虫往往不带本地缓存。但如果响應头寫成强缓存,某些抓取工具复用连接时也可能讀到舊内容。
- 邊缘节点缓存:影响最大。回源频率由节点决定,各地节点回源時間点不同,就可能出現同一個 URL 在不同节点上是不同版本。
- 服務端頁面缓存或對象缓存:源站内部先缓存一份渲染结果,只要缓存键设計不合理,入口頁之間的差异内容可能被串用,出現 A 頁顯示 B 頁锚文本的情况。
真正影响爬虫的那几個响應头
- Cache-Control:重点看 max-age、s-maxage、no-cache、no-store、private。s-maxage 决定共享缓存(含 CDN)的有效期,常被忽略。
- Expires:老式寫法,和 Cache-Control 同时存在时以 Cache-Control 為准,但两邊冲突會让人誤判。
- ETag 與 Last-Modified:用于條件請求,能让回源變轻,但對内容是否為最新没有保證。
- Vary:寫成 Vary: User-Agent 时,不同 UA 會被分配到不同缓存副本,入口頁内容一致性很难保證。
- Age 與命中标记:Age 大于 0 基本可以判断這次返回来自缓存,排查时比看内容更直接。
同一 URL,不同节点版本不一致
入口頁批量更新後,如果没有主動刷新,常见現象是:一部分节点已经回源拿到新版本,另一部分還在提供舊版本。爬虫從不同出口 IP 請求,就有可能交替拿到新舊两份内容。表現出来就是抓取记錄不连贯、锚文本對不上、頁面摘要前後矛盾。
如果入口頁需要保持内容一致,這類情况比單纯的内容陈舊更难排查,因為源站自己测是正常的。
入口頁更新與缓存刷新的配合
- 入口頁單獨一套缓存策略,不要和目标頁共用同一组規則。
- 批量生成或批量改模板後,主動按路径刷新,而不是等 TTL 自然過期。
- TTL 建议從短開始,比如几分钟到几小时,观察回源压力和抓取情况再决定是否放宽。
- 需要在更新後马上被讀到的内容,用短 TTL 加主動刷新组合,比把 TTL 全關掉更稳。
一個常用排查流程
- 用 curl -I 直接看响應头里的 Age 和命中标记,先確認是否命中缓存。
- 換不同 UA、不同出口 IP 請求同一個 URL,對比返回内容與 Age。
- 检查是否有 Vary: User-Agent 這類容易造成多副本的寫法。
- 對比源站直连返回内容與节点返回内容,定位差异發生在哪一层。
常见誤区:以為改了源站内容,爬虫下次来就一定能看到。實际决定權在缓存策略和刷新動作上,源站改了但缓存没動,爬虫看到的仍然是舊版本。
几点落地建议
- 不要把缓存键里塞入随机參數,否則每個 URL 都是獨立副本,既失去缓存意义,也让爬虫反复請求相同内容。
- 入口頁的更新動作和缓存刷新動作,尽量在同一個流程里完成,避免只有一邊执行。
- 如果入口頁需要長期稳定不變,可以放宽 TTL;如果需要频繁調整,就保持短 TTL。
- 缓存命中带来的响應速度提升,對抓取节奏是正向的,但内容陈舊會削弱爬虫再訪的意愿,两者要一起權衡。
缓存不是收錄的開關,但它會让你的更新動作打折扣。把缓存策略和入口頁的更新节奏對齐,往往是投入最小、见效最直接的一步。