蜘蛛池的入口頁大多是静態 HTML,原因很直接:連結寫在源碼里,蜘蛛不用执行脚本就能發現。但實际维護中,不少入口頁為了好看或省事,把連結交给 JavaScript 生成,结果頁面上人能看到,蜘蛛却拿不到。這里只讨论一件事:当連結依赖 JS 渲染时,會遇到什么問题,以及怎么改回可控的狀態。
搜尋引擎能跑 JS,但不等于會為你的 JS 排队
主流搜尋引擎确實具备执行 JavaScript 的能力,流程通常是先抓取 HTML 源碼,再放進渲染队列执行脚本。問题在于渲染是額外一步,成本比直接讀源碼高,所以它有自己獨立的节奏。
- 渲染队列有排队時間,短則几分钟,長則數天,這期間頁面上的 JS 連結等于不存在。
- 渲染有预算,單個站点能分到的渲染次數通常遠少于普通抓取次數。
- 渲染會超时,脚本报错、請求第三方资源失敗、接口响應太慢,都可能让這次渲染白做。
- 渲染结果不一定會被当成新的連結来源再扩散一次,深层頁面尤其如此。
換句话说,JS 渲染是“可能被抓到”,静態連結是“大概率被抓到”。對入口頁這種以發現 URL 為主要目的的頁面,主動把概率压低並不划算。
入口頁里几種典型的“假連結”
- onclick 绑定跳轉:用 div、span 加 onclick 执行 location.href,源碼里没有 href,蜘蛛拿不到目标地址。
- data 属性加脚本拼接:地址先放在 data-href 里,頁面加载後再由脚本生成 a 标簽,抓取时源碼中只是一堆空容器。
- 异步接口返回列表:入口頁只放一個空壳,連結列表由接口返回後渲染,接口本身没被索引,連結也就無從扩散。
- 延迟加载與滚動加载:連結元素進入视口才加载,渲染器不一定滚動到底部,靠後的連結容易被漏掉。
- iframe 里嵌套的列表:iframe 内容有可能被抓,但被發現的速度和繼承到的權重,通常不如主文档里的直鏈。
最省事的改法:让連結在源碼里就存在
- 優先用原生 a 标簽:href 指向真實 URL,脚本只负责样式和交互,不负责生成地址。
- 服務端渲染或静態生成:入口頁内容本来就不多,用模板在服務端拼好 HTML 直出,比前端渲染更稳。
- 预渲染兜底:确需前端框架生成时,對入口頁做预渲染,向抓取端返回一份包含連結的静態快照。
- noscript 兜底:在 noscript 中放一份基础的分類連結,至少保證主路径可達。它只是兜底,不要当成主要方案。
- 保留一個纯静態列表頁:把 URL 發現集中在這類頁面,動態部分只做展示,不承担扩散任務。
确實要用 JS 渲染时,把成本降下来
- 首屏直出:關键連結放在 HTML 靠前的位置,別等脚本跑完才出現。
- 减少第三方脚本:外部 JS、統計、字体、广告都會拖慢渲染,任何一個卡住都可能让整次渲染超时。
- 控制渲染体积:入口頁 DOM 节点數量不要無节制膨胀,長列表考虑分頁。
- 接口尽量稳定:渲染依赖的接口如果经常超时,抓取端的渲染成功率也會跟着下降。
- 保持结果一致:移動端與桌面端渲染差异過大时,容易出現同一頁面两套連結的情况,反而不利于判断。
怎么自查入口頁的連結是否可發現
- 直接查看網頁源碼,搜尋目标 URL 是否出現在 HTML 中,而不是只出現在脚本字符串里。
- 禁用浏览器 JavaScript 後打開入口頁,看還剩多少可点击的連結。
- 查看服務器日誌,確認抓取端是否請求過頁面依赖的 JS 和接口文件;如果连這些都没抓,說明渲染這一步還没走到。
- 用抓取诊断類工具對比源碼版與渲染版,看两者連結數量差多少。
- 抽查新發布的目标頁,观察一段時間内是否有抓取請求,而不是只看頁面在前端展示是否正常。
需要說明的是,連結能被發現只是第一步,之後還要经過抓取、解析、去重和索引判断。把入口頁做成静態直鏈,能减少一個不确定环节,但並不代表内容一定會被收錄。
小结
入口頁的任務是把 URL 稳定地交到蜘蛛面前。能用 HTML 直出的連結就不要交给脚本,能用服務端渲染的就別依赖前端异步。JS 渲染不是不能用,但它應当是特殊场景下的可選項,而不是入口頁連結的預設實現方式。