聊抓取时,大家习惯數條數:今天抓了多少,還剩多少没抓。但同一台服務器、同样的抓取频次,一個頁面用 80 毫秒返回 20KB 的 HTML,另一個用 3 秒返回 900KB,蜘蛛在這两個頁面上花掉的時間完全不是一個量級。抓取能力本质上由時間和带宽决定,頁面越重、响應越慢,單位時間内能走到的 URL 就越少。
先分清三個變量
影响單次抓取開销的,主要不是頁面看起来多复杂,而是下面三件事:
- 首字节時間(TTFB):服務器把响應開头吐出来的耗时。它决定了蜘蛛的等待時間,等待越長,單位時間能處理的請求越少。
- 响應体大小:HTML 源碼本身的字节數。图片和视频不算在内,但内联的样式、脚本、base64 图片都算。
- 连接建立與跳轉:每條 URL 都要重新建连接、完成 TLS 握手、跟随跳轉,這些环节不产出内容,却實實在在占用時間。
慢頁面為什么會牵连同域其他 URL
蜘蛛對同一個站点通常维持有限的並發连接數。当若干條請求卡在慢响應上,這些连接就被占着,後面的 URL 只能排队等着。表現出来就是日誌里某些頁面的抓取間隔被悄悄拉長,而它們自身其實没有任何改動。
需要說明的是,這種影响是相對而非绝對的。短期波動很常见,判断之前最好看一周以上的趋势,而不是盯住某一天的日誌下结论。
頁面体积里最容易膨胀的部分
- 把首屏之外的大量内容一次性渲染進 HTML,長列表頁尤其常见。
- 服務端渲染时把整個狀態對象塞進 script 标簽,内联 JSON 動辄几百 KB。
- 用 base64 内嵌小图标或字体文件。
- 導航、頁脚、推荐位在每頁重复輸出,且结构冗長。
這些内容對用戶未必没有意义,但對抓取来说属于額外传輸量。要處理的是重复和冗余,而不是把有用内容直接删掉。
服務器侧可以做的几件事
- 開啟 gzip 或 brotli 压缩,HTML 的压缩比通常很高,收益最直接。
- 检查資料库慢查询,列表頁的响應時間往往随資料量增長而持續恶化。
- 让静態资源走缓存或 CDN,减少源站同时處理的請求數。
- 减少不必要的重定向,一條 302 就多一個来回,鏈式跳轉更明顯。
- 對分頁、篩選這類高基數頁面,確認它們不會拖垮整站的整体响應。
用日誌量化一下
多數訪問日誌會记錄响應時間和响應体大小。把一段時間内蜘蛛訪問的這两列拉出来,分別算中位數和 P95,比看平均值更有意义——少數几百毫秒的慢請求很容易把平均值带偏。你會看到一些規律:响應体排在前面的 URL,往往集中在列表頁和詳情頁;响應時間異常的,則常常是同几個接口或同一段模板。
顺着這两组資料往下找,通常比漫無目的地翻代碼更快定位問题。
目标是让重要頁面的响應時間稳定在一個区間,而不是追求某個漂亮的數字。抓取效率的提升大多来自消除異常,而不是把每個頁面都压到极限。
哪些頁面值得優先减重
不是所有頁面都值得投入優化。優先處理的應该是:内鏈指向多、承载大量 URL 發現的列表頁和分類頁;被频繁重訪的首頁與频道頁;以及本身就要輸出大量结构化資料的頁面。這些頁面的响應時間改善,會顺着抓取路径放大到整個站点的抓取节奏上。
不要為了抓取牺牲頁面本身
把 HTML 做小是好事,但如果代價是首屏内容缺失、必须靠 JavaScript 补齐,渲染环节反而要多花一筆時間,得不偿失。比較合理的顺序是:先修慢查询和多余跳轉,再压缩响應体,最後才考虑结构調整。抓取效率只是一個侧面,頁面本身的可用性不能因此打折。