搜尋抓取

蜘蛛渲染頁面的邊界:JS 生成的内容和連結能被抓到多少

頁面用 JavaScript 生成内容和連結时,蜘蛛第一次拿到的 HTML 里可能什么都没有。本文把抓取和渲染拆成两個阶段来看,說明渲染队列、超时、视口與交互带来的限制,並给出让重要 URL 直接出現在原始 HTML 里的具体做法和自查顺序。

搜尋抓取

蜘蛛渲染頁面的邊界:JS 生成的内容和連結能被抓到多少

不少站点把列表、詳情連結和正文都交给 JavaScript 生成,然後在日誌里發現蜘蛛只訪問了首頁和几個入口。問题通常不是蜘蛛“不愿意抓”,而是它第一次拿到的 HTML 里根本没有那些 URL。把抓取和渲染分成两件事来看,排查思路會清楚很多。

抓取和渲染是两個阶段

蜘蛛請求一個地址,最先拿到的是服務器返回的原始 HTML。這一阶段做的事情很朴素:從响應里提取連結和文本,把新 URL 放進队列。如果原始 HTML 里没有某個連結,這個地址往往连被發現的机會都没有。

渲染是後面的事情。蜘蛛會把其中一部分頁面送進渲染队列,执行頁面上的 JS,等 DOM 相對稳定後再提取一次連結和内容。這一步有排队、有资源上限,也有失敗概率,和第一次抓取不是同一條流水线。

哪些内容必须依赖渲染才能出現

  • 列表由前端框架請求接口後拼出来的分頁和條目連結
  • 詳情頁地址由 JS 拼接字符串生成,HTML 里只有一個空容器
  • 正文完全来自接口返回,服務端没有任何兜底輸出
  • 跳轉寫成点击事件或按钮,而不是带 href 的 a 标簽
  • 需要登入、需要選擇地区、需要点击“展開”才顯示的内容

渲染這一环的限制在哪里

  • 排队:渲染不是同步發生的,從抓取到渲染完成可能有明顯延迟,甚至長時間不渲染。
  • 超时與失敗:脚本执行過久、依赖被阻塞、請求报错,都會让這次渲染被放弃,且不一定立刻重试。
  • 视口:渲染通常在固定视口下進行,位于视口之外、需要滚動才顯示的内容不一定被加载。
  • 交互:滚動、点击、“加载更多”這類用戶行為一般不會自動执行。
  • 资源可訪問性:被 robots.txt 或權限拦住的 JS、CSS 會影响解析结果,渲染出来的结构可能和你在浏览器里看到的完全不同。

让 URL 出現在原始 HTML 里的几種做法

  1. 列表頁做服務端渲染或预渲染,至少让條目連結以 a href 的形式直接輸出。
  2. 分頁使用可点击連結,给出真實可訪問的網址,而不是纯 JS 按钮。
  3. 图片和模块懒加载尽量用原生属性,並在 noscript 中留一份連結或文本兜底。
  4. 無限滚動配一套分頁地址,让滚動到底後存在一個可被抓取的等價 URL。
  5. 让 Sitemap 與站内連結互為备份,重要地址至少在其中一處出現。
  6. 移動端與桌面端模板差异較大时,確認两套模板輸出的連結集合不要相差太多。

渲染不是免費的通行證

即使頁面能被渲染,代價也比静態 HTML 高:排队更久、失敗概率更大、重抓間隔通常也更長,而且渲染结果一旦出错,你很难立刻從日誌里看出来。能直接輸出的内容,尽量直接輸出。

不要假设蜘蛛會滚動、点击或等待你的“加载更多”按钮。任何需要用戶動作才出現的 URL,都按“預設不存在”来设計兜底方案。

自查的顺序

  • 先看源代碼而不是审查元素,確認連結是否出現在原始 HTML 中。
  • 用抓取工具對比渲染前後的連結差异,找出只在渲染後出現的 URL。
  • 检查 JS 與接口請求是否被 robots.txt 或權限規則挡住。
  • 用站長平台的 URL 检查工具查看渲染快照,確認渲染出来的頁面是否符合预期。
  • 把 Sitemap 中的 URL 與抓取日誌做差集,差集里長期不被訪問的部分,優先补内鏈或改成静態輸出。

把站点的發現路径做成“不依赖 JS 也能走通”的结构之後,渲染問题就從阻塞項變成了優化項。剩下的工作才是提升渲染成功率、缩短渲染與重抓之間的間隔。