百度收录量忽然腰斩:用日志分析追查 Baiduspider 抓取异常的完整过程
适用读者:维护传统网站的 SEO 技术同学、管 Nginx 的后端工程师,以及所有盯着百度索引量曲线发愁的站长。
两周时间,站点的百度索引量从 4200 掉到 1900,掉了一半还多。搜索资源平台的后台却一直显示「数据正常」,反馈中心提交问题,等来的也是模板回复。运营那边急了,把电话打到运维老周(化名)那里,老周看了一眼监控说:「服务器没报警,流量也稳,应该不是我们的问题。」这句话后来被证明完全不对——问题恰恰就出在我们自己身上,而且是两个叠加在一起的低级错误。
这是一家做本地生活服务的站点,页面量不小,本地服务类目的搜索流量占了大头。索引量腰斩意味着接下来几周自然流量会跟着往下塌,等流量曲线掉下去再动手就晚了。所以那天下午我们决定不猜了,直接去翻 Nginx 的访问日志。
后台说正常,日志里全是事故现场
百度搜索资源平台的索引量工具给的是一个事后统计值,更新有延迟,而且只告诉你掉了多少,不告诉你为什么掉。后台显示「正常」指的是平台服务本身没故障,跟蜘蛛抓没抓到你的页面是两码事。这个区别当时没人想明白,大家都在等后台自己恢复,一等就是一周。

访问日志不会撒谎。当晚我把前一周的 access.log 拉下来粗看了一眼,自称 Baiduspider(百度蜘蛛)的请求量并不少,但状态码很扎眼:大片的 404 和 302,中间还夹着成串的 429。情况清楚了——蜘蛛天天来,来的却大都在撞墙。
不过在分析状态码之前,还有一道必须先做的工序:分清真假蜘蛛。
真假 Baiduspider 的判定机制
网上有大量流量伪装成百度蜘蛛来爬站,有的想薅内容,有的干脆在扫漏洞。如果把假蜘蛛的请求算进抓取统计,后面所有结论都是歪的。判定真蜘蛛只有一个可靠办法:反向 DNS(Reverse DNS)验证,这也是 Google 官方推荐验证 Googlebot 的同一套思路,百度官方文档里的判定要求是一致的。
机制拆开看是这样:真蜘蛛的来源 IP 做 PTR 反查,得到的域名一定落在 baidu.com 或 baidu.jp 的子域下,形如 crawl-116-179-32-1.baidu.com。光有这一步还不够严谨,因为 PTR 记录本身可以伪造,所以要再做一次正向解析,把反查出来的域名解析回 IP,结果里必须包含原来那个 IP,两次都过才算数。UA 字段里带 Baiduspider 字样不构成任何证据,UA(User-Agent)是客户端自己填的字符串,想写什么写什么。
flowchart TD
A[日志中发现 Baiduspider UA] --> B[提取来源 IP]
B --> C[对 IP 做 PTR 反查]
C --> D{域名以 .baidu.com 或 .baidu.jp 结尾?}
D -- 否 --> E[标记为假蜘蛛]
D -- 是 --> F[对该域名做正向解析]
F --> G{解析结果包含原 IP?}
G -- 否 --> E
G -- 是 --> H[确认为真蜘蛛]
当天日志里自称 Baiduspider 的来源 IP 有 47 个,走完两步验证后只剩 9 个,其余 38 个全是伪装流量,占比超过八成。这批假流量的请求集中在站外推广落地页和搜索参数页,一看就是冲着内容库存来的。先把它们从统计里剔掉,才能看真蜘蛛到底遇到了什么。
日志分析脚本:反查验证与状态码统计
环境说明:CentOS 7,Nginx 1.20,日志为 combined 格式,主机上装有 bind-utils(提供 host 命令)。反查这步用 shell 就够,统计部分用 Python,无第三方依赖。
# 前置条件:能登录网站服务器,装有 bind-utils 或 dnsutils(提供 host 命令)
# 目标:把日志里自称 Baiduspider 的来源 IP 逐一反查验证
# 从当天日志抽出所有带 Baiduspider 的请求 IP,去重
awk -F'"' '/Baiduspider/ {print $1}' access.log | awk '{print $1}' | sort -u > spider_ips.txt
# 逐个 IP 做反向 DNS,看 PTR 记录落在哪个域
while read ip; do
ptr=$(host "$ip" 2>/dev/null | awk '{print $NF}')
echo "$ip -> $ptr"
done < spider_ips.txt
# 判定标准:PTR 必须是 *.baidu.com 或 *.baidu.jp 结尾
# 其他任何形如 baiduspider-xxx.xxx 的域名都按假蜘蛛处理
# 把两步验证都通过的 IP 手工整理进白名单文件
cat > spider_ips_verified.txt <<'EOF'
# 以下 IP 均已通过 PTR 反查 + 正向解析双重验证
# 116.179.32.x 段与 123.125.71.x 段为常见真蜘蛛来源
EOF
# 对通过反查的域名再正向解析一次,确认解析回同一个 IP
host crawl-116-179-32-1.baidu.com
# 输出地址列表里必须包含原来那个 IP,双重验证才算过
# 依赖:Python 3.8+,无第三方库
# 用途:统计真蜘蛛(IP 已通过反查验证)请求的状态码分布
# 日志格式:Nginx combined
import re
from collections import Counter
# 读取双重验证通过的真蜘蛛 IP 清单,每行一个
REAL_IPS = set(open("spider_ips_verified.txt").read().split())
# 匹配请求行里的状态码,例如 "GET /x HTTP/1.1" 404
STATUS = re.compile(r'HTTP/1\.[01]" (\d{3})')
# 匹配请求的目标 URL,用于单独统计 404 死链
PATH = re.compile(r'"(?:GET|POST|HEAD) ([^ ?]+)')
status_counter = Counter()
path_counter = Counter()
for line in open("access.log", encoding="utf-8", errors="ignore"):
# 先按 UA 粗筛,减少后续正则的运算量
if "Baiduspider" not in line:
continue
# 再按来源 IP 精筛,只留反查验证过的真蜘蛛
ip = line.split()[0]
if ip not in REAL_IPS:
continue
m = STATUS.search(line)
if not m:
continue
code = m.group(1)
status_counter[code] += 1
# 404 的目标地址单独计数,方便后续回收旧链接
if code == "404":
p = PATH.search(line)
if p:
path_counter[p.group(1)] += 1
total = sum(status_counter.values())
# 输出状态码占比,看清抓取配额被谁吃掉了
for code, n in status_counter.most_common():
print(code, n, f"{n / total:.1%}")
# 输出被抓得最多的死链 Top 20,作为回收清单的输入
for p, n in path_counter.most_common(20):
print("404", n, p)
跑完统计,问题全部摆在桌面上。
状态码分布:抓取配额被谁吃掉了
真蜘蛛两周内一共留下约 12.6 万条请求,状态码分布如下。
| 状态码 | 占比 | 典型目标 | 问题定性 |
|---|---|---|---|
| 200 | 32% | 首页、栏目页 | 正常返回 |
| 404 | 26% | 旧新闻详情页 | 迁移后链接未回收 |
| 302 | 14% | 旧域名跳转 | 应改 301 永久跳转 |
| 429 | 20% | 全站随机 | 限流模块误伤真蜘蛛 |
| 5xx 及其他 | 8% | 动态接口 | 上游偶发超时 |
抓取配额(Crawl Budget)是个容易被忽略的东西:百度给一个站点的抓取频次有上限,蜘蛛在站内每次打在 404 或 302 上,都是在消耗这份配额。配额被死链和临时跳转吃掉四成,正常页面的抓取量自然上不去,新内容进不了索引,旧内容又因为长期得不到更新抓取被逐步清退,索引量就这么一路往下掉。
顺着 404 Top 20 的地址清单往下追,两个源头很快浮出来。一个是旧链接没回收:半年前站点从旧域名迁到新域名,新闻详情页的 URL 规则改过一次,迁移时只做了首页和栏目的跳转,十几万条详情页旧地址悬空,蜘蛛手里攥着的还是旧地址,来了就是 404。302 的来源类似,旧域名当初配的是临时跳转,一直没人改成 301(永久重定向)。另一个是 robots 文件误封:对比迁移前后的 robots.txt,新文件里多了一行 Disallow: /m/。本意是封掉移动端一套废弃模板,但移动端站点实际就挂在 /m/ 目录下,这一封等于把移动端全部页面挡在抓取之外。上线两个月,/m/ 下没有任何新页面被抓过。
至于 429,是迁移时新上的限流模块干的。运维照着网上教程配了 Nginx 限流(Rate Limiting),阈值给得激进,真蜘蛛抓取频次一冲高就撞墙,撞了就收到 429。假蜘蛛倒是不受影响——它们分散在各个 IP 上,没触发单 IP 限流。这个对比很讽刺,也说明限流策略压根没考虑过搜索引擎蜘蛛的来源特征。
修复时间线:六周从 1900 回到 5100
问题清单确定后,按影响面从大到小排期动手,前两周见效最快。
timeline
title 六周修复与恢复时间线
第 1 周 : 删除 robots 中的 Disallow: /m/ : 限流模块为已验证蜘蛛 IP 开白名单
第 2 周 : 详情页旧地址批量映射 : Nginx 层下发 301 规则
第 3 周 : 旧域名 302 全部改为 301 : 抓取异常占比首次降到 10% 以内
第 4 周 : 资源平台提交新链接与死链清单 : 索引量止跌回升到 2600
第 5 周 : /m/ 移动端页面重新被抓取 : 索引量到 3700
第 6 周 : 常规内容更新节奏恢复 : 索引量回到 5100
第 1 周先把 robots.txt 里的 Disallow: /m/ 删掉,这是零成本动作;同一周给限流配置加上了已验证蜘蛛 IP 段的白名单,429 从此归零。第 2 周到第 3 周处理旧链接,十几万条详情页旧地址按规则映射到新地址,直接在 Nginx 层做批量 301,同时把旧域名的 302 全部改成 301。第 4 周在搜索资源平台提交新链接和死链清单,让百度那边尽快更新对站点的认知。第 5、6 周基本是等,索引量从第三周末开始抬头,第 6 周末回到 5100,比掉之前的 4200 还多出 900——迁移后一直没被抓的移动端页面这回全进来了。
修复前后的关键数据对比:
| 指标 | 修复前(8 月中) | 修复后(10 月初) |
|---|---|---|
| 真蜘蛛日均抓取次数 | 约 9000 | 约 21000 |
| 404 占比 | 26% | 3% |
| 429 占比 | 20% | 0 |
| 302 占比 | 14% | 1% |
| 百度索引量 | 1900 | 5100 |
几条经验写给同行,少走弯路。索引量下降时别只盯后台,第一时间去服务器拉日志,日志是最接近事实的数据源。任何迁移,301 重定向和 robots 检查要当成发布清单的固定项,不能靠事后补。限流策略必须给搜索引擎蜘蛛留通道,否则等于亲手把蜘蛛往外推。真假蜘蛛的判定只能靠 DNS 反查加正向验证,UA 靠不住,这条没有变通余地。
顺带说说 GEO:AI 爬虫的日志长得不一样
这套日志分析方法同样适用于盯 AI 爬虫(GEO,Generative Engine Optimization 关注的那批机器人),但识别方式有差别。搜索引擎蜘蛛普遍提供官方反向 DNS 验证,百度蜘蛛认 baidu.com 和 baidu.jp,Googlebot 认 googlebot.com 和 google.com;而不少 AI 爬虫的反查验证要么没提供,要么域名不统一,GPTBot、ClaudeBot 这类主要靠 UA 加已知 IP 段对照来识别,误判率天然高一些。关注点也不同:搜索引擎蜘蛛看重抓取频次与收录规模,AI 爬虫更在意内容能不能被引用进生成的答案,日志里的地址分布、命中页面类型差别都很大。传统 SEO 和 GEO 短期内是两套并行的事,别混在同一个仪表盘里看。
误区澄清或趋势预判
两个常见误区顺手澄清。有人认为索引量下降一定是内容质量问题,先改文章再说——如果抓取环节满是 404 和 429,内容写得再好蜘蛛也拿不走,先修抓取链路再看内容,顺序不能反。也有人觉得给蜘蛛放开抓取会让服务器压力变大,实际上配合正确的限流白名单和缓存策略,蜘蛛的请求模式相当规律,压力完全可控。
往后看,搜索引擎对站点质量的判断会越来越依赖抓取环节的信号:返回 410 和正确 301 的站点,比满站 404 加 302 的站点,在同等内容条件下拿到的抓取配额只会差距更大。日志分析这门老手艺,做站点的人值得练熟。
参考与延伸
- 百度搜索资源平台学院(官方教程与规范入口):https://ziyuan.baidu.com/college/index
- Google 官方对验证 Googlebot 的反查 DNS 方法说明(思路与百度蜘蛛一致):https://developers.google.com/search/docs/crawling-indexing/verify-googlebot
- 百度搜索资源平台(索引量工具与反馈中心):https://ziyuan.baidu.com/
百度收录、Baiduspider、日志分析、抓取配额、Nginx、301 重定向