抓取日誌里有时會出現同一 URL 在短時間内返回不同内容、不同長度,甚至不同狀態碼的情况。排除源站更新因素後,剩下的一類常见原因来自 CDN 或反向代理的缓存层:一部分請求命中了邊缘缓存,另一部分請求穿透到源站,两邊拿到的版本不一致,抓取结果自然就對不上。
缓存层為什么會让抓取结果不一致
缓存命中的判断依據是缓存键。多數 CDN 預設以完整 URL 作為缓存键,但也可以把 Host、查询參數、請求头(如 Accept-Encoding、User-Agent)、Cookie 纳入或排除。只要這些規則和源站的實际内容逻辑不匹配,就會出現同一個頁面被拆成多個缓存條目,或者本應区分的版本被合並成一個。
- 缓存键包含查询參數:带跟踪參數的 URL 單獨成條目,命中率下降,回源次數上升。
- Vary 头設定過宽:例如 Vary: User-Agent,會让每個 UA 各存一份,蜘蛛與普通浏览器看到的内容可能来自不同副本。
- Set-Cookie 触發不缓存:源站對所有响應下發 Cookie,邊缘节点可能直接跳過缓存,回源压力集中到源站。
- TTL 與更新节奏错位:内容已更新但缓存仍返回舊版本,抓取到的仍是歷史頁。
- 节点差异:不同地区、不同运营商节点命中情况不同,同一 URL 的抓取结果不一致。
從日誌和响應头入手排查
排查的第一步是把缓存狀態變成可观测的信息。多數 CDN 會在响應头里寫入缓存命中标记與缓存时長,例如 X-Cache、Age、CF-Cache-Status 之類的字段,源站日誌里也可以记錄回源来源。把這些字段和抓取记錄放在一起看,命中與回源的分布會立刻清晰起来。
- 用固定 UA 和随机 UA 分別請求同一 URL,對比响應头中的缓存标记與正文長度。
- 观察 Age 头的增長情况,判断缓存是否為新鲜副本,還是長期未刷新的舊副本。
- 检查 Vary、Cache-Control、Set-Cookie 三個响應头,確認缓存策略和源站预期是否一致。
- 抽样對比邊缘命中响應與直接回源响應,逐字节核對關键区块,例如價格、库存、正文開头。
- 在抓取日誌中按入口聚合,找出反复出現“命中—回源”交替的 URL,這類入口通常缓存策略最不稳定。
几類高频問题與處理方向
缓存長期返回舊内容
内容更新後没有主動刷新缓存,或者刷新只覆盖了部分节点,抓取就會讀到舊版本。可以按内容類型区分 TTL:列表頁、首頁這類更新频繁的頁面設定較短的缓存时長,詳情頁、静態资源可以放長一些,内容發布时對相關 URL 做一次定向刷新。
回源過于集中導致源站抖動
缓存命中率低时,抓取請求會大量落到源站,源站在峰值时段可能出現响應變慢甚至 5xx。此时應優先检查缓存键是否被參數撑開、是否因為 Cookie 或 Vary 被整体跳過缓存,而不是先考虑压缩抓取速率。
不同节点表現不一致
如果只有部分地区的节点命中異常,需要確認是节点预热未完成,還是该区域的回源线路本身不稳定。可以在抓取日誌中按時間段對比命中率,配合节点的刷新记錄定位。
調優时的几個取舍
- 缓存键尽量稳定,避免把與内容無關的請求头纳入計算。
- 除非确實存在按 UA 分發不同内容的逻辑,否則不要让 Vary: User-Agent 影响缓存。
- 對爬虫 UA 不要單獨放行到源站,這會让缓存层形同虚设。
- 内容更新後的刷新范围和 TTL 要配套,只改其中一個往往解决不了問题。
- 把缓存命中率和源站响應時間一起看,單看其中一個指标容易誤判。
缓存命中率高並不等于抓取正常。判断标准應该是邊缘返回的内容與源站目前内容是否一致,而不是命中比例本身。
缓存层是抓取路径上容易被忽略的一环,它既可能帮站点扛住抓取压力,也可能因為策略配置不一致,让蜘蛛看到和用戶不一样的頁面。把响應头的缓存信息纳入日常观察,遇到抓取内容異常时先分清“命中版本”和“回源版本”,排查方向會明确很多。