网站收录量突然翻倍?索引膨胀与 site 指令诊断实录
周四早上九点,运营在群里甩来一张截图:site:example.com 的结果数从 38 万跳到了 79 万,Google Search Console(GSC)页面索引报告同步暴涨,一天之内多出四十多万个"已收录"页面。我们没上新内容,没改架构,收录却翻倍——干过几年 SEO 的都知道,这不是喜报,是事故。
一、事发当天:数字翻倍,流量纹丝不动
先说结论放前面:收录数暴涨而自然流量不动,大概率是索引膨胀(Index Bloat),而不是内容红利。
索引膨胀指大量低价值 URL 被搜索引擎收进索引,稀释了站点排名信号和抓取资源。那天我们逐项排除了正常可能:内容团队确认没有批量发稿,发版记录里只有一次无关紧要的样式改动,sitemap 文件也没人动过。
但有两个细节不对劲。一是收录暴涨的曲线几乎是台阶式的,一夜之间完成的,自然内容增长画不出这种形状。二是抽样点开 site: 结果的后几页,出来的 URL 全是日历翻页、带排序参数的商品列表、打印版页面——这些东西什么时候进索引的?
第一个动作是把 site: 查询、GSC 页面索引报告、服务器访问日志三边数据摆到一张桌子上对账。收录暴涨前后的 URL 构成对比如下:
| URL 类型 | 暴涨前收录占比 | 暴涨后收录占比 | 变化 | 价值判断 |
|---|---|---|---|---|
| 商品详情页 | 42% | 19% | 被稀释 | 核心页面 |
| 分类列表页 | 18% | 8% | 被稀释 | 有索引价值 |
| 内容资讯页 | 21% | 10% | 被稀释 | 有索引价值 |
| 日历翻页页 | 7% | 31% | 暴涨 | 零价值 |
| 排序/筛选参数页 | 8% | 24% | 暴涨 | 零价值 |
| 打印版页面 | 4% | 8% | 小涨 | 零价值 |

表格一摆出来就清楚了:真正有价值的页面占比从八成掉到不足四成,新增的四十多万收录几乎全是垃圾 URL。接下来是定位膨胀源、搞清楚它为什么突然被放出来。
二、三方对账:site 指令、GSC 报告、服务器日志
site 指令抽样:先看 URL 构成
site: 查询给的是估算值,精确数字没意义,但用它看收录的 URL 构成非常好用。人工翻页太慢,写了个 Python 脚本做抽样聚合,把结果按目录和参数特征分桶。
# 依赖:requests==2.31.0,Python 3.10+
# 用途:低频抽样 site: 指令结果,统计收录 URL 的构成分布
import requests
import re
import time
import random
from collections import Counter
UA = {"User-Agent": "Mozilla/5.0 (compatible; SEODiag/1.0)"}
# 带可识别的 UA 前缀,方便事后在风控日志里定位自己的请求
def site_sample(domain, pages=5):
# site 结果只是估算,这里只关心样本里的 URL 形态
urls_seen = []
for page in range(pages):
# 每页取 10 条,翻页用 start 参数
url = "https://www.bing.com/search?q=site%3A" + domain
url += "&first=" + str(page * 10 + 1)
html = requests.get(url, headers=UA, timeout=10).text
# 正则抽取结果里的目标站链接
links = re.findall(r'href="(https?://[^"]*%s[^"]*)"' % domain, html)
for link in links:
# 去掉查询参数,只保留路径骨架,方便分桶
path = re.sub(r"\?.*$", "", link)
path = path.replace("https://", "").replace("http://", "")
urls_seen.append(path)
# 拉长随机间隔,降低触发风控的概率
time.sleep(random.uniform(4, 8))
return urls_seen
paths = site_sample("example.com")
buckets = Counter()
# 分桶键决定结论的可读性,先按低质特征分,再按目录兜底
for p in paths:
# 判定低质页面特征:日历、排序参数、打印版
if "/calendar" in p:
key = "日历页"
elif re.search(r"[?&](sort|order|filter|price)=", p):
key = "排序参数页"
elif "/print" in p:
key = "打印版页面"
else:
# 其余按前两级目录聚合
parts = p.split("/")
key = "/".join(parts[:3]) if len(parts) > 3 else p
buckets[key] += 1
for k, v in buckets.most_common(20):
# 输出各桶样本占比,膨胀源一眼可见
print(v, round(v / len(paths) * 100, 1), k)
脚本跑出来的样本里,日历页加参数页占了六成以上。注意这类抽样样本量小、且搜索引擎反感高频程序化查询,只用于定性判断,别拿样本比例直接外推全站。
GSC 页面索引报告:看官方口径的增量和原因
GSC 的"页面索引记录"比 site: 数字可信得多。两个看重点:
- 增量来源:报告里"已收录"曲线暴涨的时间点,和"为什么未收录网页"里某些原因的减少是对应的。我们这次是"重复网页,Google 选择了不同规范网页"和"已编入索引"两条曲线互换——大量原本被判为重复的 URL 突然被收进了索引。
- 交叉验证:点开"已编入索引"的示例列表,抽十个 URL 看看是什么页面。如果示例里出现日历页、参数页,基本可以定性。
服务器日志:爬虫的钱花在哪了
前两步只能看到"收录了什么",日志能看到"爬虫(Spider)天天在抓什么"。这是定位抓取预算(Crawl Budget)去向的硬证据:
# 依赖:无第三方库,Python 3.10+
# 日志格式:nginx combined,文件为 access.log
import re
from collections import Counter
# 匹配请求行、状态码与 User-Agent
LINE = re.compile(r'"GET (\S+) HTTP/1\.[01]" (\d{3}) \d+ .*?"[^"]*" "(.*?)"')
BOT = re.compile(r"(Googlebot|Baiduspider)", re.I)
bucket = Counter()
with open("access.log", encoding="utf-8", errors="ignore") as f:
for line in f:
m = LINE.search(line)
# 解析失败的脏行直接跳过
if not m:
continue
path, status, ua = m.groups()
if not BOT.search(ua):
# 只统计搜索引擎爬虫的请求
continue
# 按低质页面特征给爬虫请求分桶
if "/calendar" in path:
key = "日历页"
elif re.search(r"[?&](sort|order|filter|price)=", path):
key = "排序参数页"
elif "/print" in path:
key = "打印版页面"
else:
key = "正常内容页"
bucket[key] += 1
total = sum(bucket.values())
# 分母为零时说明日志里根本没有爬虫请求,先报警再算占比
for k, v in bucket.most_common():
# 输出爬虫抓取量分布,这就是抓取预算的去向
print(k, v, str(round(v / total * 100, 1)) + "%")
日志给了最后一锤:过去 30 天 Googlebot 的抓取里,日历页和参数页吃掉了 61% 的抓取量。爬虫不是不想抓好页面,是被垃圾 URL 的链接入口带着满站跑。
三种诊断手段各有分工,对账时按这个表来:
| 手段 | 回答的问题 | 口径特点 | 用途 |
|---|---|---|---|
| site: 指令 | 收录的 URL 长什么样 | 粗略估算,样本随机 | 定性看构成,快 |
| GSC 页面索引报告 | 收录量的精确增减与原因 | 官方口径,滞后 1-3 天 | 定量确认趋势 |
| 服务器日志 | 爬虫实际抓了什么 | 原始数据,最真实 | 定位抓取预算去向 |
三、原理剖析:膨胀的索引为什么能拖垮整站
抓取预算是被垃圾 URL 吃掉的
抓取预算是搜索引擎分配给一个站点的抓取频次上限,由站点体量、服务器健康度、页面价值综合决定,具体机制见 Google 官方的抓取预算管理文档。它像一个每天定量的"抓取额度":爬虫每天来抓五万次,抓了三万次日历页和参数页,留给商品页的就只剩两万。
更糟的是恶性循环。参数页之间互链(日历页上挂着 30 个参数链接,参数页上又挂着日历),爬虫顺着链接图跑,垃圾页面之间互相喂流量,核心页面反而排队等抓。我们日志里还看到部分深层商品页的"已抓取-未收录",就是预算被挤占的直接后果。
排名信号被稀释的机制
搜索引擎给页面算权重时,站内链接是重要信号通道。全站导航、商品页面包屑都会链接到列表页,而列表页上多了几十个排序变体后,一个分类页实际裂变成了几十个内容高度雷同的 URL。原本集中到一个分类页的内外链信号,被这些重复变体摊薄了——每个变体拿到的信号都不够越过质量阈值,排名自然往下掉。
site: 数字和 GSC 对不上的原因
对账时很多人纠结 site: 显示 79 万、GSC 只有 62 万,怀疑数据有假。其实两者口径不同:site: 是检索引擎的粗略估算,含大量重复和过期条目,仅作参考;GSC 页面索引报告是索引系统的真实记录,滞后一到三天。做决策看 GSC,看构成用 site:,别拿两者互相比对谁对谁错。
回头看这次事故的起点:为什么突然放出这么多垃圾页?排查后发现是发版时前端同学把分页日历的链接从 JS 动态加载改成了 <a href> 直出——爬虫原本"看不见"这些入口,改完一夜之间全暴露了。收录暴涨从来不是引擎抽风,先查自己最近的发版记录。
四、收敛方案:noindex、robots、canonical 三板斧
定位之后就是收敛。三种手段解决三个不同问题,选错了会踩坑:
| 手段 | 作用 | 爬虫还抓吗 | 适用场景 |
|---|---|---|---|
| meta noindex | 允许抓取但禁止收录 | 抓 | 已有大量收录的低质页 |
| robots.txt 屏蔽 | 禁止抓取 | 不抓 | 入口明确、不想浪费预算 |
| canonical 合并 | 把信号归到规范 URL | 抓 | 参数页指向默认列表页 |
关键选型逻辑:已经进索引的页面,不要直接上 robots 屏蔽——屏蔽了抓取,引擎看不到 noindex 标记,旧索引反而赖着不走。正确顺序是先 noindex 让索引退出,稳定后再评估是否用 robots 收回抓取预算。
我们的落地动作分三步:
第一步,参数页和打印版页面模板头部加元标签,指向规范版本:
<!-- 依赖:无,模板公共头部片段,适用于排序参数页与打印版 -->
<!-- 顺序很关键:先声明 robots 元标签,再写 canonical -->
<meta name="robots" content="noindex, follow">
<!-- noindex 禁收录,follow 保留页内链接的发现价值 -->
<link rel="canonical" href="https://www.example.com/products/123">
<!-- canonical 指回不带参数的规范地址,合并链接信号 -->
第二步,日历页这类纯翻页垃圾直接在 nginx 层拒掉,省得爬虫来回跑:
# 依赖:nginx/1.24,server 块内配置
# 日历翻页路径返回 410,明确告知资源已移除
location ~* ^/calendar/ {
# 410 比 404 语义更强,引擎退索引更快
return 410;
}
# 打印版路径做 301 跳转到正文页
location ^~ /print/ {
# 用跳转把外链权重顺手带回去
rewrite ^/print/(.*)$ /$1 permanent;
}
第三步,收尾与验证:全站 sitemap 重新生成,只留规范 URL;两周期望 GSC 曲线回落。实际结果,第十四天收录回落到 41 万,商品页的抓取占比从 25% 回到 68%,受影响的几个分类词排名在第三周回来。
收敛前后的抓取预算流向对比:
flowchart LR
subgraph G1[收敛前]
A1[日抓取 5 万次] --> A2[日历+参数页 61%]
A1 --> A3[核心内容页 25%]
A1 --> A4[其他 14%]
end
subgraph G2[收敛后]
B1[日抓取 5 万次] --> B2[核心内容页 68%]
B1 --> B3[参数页残量 9%]
B1 --> B4[其他 23%]
end
G1 -->|noindex+robots+canonical| G2
诊断和收敛的完整流程:
flowchart TD
A[发现收录暴涨且流量不动] --> B[site 抽样看 URL 构成]
B --> C{低质 URL 占比高}
C -->|是| D[GSC 页面索引报告定量确认]
C -->|否| E[回查正常页面与外链]
D --> F[服务器日志对账爬虫去向]
F --> G[定位膨胀源与暴露原因]
G --> H{按页面类型选收敛手段}
H --> I[已收录低质页: noindex]
H --> J[纯垃圾路径: robots 或 410]
H --> K[参数变体页: canonical]
I --> L[两周观察 GSC 回落]
J --> L
K --> L
五、复盘:三个误区,一条原则
复盘会上理了三个常见误区,都踩过或差点踩过:
- 误区一:收录越多越好。 收录是手段不是目标。超过站点内容质量上限的收录,只会摊薄信号、浪费预算。判断标准永远是"收录的页面有没有检索价值"。
- 误区二:发现垃圾收录第一反应上 robots 屏蔽。 如前所述,屏蔽抓取会让 noindex 失效,旧索引滞留。先 noindex、后屏蔽,顺序不能反。
- 误区三:拿 site: 数字当精确值做汇报。 它是估算口径,波动几个百分点毫无意义,对外汇报一律用 GSC。
一条原则:索引应该跟着站点价值走,而不是跟着链接数量走。 参数、翻页、打印版这类功能性 URL,默认就应该挡在索引之外——用模板层的 noindex 加 canonical 把闸门焊死,比每次事后救火划算得多。
顺带一提,这套"先看内容质量、再定收录边界"的思路,和面向 AI 搜索引擎的 GEO 优化在底层是相通的:无论传统引擎还是生成式引擎,能被检索、被引用的,永远是有价值的规范页面。
排查思路或参数收敛策略上有不同做法的,欢迎评论区聊聊。
参考与延伸
网站收录量、索引膨胀、site 指令、抓取预算、noindex、canonical、收录诊断