搜尋抓取

頁面靠 JavaScript 渲染时,蜘蛛的抓取路径會怎么走

当頁面由 JavaScript 渲染时,蜘蛛拿到的初始 HTML 往往和用戶看到的並不一样。渲染要排队、要加载资源,任何一步受阻,停在脚本里的連結就不會被發現。本文梳理蜘蛛從下载 HTML 到执行脚本的路径,並给出让内鏈在两種狀態下都可见的做法。

搜尋抓取

頁面靠 JavaScript 渲染时,蜘蛛的抓取路径會怎么走

前端框架普及之後,服務器返回的 HTML 常常只剩一個空容器,正文和連結都要等 JavaScript 跑完才出現。蜘蛛在這方面比以前進步很多,但它走的顺序和用戶不同:先下载 HTML,再去排队执行脚本,中間任何环节出問题,停在脚本里的連結就不會進入下一步。

第一份 HTML 决定第一次分叉

蜘蛛請求一個 URL 时,最先拿到的是服務端直接返回的源碼。如果導航、栏目入口、詳情頁連結都寫在這份源碼里,路径在這一层就铺開了;如果它們全部由 JS 在浏览器端生成,第一份源碼里就是空的,蜘蛛只能等渲染。等待不是問题,問题是等待有成本,而且不保證成功。

渲染是排队發生的,不是同步的

渲染阶段的执行依赖不少外部條件:

  • 渲染用的脚本和样式如果被 robots.txt 屏蔽,蜘蛛看不到完整頁面;
  • 第三方統計、字体、地图等脚本加载慢或超时,會拖住整頁的渲染;
  • 渲染任務有排队,頁面越依赖脚本,真正拿到内容的延迟越大;
  • 需要登入、需要点击才出現的連結,基本不會進入抓取路径。

所以,一個在浏览器里看起来完全正常的頁面,可能在蜘蛛那一侧只呈現出一個框架结构。

用 a 标簽還是用点击事件

同样是跳轉,寫法不同,结果差別很大。寫成 <a href="/detail/123">,蜘蛛在解析 HTML 时就能拿到連結;寫成 span 加 onclick 跳轉,或者靠 JS 绑定事件,連結對蜘蛛来说等于不存在。用 history.pushState 做客戶端路由时也一样,地址栏變了,但 HTML 里没有對應的 href,蜘蛛没有可跟随的目标。

另一種常见情况是連結确實存在,但被包在條件判断里,比如只有鼠标悬停、只有滚動到可视区域才插入 DOM。蜘蛛的渲染不一定触發這些條件。

懒加载和無限滚動要留出口

图片懒加载問题不大,内容連結的懒加载就要留意。列表頁如果只在滚動到底部时才追加下一頁,蜘蛛很难走完。比較稳妥的做法是把分頁做成带真實 URL 的連結,滚動加载只是给用戶的补充,而不是唯一入口。

怎么確認蜘蛛真的走通了

  1. 用可以查看渲染结果的工具检查頁面,對比初始 HTML 和渲染後的 HTML,看連結在哪個阶段出現。
  2. 在服務器日誌里搜尋子頁面路径,確認有没有来自蜘蛛的請求,而不是只有首頁的訪問记錄。
  3. 检查 robots.txt 是否挡住了渲染必需的 JS、CSS 资源。
  4. 排查脚本错誤和超时,尤其是首屏依赖的第三方资源。
判断标准不是頁面在浏览器里能不能打開,而是初始 HTML 或渲染结果里有没有可跟随的 href。

让两條路径都能走

關键導航、栏目入口、詳情頁連結尽量在服務端渲染輸出,或者至少保證初始 HTML 中存在連結。對重内容頁面,可以考虑服務端渲染或预渲染,把主体内容先给出去。連結统一用标准 a 标簽承载,客戶端路由只作為增强。這样無论蜘蛛走初始 HTML 還是走渲染路径,都能找到下一步。

抓取路径断在渲染阶段的信号

如果日誌里首頁和几個主要栏目頁抓取正常,更深一层的 URL 却長期没有记錄,而站点又是重度依赖 JS 的结构,那基本可以把排查方向放在渲染环节,而不是外鏈或權重。先把這個环节理顺,再谈抓取频率和收錄,顺序會更顺一些。