给頁面里的每個小节都加一個 id,然後在目錄里放上一排 # 連結,是很多站点的常規做法。有人會顺手把它当成一種“多出几個可收錄地址”的技巧,觉得同一篇内容能拆成好几條索引记錄。這個想法基本站不住脚,原因就藏在片段标识符本身。
# 後面的内容不會出現在請求里
浏览器地址栏里顯示的是 https://example.com/page#section-2,但真正發给服務器的請求是 https://example.com/page。片段标识符由浏览器自己保留,不參與 HTTP 請求,服務器端拿不到它,日誌里也不會出現它。
搜尋引擎蜘蛛的抓取方式是一样的。它請求的是不带 # 的那個地址,拿到的也是同一份 HTML。所以頁面里有十個锚点連結,並不會因此产生十條索引记錄,索引里對應的仍然只有那一個 URL。
一條索引记錄,對應一個不带锚点的地址
搜尋结果里出現的連結,指向的通常是去掉 # 之後的規范地址。点击後跳到頁面某一處,是浏览器根據連結里的片段做的定位行為,属于使用体驗的一部分,和收錄本身無關。
- 同一個 URL 配上不同锚点,索引里仍然只算一條记錄;
- 锚点連結不會被当成新的 URL 去做發現和入队;
- canonical、sitemap 和站内連結,都建议寫不带锚点的干净版本。
在 sitemap 里寫带 # 的地址不算错誤,但也没有額外意义,反而會让一次收錄對帳變得啰嗦——你會在文件里看到一堆看起来不同的地址,實际指向同一個頁面。
例外情况:用 # 做路由的單頁應用
歷史上确實存在過让 # 參與抓取的方案,也就是常说的 hashbang,形如 https://example.com/#!/detail/123。它的出現是因為早期的蜘蛛不执行 JavaScript,站点只能通過一個特殊约定,把片段里的路径交给服務器去返回對應的内容。
這套方案現在基本不用了
Google 早已不再推荐這種寫法,並在 2018 年前後正式停止了對 AJAX 抓取方案的支持。現在更常见的是用 History API 改寫地址,让每個视图對應一個真實路径。如果還在用 hashbang,可以理解為:片段本身仍然只是片段,只是当年的蜘蛛會額外把它翻译成一個請求。
改用真實路径後仍要過一遍渲染
路径變了不代表内容就能被收錄。單頁應用里,直接訪問某個路径时,服務器要么返回包含该视图内容的 HTML,要么让蜘蛛能执行脚本把内容渲染出来。如果首屏只有一張空白壳子,那么無论 URL 寫得多規范,索引里也很难得到實质内容。
锚点連結對 URL 發現還有没有用
同一頁面内部的锚点,對發現新 URL 没有帮助,因為它指向的就是目前地址。但有些内部的寫法值得留意:
- 導航里連結的如果是“另一個頁面的锚点”,被發現的是那個頁面本身,锚点只是附加信息;
- 把重要頁面藏在折叠区域或标簽頁里,靠锚点展開才可见,容易被忽略,連結最好在初始 HTML 里就能讀到;
- 只有锚点、没有真實地址的“假連結”,既不會带来發現,也無法作為可跳轉的目标。
判断标准很简單:這個連結指向的地址,能不能在浏览器新标簽頁里直接打開並看到對應内容。能,它才是一個真實的 URL。
几個可以顺手核對的地方
- 翻一下服務器日誌,確認請求行里没有 #。如果出現了,說明中間某一层做了改寫,需要查清楚;
- 用搜尋後台的 URL 检查工具輸入带锚点的地址和不带锚点的地址,看报告是否指向同一條记錄;
- 检查 canonical 是否誤寫成带 # 的版本,虽然大多數情况下會被归一,但没必要留這個隐患;
- 確認目錄型導航里的連結都是可点击的真實連結,而不是纯 JavaScript 的滚動行為。
如果确實想让每块内容獨立被收錄
把長頁面拆成几個獨立 URL,是更直接的做法。每個地址有自己的标题、正文、canonical 和可被訪問的入口,蜘蛛可以分別抓取、分別判断质量,索引里也就會出現各自的记錄。反過来,指望靠 # 把一頁拆成多頁,往往只是给自己增加了维護成本,索引侧不會有對應的變化。
简單收個尾:锚点是頁面内的位置标记,不是新的地址。它影响的是用戶点击後落在哪里,不影响蜘蛛看到的是什么。真正决定收錄的,仍然是那個不带 # 的 URL 能不能被訪問、能不能被抓到、以及頁面本身值不值得進索引。