同一條連結為什么會被当成两個 URL
排查抓取問题时,很多人先看 robots.txt 和 Sitemap,却容易忽略更底层的一点:站内輸出的 URL 编碼形態並不统一。對使用者来说,同一段連結看起来指向一個頁面,但對抓取队列而言,它們是不同的字符串。字符串不同,就可能被当作两條獨立 URL 分別排队、分別請求、分別計入抓取統計。
常见的几種编碼分叉
- 百分号编碼大小寫不一致:一段中文路径经過编碼後,字母部分大小寫不同,字面量並不相等,但解碼结果一致。
- 空格處理方式不同:有的模板輸出 %20,有的表單拼接成加号,两者在部分服務端解碼後结果相同,在 URL 层面却是两個地址。
- 中文與特殊字符未编碼:某些内鏈直接輸出中文路径,另一些位置经過一次编碼,日誌里就出現两種形態。
- 连接符轉义混乱:參數分隔符在 HTML 中被寫成實体形式,若在連結拼接环节再轉义一次,參數结构會整体改變。
- 锚点與查询串混用:同一位置用锚点定位和用參數定位,被抓取到的概率與生成的路径完全不同。
抓取侧會表現出什么現象
最直观的信号来自服務器日誌:同一篇内容在日誌里對應多條 URL 形態,且抓取時間接近。其次是抓取分布被摊薄,本该集中在一個地址上的抓取频次,被分到几個變体上,更新較慢的頁面更难被及时回訪。如果這些變体還各自返回正常狀態碼,問题會持續累积,且很难從报表上直接看出。
站点侧怎么收敛
- 定义唯一形態:明确站内 URL 采用哪一種编碼方式,百分号统一為大寫還是小寫,空格用 %20 還是路径分隔,中文是否编碼,寫成一句话的規范並让前後端都遵守。
- 内鏈先行统一:導航、面包屑、列表頁、正文内鏈、分頁連結、頁脚,凡是由模板或編輯器生成連結的位置,都按同一規則輸出,避免同一頁面在站内就有多種寫法。
- Sitemap 與内鏈一致:Sitemap 中出現的地址應與内鏈輸出的形態完全一致,不要一邊给未编碼形態,一邊铺编碼形態。
- 服務端做归一:對非規范形態返回 301 指向規范形態,而不是直接放行或返回正常頁面。归一时只處理确定等價的编碼差异,不要把實质上不同的頁面合並。
- 頁面内自引用保持一致:canonical 自引用、结构化資料中的地址、分享連結,都應使用規范形態,减少二次分叉的来源。
几個容易踩的邊界
编碼差异有时是有意為之,例如多語言站点用不同路径前缀区分語言版本。這類情况應通過路径结构而非编碼變体来区分,否則不同語言頁面容易被誤判為同一地址的重复形態,反而影响各自的抓取表現。
另外,不要在归一過程中做過度解碼。有些字符在參數里必须保持编碼狀態,强行解碼會破坏參數含义,甚至引發安全問题。归一只處理确定等價的形態差异,拿不准的保持原样並單獨观察,比一刀切更稳妥。
一個可执行的检查清單
- 随机抽取日誌中的重复 URL,對比其字符串差异,確認是编碼差异還是内容差异。
- 检查模板渲染层是否對同一變量做了两次轉义或两次编碼。
- 检查站点地图生成脚本與前端路由是否使用同一套 URL 拼接逻辑。
- 检查服務端是否能识別非規范形態並给出 301,而非直接返回正常頁面或 404。
- 收敛後持續观察日誌,確認變体請求比例下降,抓取集中在規范地址上。
编碼一致看起来是细节,但它直接影响 URL 發現的數量與质量。把這件事做干净,抓取资源才更容易花在真正需要更新的頁面上。