搜尋抓取

JavaScript 生成的連結:搜尋蜘蛛能不能顺着脚本發現 URL

不少站点的導航和列表連結由 JavaScript 動態生成,搜尋蜘蛛在初始 HTML 里看不到這些地址,只能等渲染阶段补上,URL 發現因此被拉長甚至漏掉。本文說明渲染阶段實际發生了什么、哪些常见寫法容易让連結落空,以及如何用服務端渲染、带參數的翻頁地址和站点地图把關键入口寫回 HTML。

搜尋抓取

JavaScript 生成的連結:搜尋蜘蛛能不能顺着脚本發現 URL

連結寫在脚本里,發現路径就換了

传统意义上的 URL 發現很直接:搜尋蜘蛛下载 HTML,解析其中的 a 标簽和 href 属性,把新地址放進待抓取队列。這條鏈路不依赖浏览器,只要 HTML 里有連結就能走通。

当站点的導航、列表、相關阅讀由 JavaScript 在頁面加载後動態插入 DOM 时,初始 HTML 里往往没有對應的 href。蜘蛛需要進入渲染阶段,用類似無头浏览器的方式执行脚本,才能看到這些地址。發現過程通常不會彻底消失,但會被推後、缩量,而且更不稳定。

渲染阶段實际發生了什么

主流搜尋引擎對頁面的處理大致分两步:先抓取原始 HTML 做初步解析,再把需要渲染的地址排入渲染队列,执行完脚本後取一次最终的 DOM。渲染队列的资源比抓取队列更紧,排队時間往往更長。

  • 渲染要下载脚本、样式和字体,還可能發起接口請求,成本遠高于抓一份静態 HTML。
  • 渲染超时、脚本报错或接口失敗时,這一轮里新出現的連結就丢了,什么时候再补上没有保證。
  • 如果 robots.txt 挡住了 JS、CSS 文件,渲染出来的 DOM 可能是不完整的,連結自然也拼不出来。

哪些寫法最容易让連結落空

  • 用 onclick 加 location.href 做的伪連結,元素本身没有 href 属性。
  • 纯 div、span 加事件监听實現的卡片和按钮。
  • 点“加载更多”之後才追加進来的列表項。
  • 依赖登入態、本地存储或某個接口返回才渲染的導航。
  • 無限滚動:不滚動就不产生新地址,滚動位置也無法传递。
  • 接口返回 JSON 後在前端拼出来的網址,蜘蛛不一定执行那次請求。

把關键入口放回 HTML 里

做法並不复杂:主導航、面包屑、列表翻頁、文章底部的相關阅讀,尽量由服務端渲染輸出真實的 a href。如果站点是纯前端框架,至少给這几類頁面做 SSR 或者预渲染,让原始 HTML 里就带着完整連結。

判断标准可以很简單:關掉 JavaScript,還能不能顺着連結点到目标頁。能点到,發現路径就是稳的。

無限滚動與懒加载的折中做法

列表頁不要只靠滚動触發。保留带參數的翻頁地址,比如 page=2、page=3,每一頁服務端返回真實連結,滚動加载只作為同一頁内的视觉补充,不要让滚動成為唯一的入口。图片懒加载、次要模块懒加载都可以留,但連結本身不该被延後生成。

用站点地图和静態入口兜底

渲染成本高的站点,用 XML 站点地图把重要 URL 直接列出来,是成本最低的补充入口;再配一個服務端輸出的 HTML 站点地图頁,把栏目、归档、分頁連結寫死在 HTML 中。這样即使渲染阶段出問题,地址仍有一條不依赖脚本的發現路径。

自查清單

  1. 關閉 JavaScript 抓一遍站内連結,记錄能發現多少 URL。
  2. 再開着 JavaScript 抓一遍,對比两次的連結差集,差集里的地址就是依赖渲染才出現的。
  3. 检查 robots.txt 是否誤挡了 JS、CSS 等渲染资源。
  4. 看服務器日誌里渲染類請求是否正常拿到頁面,有没有大量超时。
  5. 把差集中真正重要的地址补進内鏈或站点地图,次要的可以先放着。

用脚本渲染本身没有問题,問题在于让 URL 發現完全依赖它。把最關键的几條路径用最朴素的方式寫出来,其余的交给脚本也無妨。