给页面里的每个小节都加一个 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 能不能被访问、能不能被抓到、以及页面本身值不值得进索引。