單頁應用(SPA)里,頁面切換时地址栏會變化,但服務器可能從头到尾只返回同一份 HTML。這類站点做收錄时,常见的卡点不是内容不够,而是"能被蜘蛛当成獨立頁面處理的地址"其實没几個。下面按地址形態拆開看。
井号是给浏览器看的,不是给服務器看的
URL 中 # 之後的部分叫片段(fragment),它只在浏览器内部生效,不會随請求發送到服務器。也就是说,/list#page-2 和 /list 對服務器来说是同一個請求,返回的 HTML 完全一样。搜尋引擎抓取时通常也會把带 # 的地址归一成不带 # 的地址来處理,片段本身不构成一個可被抓取的獨立 URL。
所以像 /#/detail/123 這種哈希路由,蜘蛛拿到的始终是首頁那份 HTML。地址栏里看着"不同"的那些頁面,在抓取层面只是同一個地址。
History 路由:地址變了,内容有没有跟着變
用 pushState 把地址改成 /detail/123 之後,URL 本身是真實地址了,關键是服務器收到這個請求时能不能返回對應内容。
- 如果服務器對所有路径都回退到同一個空壳 HTML,内容全靠前端 JS 渲染,蜘蛛要先执行 JS 才可能看到正文,鏈路長、成本高,也更容易在渲染环节被跳過。
- 如果服務器能针對這些地址返回带标题、正文、内鏈的 HTML,抓取和後續的規范化判断都會简單很多。
- 更麻烦的情况是:地址路径各不相同,返回的 HTML 却完全一样。這样就變成了一批内容相同的 URL,谁被留下由規范化和去重决定,不由站点决定。
三種常见做法的實际差別
纯哈希路由
所有頁面共用一個物理地址,天然只有一份 HTML。在這種结构上做收錄,通常需要另外為每個内容生成可返回内容的地址,再把哈希地址映射過去,或者干脆改造成真實路径。
History 路由 + 客戶端渲染
地址是規范的,但 HTML 是空壳,需要確認渲染後的内容能被拿到。同时頁面之間要有正常的 <a href> 連結——是 a 标簽里的 href,不是 onclick 跳轉,蜘蛛不會去点按钮。
服務端渲染或预渲染
每個地址返回的内容不同、互相可連結、有獨立的标题與正文。這種结构對抓取最友好,後續排查也最省事。
連結形式:可点的按钮不等于可爬的内鏈
單頁應用常把導航寫成 div 加点击事件,或者用统一的跳轉函數處理。蜘蛛顺着連結爬行时看的是 a 标簽里的 href,JS 触發的跳轉不一定能被跟着走。把主要導航、列表項、面包屑改成标准連結,改動不大,但往往是這一類站点最直接的改善点。
判断哪些地址值得獨立收錄
- 内容确實不同,不是同一個列表的篩選结果或排序變化。
- 该地址被單獨搜尋时有被找到的價值,比如商品、文章、分類首頁。
- 存在稳定的内鏈入口,能從首頁或其他頁面点進来,而不是只靠站内搜尋才能到達。
- 服務器能對這個地址返回内容,而不是统一回退到首頁 HTML。
篩選、排序、無限滚動加载出来的中間狀態,一般不需要每個都進索引。用參數處理,或者干脆不把中間狀態做成獨立地址,比事後清理省力。
一份可执行的检查清單
- 随机抽几個地址,關掉 JS,看服務器返回的 HTML 里有没有正文與連結。
- 看服務器日誌或抓包,確認蜘蛛請求的是 /detail/123 這類真實路径,而不是只有根路径反复出現。
- 检查頁面之間的跳轉是否是 a href,而不是纯 JS 事件。
- 確認規范化标簽指向的那個地址,是站点能返回内容的那個。
- 用站点地图或内鏈,把重要的深层地址暴露出来,別只留首頁一個入口。
判断标准可以很简單:關掉 JS 之後,如果這個地址返回的 HTML 能让人看明白這是個什么頁面,蜘蛛多半也能;如果不能,就要先解决渲染成本與返回内容的問题。
單頁應用的收錄問题,多數不是内容太少,而是地址和内容對不上。把這一点理顺,其他優化才有落点。