網站收錄

正文由 JavaScript 渲染:抓取阶段拿到的 HTML 可能只是個空壳

很多頁面在浏览器里看着内容完整,但搜尋引擎抓取时拿到的原始 HTML 里正文是空的,原因多是内容靠 JavaScript 在客戶端渲染。本文說明抓取與渲染的区別、哪些寫法最容易出問题、怎么用查看源代碼和禁用 JS 自查,以及服務端渲染、预渲染、静態生成的适用场景。

網站收錄

正文由 JavaScript 渲染:抓取阶段拿到的 HTML 可能只是個空壳

很多站点在浏览器里看着内容很完整,但搜尋引擎抓取时拿到的第一版 HTML 里,正文位置是空的。原因通常是頁面主体靠 JavaScript 在客戶端渲染出来。這不必然導致不收錄,但會让收錄多一道不确定性。

抓取时拿到的,是服務器返回的原始 HTML

搜尋引擎處理一個 URL,大致分两步:先抓取,拿到服務器返回的 HTML 源碼;再决定是否执行頁面里的 JavaScript,把渲染後的结果作為补充。第一步几乎每個頁面都會有,第二步要看资源、看優先級、看頁面本身的重要程度。

如果标题、正文、主要連結都靠 JS 生成,那么抓取這一步拿到的就是一個空壳。渲染队列和抓取队列不是同一件事,渲染更慢,也更挑頁面。结果往往是:頁面被抓過很多次,索引里的内容却始终不完整,甚至進不了索引。

渲染是有成本的,別当成預設會發生的事

搜尋引擎确實會渲染 JavaScript,但渲染资源有限,它更愿意把机會给重要、有獨特内容、有外部連結指向的頁面。一個栏目下的第两百個商品頁,或者几乎没人訪問的詳情頁,被渲染的概率就低很多。

所以不要把"反正搜尋引擎會渲染"当成方案。能放在初始 HTML 里的内容,就放在初始 HTML 里。

這几類寫法最容易出問题

  • 單頁應用:整站路由由前端接管,直接訪問某個内頁时,服務器返回的是同一個空模板。
  • 無限滚動:列表靠滚動加载,初始 HTML 里只有第一屏的前几條。
  • 選項卡與折叠面板:預設只加载目前選中項,其余内容在点击後才請求。
  • 评论、問答、分頁列表:靠加载更多按钮触發,第一頁之外的連結不出現在 HTML 里。
  • 用 onclick 或 JS 跳轉代替 a 标簽:蜘蛛看不到連結,URL 發現路径就断了。
  • canonical、robots meta、hreflang 由 JS 插入:這類指令需要在初始响應里就能讀到。

自查:確認抓取到的版本里有没有内容

  1. 打開頁面,查看網頁源代碼(不是审查元素),搜尋正文里一句獨特的话。
  2. 如果源碼里搜不到,說明這段内容在初始 HTML 中並不存在。
  3. 禁用 JavaScript 再打開一次,看還剩多少内容。
  4. 在搜尋後台的 URL 检查工具里看抓取到的 HTML,與源代碼對照。
  5. 检查主要導航和内鏈,是否都是可点击的 a href。

如果源碼里既没有正文,也没有内鏈,基本可以判断這個頁面在抓取阶段的信息量很低。

修的时候,按優先級来

第一優先,让核心正文出現在初始 HTML 里,哪怕只是纯文本。搜尋引擎要先讀到内容,才谈得上判断质量。

第二優先,把主要内鏈寫成真正的 a 标簽。URL 發現靠連結,如果連結只存在于 JS 事件里,蜘蛛找不到下一层頁面。

第三優先,canonical、robots、hreflang 等指令放到服務端輸出的 HTML head 里,不要靠 JS 動態寫。

第四優先,图片懒加载给一個 src 或 noscript 兜底,避免图片资源完全抓不到。

服務端渲染、预渲染、静態生成怎么選

  • 内容更新不频繁的頁面,如文档、商品詳情、文章,用静態生成或服務端渲染,成本最低,效果最直接。
  • 已有前端框架的項目,可以只對内容頁做服務端渲染,交互部分保留客戶端渲染。
  • 预渲染适合頁面數量不大、更新不频繁的站点,在构建时生成静態 HTML。
  • 無限滚動和加载更多,最好同时提供可抓取的分頁連結,让後續内容有獨立 URL。
渲染能力在提升,但它是补充,不是可以長期依赖的兜底。把内容放在初始 HTML 里,是成本最低、也最稳的做法。

判断一個頁面能不能顺利進入索引,先看蜘蛛拿到的那份 HTML 里有什么。用戶看到的是渲染後的结果,搜尋引擎看到的第一眼往往是原始源碼。两者差距越大,收錄的不确定性就越高。先把内容搬回源碼里,再谈其他優化。