連結寫在脚本里,發現路径就換了
传统意义上的 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 中。這样即使渲染阶段出問题,地址仍有一條不依赖脚本的發現路径。
自查清單
- 關閉 JavaScript 抓一遍站内連結,记錄能發現多少 URL。
- 再開着 JavaScript 抓一遍,對比两次的連結差集,差集里的地址就是依赖渲染才出現的。
- 检查 robots.txt 是否誤挡了 JS、CSS 等渲染资源。
- 看服務器日誌里渲染類請求是否正常拿到頁面,有没有大量超时。
- 把差集中真正重要的地址补進内鏈或站点地图,次要的可以先放着。
用脚本渲染本身没有問题,問题在于让 URL 發現完全依赖它。把最關键的几條路径用最朴素的方式寫出来,其余的交给脚本也無妨。