一個常见現象:URL 已知,内容却迟迟不来
在服務器日誌里经常能看到這種情况:某個新 URL 在几小时内就被請求了一次,但返回的 HTML 几乎是空壳,正文全靠 JS 渲染。此後這只 URL 長時間没有第二次請求,收錄自然也無從谈起。這不是蜘蛛没發現 URL,而是發現 URL和拿到可用内容被拆成了两步。
發現與渲染是两個獨立的队列
抓取大致包含两類動作:一類是爬取原始响應,用于提取連結、判断頁面是否值得繼續處理;另一類是把頁面放進渲染环境,执行脚本後再讀取最终 DOM。前者通常較快,後者依赖渲染资源與計算能力,排队時間往往更長。
于是頁面在第一次被抓取时,只贡献了連結和少量文本。蜘蛛需要等到渲染队列轮到它,才能看到完整内容。這個間隔没有公開的固定值,短則數小时,長則更久;期間站点如果频繁改動,還可能拿到過渡狀態的版本。
哪些頁面更容易進入渲染队列
- 首屏内容由前端框架在客戶端請求接口後拼装,初始 HTML 里没有正文。
- 正文依赖用戶交互才展開,比如点击标簽、滚動懒加载。
- 關键内容被脚本按條件插入,例如按 UA 或地区判断後再渲染。
- 頁面依赖大量第三方脚本,渲染环境需要反复等待超时。
- 列表頁的連結由 JS 生成,初始 HTML 中不包含任何指向詳情頁的 a 标簽。
這几類頁面的共同点是:原始 HTML 對蜘蛛的信息量很低,几乎所有判断都被推到了渲染阶段。問题是渲染资源永遠比抓取资源紧張,頁面越多,平均等待就越長。
把關键内容留在初始响應里
服務端渲染或静態化不是為了讨好蜘蛛,而是让内容在第一轮請求中就可讀。做不到全站 SSR 时,可以按優先級處理:
- 先保證詳情頁的正文、标题、發布時間在初始 HTML 中完整輸出。
- 确保分頁、列表頁和導航里的連結是服務端輸出的真實 a 标簽,而不是脚本拼接出来的。
- 把影响理解的结构化資料也放進初始 HTML 或獨立 JSON-LD。
- 對必须依赖接口的模块,考虑在服務端预取資料後注入首屏。
- 保留一份不依赖 JS 的降級视图,至少让核心内容可讀。
驗證渲染造成的时滞
判断問题是否出在渲染,可以用几種简單方式對照:
- 關閉 JavaScript 抓取頁面,看初始 HTML 里還剩多少正文和連結。
- 比對日誌中同一 URL 的多次請求,观察後續請求是否携带渲染特征,例如更長的响應時間、不同的资源請求序列。
- 把服務器日誌與站点自身清單對帳,找出被抓過却没有後續渲染請求的頁面。
- 记錄新内容的首次出現時間與内容完整時間,两者的差值就是渲染时滞的粗略下限。
几個容易踩的坑
把渲染当成萬能解法,往往會掩盖更基础的問题:連結不可達、HTML 结构混乱、内容必须交互才出現。
- 用脚本生成連結並期待被抓到,等于把 URL 發現也交给了渲染队列。
- 在渲染时才决定 canonical 或 noindex,蜘蛛很可能先看到另一個版本。
- 把重要内容放在异步加载的第二屏,渲染過程也可能不触發。
渲染是补充手段,不是抓取的起点。让初始 HTML 承载尽可能多的信息和連結,渲染队列的压力才會下降,從 URL 被發現到内容被理解之間的空档也會随之缩短。至于最终是否收錄、何时收錄,仍由搜尋引擎自行判断,站点能做的是减少那些本不必要的等待。