很多做站点运营的人會遇到一個現象:導航里明明放了入口,頁面也没有被屏蔽,但几個月過去,目标 URL 還停留在「已發現,尚未抓取」,或者干脆没出現在索引报告里。排查一圈 robots、sitemap、canonical 都没發現問题,最後發現症结在最基础的一环——連結根本没有以爬虫能看见的形式存在。
抓取和渲染是两步,不是一步
搜尋引擎處理一個 HTML 頁面时,通常先取回原始 HTML 源碼,解析其中的 a 标簽、图片地址等静態連結;之後才會安排渲染,执行頁面上的 JavaScript,拿到渲染後的 DOM。
如果你的連結是 JS 動態加進去的,那它只在第二步之後才存在。第一步的解析结果里,這些 URL 是空白的。渲染是有成本的動作,不會對所有頁面、所有時間点都無條件执行,所以纯 JS 生成的連結,被發現的時間点會明顯靠後,甚至一直不出現。
這也是為什么「在浏览器里能看到入口」和「爬虫能發現入口」是两回事:你在浏览器里看到的,永遠是渲染後的结果。
四種常见的不易發現寫法
- 点击後才生成。下拉菜單、選項卡里的連結寫在 click 回調里,不点就不存在,爬虫不會替你点。
- 滚動到底才加载。無限滚動、懒加载列表,第一屏 HTML 里没有任何指向後續内容的 a 标簽。
- 只用 pushState 路由。頁面用 history API 切換视图,地址栏變了,但 HTML 里没有對應連結,爬虫找不到這些虚拟地址。
- 連結由接口返回。列表資料来自 fetch,HTML 里只有一個空容器。
這几種寫法本身没有對错,用戶体驗往往更好。問题在于它們把 URL 的發現完全交给了渲染,而渲染是你不完全可控的一环。
自查顺序
- 看源碼,不看元素面板。在浏览器里右键查看網頁源代碼,搜尋 href。開發者工具顯示的是渲染後的 DOM,會掩盖問题。
- 用抓取測試工具跑一遍。多數站長平台都提供頁面抓取或渲染測試,能看到原始 HTML、渲染後 HTML 以及被發現的連結列表,两者對比最直观。
- 看服務器日誌里有没有渲染請求。有的渲染服務带特定 UA,有的来自不同的 IP 段。如果日誌里完全没有相關請求,說明渲染這一环根本没有触發。
- 检查是否被技術手段挡住。robots 里屏蔽 JS、CSS 文件,或者對静態资源做了鉴權、按 UA 返回 403,都會让渲染失敗,連結自然出不来。
- 確認没有互相打架的指令。比如頁面本身可抓取,但 JS 文件被 robots 屏蔽,等于给爬虫送了一個残缺頁面。
给關键 URL 留一條静態路径
渲染不该是 URL 發現的唯一通道。對真正希望被收錄的頁面,最省事的做法是至少保留一條静態可達路径:
- 主導航、面包屑、頁脚這類全站出現的区域,用原生 a 标簽寫死關键栏目和一批重要詳情頁。
- 列表頁尽量做成分頁 URL,而不是纯無限滚動;或者在首屏之外保留「下一頁」的静態連結。
- 把重要頁面按分组放進 sitemap。sitemap 是發現渠道,不是收錄保證,但至少让 URL 有一個出口。
- 對确實無法静態暴露的頁面,先评估數量。几十個可以靠 sitemap 兜底,上萬條就別指望這條路了。
判断标准很简單:把 JavaScript 全部關掉,看還能不能從首頁走几步点到目标頁面。能点到,發現就不成問题;点不到,就要靠渲染或 sitemap 补。
几個容易忽略的细节
一是锚点與 hash 路由。地址里 # 後面的内容不會作為獨立 URL 參與抓取,靠 hash 区分的「頁面」在抓取视角里属于同一個地址。
二是前端路由的返回狀態。用 JS 做路由的站点,如果所有路径都返回 200 並渲染同一份骨架,容易让爬虫分不清哪些是真實存在的頁面。服務端能区分的话,尽量给出准确的 HTTP 狀態。
三是別為了發現而堆連結。把几千個連結塞進頁脚、塞進隐藏容器,既影响体驗,也不會因為數量多就更快被發現,反而可能让頁面失去重点。
小结:URL 發現的第一步,是让連結以可解析的形式出現在 HTML 里,渲染只是补充手段。排查时按源碼、渲染结果、日誌、屏蔽規則這個顺序走,通常能比較快地定位到底是哪一环断了。