KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA

05 · 日志诊断:爬虫到底来没来 — keel 龙骨

本章目标:能从访问日志里回答三个问题——谁来了、来了多少次、抓到了什么。 验收标准:跑出各爬虫 UA 的抓取量统计,并能定位 404/5xx/超时都发生在哪些路径。

本章目标:能从访问日志里回答三个问题——谁来了、来了多少次、抓到了什么。
验收标准:跑出各爬虫 UA 的抓取量统计,并能定位 404/5xx/超时都发生在哪些路径。

为什么这一章重要:GSC 只能告诉你 Google 看到了什么,百度平台只告诉你百度的事。服务器日志是唯一的中立数据源——所有爬虫的请求都在那里,不管它属于哪家。当"搜不到"时,日志是最快区分"爬虫没来"和"来了但抓不动"的手段。


一、日志里要有什么

nginx 默认日志格式(combined)包含 $http_user_agent 与 $status,够用了。确认你的配置里有 access_log:

access_log /var/log/nginx/learning.access.log;

若想额外看响应时间与上游耗时(诊断抓取超时很有用),自定义一个格式:

log_format main '$remote_addr - [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" '
                '"$http_user_agent" rt=$request_time urt=$upstream_response_time';
access_log /var/log/nginx/learning.access.log main;

二、统计各爬虫的抓取量

LOG=/var/log/nginx/learning.access.log

# 全量 UA 排行(看谁在访问你的站)
awk -F'"' '{print $6}' $LOG | sort | uniq -c | sort -rn | head -30

# 只看搜索引擎与 AI 爬虫
grep -iE 'googlebot|bingbot|baiduspider|sogou|bytespider|GPTBot|ClaudeBot|PerplexityBot|Applebot' $LOG \
  | awk -F'"' '{print $6}' \
  | sed -E 's/.*(Googlebot|Bingbot|Baiduspider|Bytespider|GPTBot|ClaudeBot|PerplexityBot|Sogou).*/\1/' \
  | sort | uniq -c | sort -rn

这个统计能立刻回答:"爬虫到底来没来"。


三、看爬虫抓了哪些路径、拿到了什么状态码

# Googlebot 的请求:路径 + 状态码 分布
grep -i 'googlebot' $LOG \
  | awk '{print $7, $9}' | sort | uniq -c | sort -rn | head -30

(字段位置随 log_format 变化;先用一条样本确认 $7 是 path、$9 是 status。)

重点看三类异常

# 1. 爬虫遇到的 404(sitemap 里混进了死链,或内链指向已删除内容)
grep -iE 'googlebot|bingbot|baiduspider' $LOG | awk '$9==404 {print $7}' | sort | uniq -c | sort -rn | head

# 2. 爬虫遇到的 5xx(真正的收录杀手)
grep -iE 'googlebot|bingbot|baiduspider' $LOG | awk '$9>=500 {print $7, $9}' | sort | uniq -c | sort -rn | head

# 3. 爬虫遇到的 301/302(说明它抓到的是非 canonical 形式)
grep -iE 'googlebot|bingbot' $LOG | awk '$9==301 || $9==302 {print $7}' | sort | uniq -c | sort -rn | head

第 3 项容易被忽视:如果爬虫大量抓到 301,说明你的 sitemap 或内链里还有非 canonical 的 URL 形式,抓取预算被浪费在跳转上。正确做法是让 sitemap 与内链直接指向最终 URL。


四、验证爬虫抓到的内容(关键一步)

爬虫来了 ≠ 抓到了正文。日志能看到它请求了哪些资源:

# 某次 Googlebot 请求后,紧接着有没有请求内容 JSON / 抓取 HTML
grep -i 'googlebot' $LOG | tail -20 | awk '{print $7}'

判据:

日志里看到的 结论
只请求了 /learning/ 的 HTML,之后没有任何 /content/*.json 请求 说明正文已内联在 HTML(预渲染生效)✅
请求了 HTML 后又请求了若干 /content/*.json,但抓取后仍未收录 爬虫需要执行 JS,风险较高
只有 HTML 请求,且你的正文靠 JS 渲染 爬虫抓到的是空壳 ❌

这就是第 01 章"关掉 JS 看正文"那条自检的日志侧验证。两条证据互相印证,结论才可靠。


五、识别真假爬虫

日志里的 UA 是可以伪造的。冒牌 Googlebot 常见于扫描器。验证方法:反查 IP。

# 取 Googlebot 的来源 IP 排行
grep -i 'googlebot' $LOG | awk '{print $1}' | sort | uniq -c | sort -rn | head

# 反解域名(Google 的爬虫 IP 反向解析应为 *.googlebot.com 或 *.google.com)
dig -x <IP> +short

# 再用正向解析确认(双向 DNS 校验)
dig <反解得到的域名> +short    # 应再解析回同一个 IP

官方推荐的校验方式就是双向 DNS:反解得到 *.googlebot.com / *.google.com(百度是 *.baidu.com / *.baidu.jp),再正解回同一 IP,两条都成立才是真的。

伪造 UA 的扫描器通常反解不到这些域——直接忽略即可,不要为它们调整配置。


六、抓取预算(Crawl Budget)的实用理解

搜索引擎给每个站点的抓取配额是有限的。浪费预算的常见行为:

  1. 无限空间的 URL:带 session id、排序参数、搜索参数的 URL 都能被抓到 → 用 robots.txt 屏蔽,或用 canonical 归一
  2. 重定向链:http → https → www 两跳 → 让 sitemap 直接指向最终 URL
  3. 大量 404:及时从 sitemap 移除已删除页面
  4. 重复内容:同一内容多个 URL → canonical

对几百到几千页的站点,预算通常不是瓶颈;但让每一条 sitemap 都指向 200 的 canonical URL 是无论规模都该做的。


七、建立常态化观测

把统计做成一条命令,每个月跑一次:

# /usr/local/bin/crawler-report.sh
#!/usr/bin/env bash
LOG=/var/log/nginx/learning.access.log
DAYS=${1:-30}
SINCE=$(date -d "-$DAYS days" +'%d/%b/%Y:%H:%M:%S')

echo "== 近 $DAYS 天爬虫抓取量 =="
awk -v since="$SINCE" '{
  # 提取 [28/Sep/2026:10:00:00
  match($0, /\[[0-9]{2}\/[A-Za-z]{3}\/[0-9]{4}:/);
  ts = substr($0, RSTART+1, RLENGTH-2);
} { print }' $LOG > /tmp/recent.log

grep -iE 'Googlebot|Bingbot|Baiduspider|Sogou|Bytespider|GPTBot|ClaudeBot|PerplexityBot|Applebot' /tmp/recent.log \
  | awk -F'"' '{print $6}' \
  | grep -oiE 'Googlebot|Bingbot|Baiduspider|Sogou|Bytespider|GPTBot|ClaudeBot|PerplexityBot|Applebot' \
  | sort | uniq -c | sort -rn

echo
echo "== 爬虫遇到的非 2xx =="
grep -iE 'Googlebot|Bingbot|Baiduspider' /tmp/recent.log \
  | awk '{print $9}' | sort | uniq -c | sort -rn | head

⚠️ 上面按日期过滤的 awk 只是近似实现(nginx 时间格式解析比较麻烦)。更稳的做法是让 logrotate 每天切一个文件,然后只对最近 N 个文件做统计:

ls -t /var/log/nginx/learning.access.log* | head -7 | xargs zcat -f \
  | grep -icE 'googlebot'

八、本章验收

# 一条命令的总览
grep -iE 'googlebot|bingbot|baiduspider|bytespider|GPTBot|ClaudeBot' /var/log/nginx/learning.access.log \
  | grep -oiE 'Googlebot|Bingbot|Baiduspider|Bytespider|GPTBot|ClaudeBot' \
  | sort | uniq -c | sort -rn

如果某家计数为 0 → 回到第 03/04 章确认是否已提交。
如果抓取量正常但收录为 0 → 问题在内容与结构,进入第 06 章。

进入 keel 阅读