GoAccess 实战:用 Nginx 日志诊断搜索引擎爬虫抓取与收录问题

搜索引擎爬虫到底有没有来

个人站长做 SEO,最常见的困惑不是「怎么优化」,而是「我改了这么多,搜索引擎到底有没有看到」。很多人靠猜:盯着百度站长平台或者 Google Search Console 的图表看,但那些数据往往滞后几天,而且不会告诉你爬虫具体卡在哪一步。

其实答案就藏在你自己的 Nginx 访问日志里。搜索引擎爬虫每一次来访,都会在日志里留下一条带 User-Agent 的记录,包括它请求了哪个 URL、返回了什么状态码、消耗了多少时间。把这些记录分析清楚,收录问题基本就能定位到具体原因。本文用 GoAccess 这个工具,搭一套属于自己的爬虫诊断面板。

为什么不用现成的统计工具

百度统计、Google Analytics 这类前端统计工具依赖 JavaScript 执行,而搜索引擎爬虫绝大多数不执行 JS。这意味着你在统计后台看到的流量,几乎全是真人访问,爬虫流量是完全缺失的。更麻烦的是,你无法从这些工具里得知爬虫被 403 挡了多少次、被 404 挡了多少次。

服务端日志则记录了全部请求,不管是人是爬虫,不管是 200 还是 500。这是唯一完整的真相来源。

先确认日志格式够用

默认的 Nginx combined 格式包含 IP、时间、请求行、状态码、Referer 和 User-Agent,对爬虫分析来说已经足够。但如果你想要响应时间,就需要自定义格式。推荐在 nginx.confhttp 块中定义:

log_format seo_analysis '$remote_addr - $remote_user [$time_local] '
                        '"$request" $status $body_bytes_sent '
                        '"$http_referer" "$http_user_agent" '
                        'rt=$request_time uct=$upstream_connect_time '
                        'urt=$upstream_response_time';

然后在站点的 server 块里引用:

access_log /www/wwwlogs/zz1984.log seo_analysis;
access_log /www/wwwlogs/zz1984-crawler.log seo_analysis;

改完执行 nginx -t && nginx -s reload。这里需要注意,日志格式一旦变更,新老日志的字段数会不一致,GoAccess 解析时会报 malformed 记录。所以建议变更时把旧日志归档改名,让新日志从零开始。

用 grep 做最直接的爬虫体检

在装工具之前,先用最朴素的命令确认爬虫是否真的在访问。下面这几条命令能立刻给出答案:

# 统计各搜索引擎爬虫的访问次数
grep -iE 'Baiduspider|Googlebot|bingbot|Sogou web spider|360Spider|YisouSpider' \
  /www/wwwlogs/zz1984.log | wc -l

# 分别统计
for ua in Baiduspider Googlebot bingbot YisouSpider; do
  echo -n "$ua: "
  grep -c -i "$ua" /www/wwwlogs/zz1984.log
done

如果某个爬虫的数字是 0,那问题很简单:它压根没来,不用再往下查收录了,应该去检查 robots.txt 是否全站 Disallow、是否有 IP 层防火墙拦截、以及服务器是否长时间不可达。

确认爬虫来过之后,接着看它拿到的是什么状态码,这往往就是收录失败的直接原因:

# 查看百度爬虫遇到的所有非 200 响应
grep -i 'Baiduspider' /www/wwwlogs/zz1984.log \
  | awk '{print $9}' | sort | uniq -c | sort -rn

# 列出被 403 挡掉的 URL
grep -i 'Baiduspider' /www/wwwlogs/zz1984.log \
  | awk '$9 == 403 {print $7}' | sort -u | head -50

这里的 $9 是状态码字段,$7 是请求路径。如果发现大量 403,通常意味着站点装了 WAF 或者 Nginx 里用了 deny 规则误伤爬虫;如果大量 429,是限流规则太激进;如果大量 503,则是服务器过载或者 PHP-FPM 队列打满,爬虫来了也抓不到内容。

爬虫 IP 反查:识别真假爬虫

日志里写着 Baiduspider 的请求,未必真的来自百度。伪装 UA 的采集器非常多,它们消耗你大量带宽。识别方法是对 IP 做反向 DNS 解析,真百度爬虫的 IP 反解一定落在 baidu.com 域名下:

# 提取访问最频繁的爬虫 IP
grep -i 'Baiduspider' /www/wwwlogs/zz1984.log \
  | awk '{print $1}' | sort | uniq -c | sort -rn | head -20

# 对某个 IP 做反向解析
dig -x 220.181.108.15 +short
# 正常结果类似:baiduspider-220-181-108-15.crawl.baidu.com.
# 如果返回的是某个 IDC 的域名,那就是伪造的

批量验证可以写个小脚本:

grep -i 'Baiduspider' /www/wwwlogs/zz1984.log \
  | awk '{print $1}' | sort -u | while read ip; do
      host=$(dig -x "$ip" +short)
      case "$host" in
        *.baidu.com.) echo "REAL $ip $host" ;;
        *)            echo "FAKE $ip $host" ;;
      esac
    done | tee /tmp/spider_check.txt

对确认为伪造的 IP 段,可以在 Nginx 里直接封掉,既省带宽也让日志更干净:

# 在 server 块或 http 块中
deny 1.2.3.0/24;
deny 45.67.89.10;

安装并配置 GoAccess

GoAccess 是一个实时日志分析工具,能把 Nginx 日志渲染成可交互的 HTML 报告,非常适合放在服务器上做常驻诊断面板。安装方式按系统不同略有差异:

# Debian / Ubuntu
apt-get install -y goaccess

# 或者用官方源拿最新版
wget -O - https://deb.goaccess.io/gnugpg.key | apt-key add -
echo "deb https://deb.goaccess.io/ $(lsb_release -cs) main" \
  > /etc/apt/sources.list.d/goaccess.list
apt-get update && apt-get install -y goaccess

用配置文件的方式运行,避免每次敲一长串参数。新建 /etc/goaccess/zz1984.conf

time-format %H:%M:%S
date-format %d/%b/%Y
log-format %h - %^ [%d:%t %^] "%r" %s %b "%R" "%u" "rt=%T"

output /www/wwwroot/stat/index.html
real-time-html true
port 7890
ws-url wss://stat.zz1984.com/ws

html-prefs {"theme":"bright","perPage":50}
html-report-title SEO 爬虫诊断
ignore-crawlers false
anonymize-ip true

这里 ignore-crawlers false 是关键:GoAccess 默认会把已知爬虫单独归类,我们要的就是看清爬虫的行为,所以不要忽略它们。anonymize-ip true 则是在报告里把 IP 最后一段打码,避免面板万一暴露导致访客隐私泄露。

启动实时模式:

goaccess /www/wwwlogs/zz1984.log \
  --config-file=/etc/goaccess/zz1984.conf \
  --real-time-html \
  --daemonize

需要注意的坑有三个。第一,log-format 必须和 Nginx 里定义的格式严格逐字符对应,包括空格和引号,任何差异都会导致解析失败,日志里会出现大量 Token 'x' doesn't match specifier。第二,实时模式的前端通过 WebSocket 接收数据,如果站点走 HTTPS,ws-url 必须用 wss://,否则浏览器会因混合内容拦截而白屏。第三,GoAccess 读取日志需要权限,如果 nginx 日志是 600 且属主是 www,用 root 跑没问题,但用其他用户跑就要加入 adm 组或者放宽权限。

从报告里读收录问题的信号

GoAccess 报告里对 SEO 诊断最有价值的几个板块是:

Requests by Status Code。 如果 404 比例异常高(超过 5%),说明站内有大量死链,爬虫在浪费抓取配额。可以拿日志导出 404 URL 清单,做 301 定向到相关页面:

grep ' 404 ' /www/wwwlogs/zz1984.log \
  | awk '{print $7}' | sort | uniq -c | sort -rn | head -50 \
  > /tmp/404_list.txt

Top Requested Files。 如果爬虫反复抓取的 URL 高度集中,而新发布的内容几乎没被请求过,说明内链结构有问题——新文章没有被站内链接指向,爬虫发现不了。解决办法是在首页、分类页、相关文章模块里给新内容足够的曝光入口。

Time Distribution。 观察爬虫的抓取时段分布。如果集中在深夜且每小时只有几条,说明抓取频次被限制在很低的水位,站点的整体权重或响应速度还有提升空间。

Bandwidth。 如果爬虫占用了大量带宽但页面收录没有增加,说明它在抓取无价值页面,比如分页、标签云、搜索结果的 URL 参数页。这时应该在 robots.txt 里屏蔽这些页面,把抓取配额留给真正的内容页。

robots.txt 与日志的配合

一个常见的错误是:robots.txt 里屏蔽了某个目录,但日志显示爬虫仍在大量请求它。原因是对 robots.txt 的 Disallow 只是「请求不要再抓」,并不能阻止之前的旧链接被抓到,也不能阻止恶意爬虫。正确的做法是 Disallow + 页面加 noindex 双管齐下,但要注意两者不能同时用——被 Disallow 的页面爬虫看不到页面内容,也就看不到 noindex 标签,标签会失效。所以对需要保留但不想收录的页面,应该用 noindex 而不是 Disallow。

验证 robots.txt 是否正确生效:

curl -s https://www.zz1984.com/robots.txt?_=$(date +%s)

# 检查自己的写法是否合法(会指出语法错误和冲突规则)
curl -s -A "Googlebot" https://www.zz1984.com/robots.txt

收录慢与新页面堆积的典型信号

除了状态码,日志还能告诉你一个更隐蔽的问题:新发布的文章明明在 sitemap 里,爬虫却迟迟不来抓。判断方法是把 sitemap 中的 URL 列表与日志中出现过的 URL 做交集运算。如果新文章发布的头三天在日志里完全找不到记录,说明发现机制出了问题,而不是抓取机制出了问题。

常见原因有三种。第一,sitemap 没有在 robots.txt 里声明,爬虫不知道去哪里找这份清单。第二,sitemap.xml 返回的 Content-Type 不对(比如返回了 text/html 而不是 application/xml),部分爬虫会直接忽略。第三,站点没有主动向搜索引擎提交 sitemap,只靠爬虫自己周期性巡站,而这个周期可能长达数周。

用下面这条命令可以直接验证爬虫是否抓取过 sitemap:

grep -i 'sitemap' /www/wwwlogs/zz1984.log | tail -20

# 确认 sitemap 的可访问性和类型
curl -sI https://www.zz1984.com/sitemap.xml | grep -i content-type
# 正确应返回:content-type: application/xml

另一个高频信号是「抓取深度过浅」。如果日志显示爬虫只抓过首页和少量一级页面,从不进入文章详情页,那多半是内链结构的问题:首页文章列表用了 JavaScript 动态加载,或者链接用了 onclick 跳转而不是标准的 <a href>。爬虫不执行 JS 的情况下,这些链接对它来说根本不存在。修复方式是把关键链接改成服务端渲染的标准超链接,保证在关闭 JS 的情况下页面依然有完整的可点击路径。

建立每日爬虫监控

把下面这段脚本放进 crontab,每天生成一份爬虫概况,改动 SEO 之后能立刻看到效果:

#!/bin/bash
LOG="/www/wwwlogs/zz1984.log"
OUT="/var/log/seo/crawler-$(date +%F).txt"
mkdir -p /var/log/seo

{
  echo "=== 报告日期: $(date '+%F %T') ==="
  echo
  echo "--- 各爬虫请求量 ---"
  for ua in Baiduspider Googlebot bingbot YisouSpider 360Spider Sogou; do
    printf "%-14s %s\n" "$ua" "$(grep -c -i "$ua" "$LOG")"
  done
  echo
  echo "--- 爬虫状态码分布 ---"
  grep -iE 'spider|bot' "$LOG" | awk '{print $9}' \
    | sort | uniq -c | sort -rn
  echo
  echo "--- 被爬虫请求最多的 20 个页面 ---"
  grep -iE 'spider|bot' "$LOG" | awk '{print $7}' \
    | sort | uniq -c | sort -rn | head -20
  echo
  echo "--- 4xx/5xx 合计 ---"
  grep -iE 'spider|bot' "$LOG" | awk '$9 >= 400' | wc -l
} > "$OUT"

# 只保留 30 天
find /var/log/seo -name 'crawler-*.txt' -mtime +30 -delete

加进定时任务:

10 6 * * * /bin/bash /opt/seo/crawler-report.sh

每天六点生成前一天的概况,改动 robots.txt、调整内链、发布新文章之后,第二天就能从数字上看到爬虫反应是否积极,而不是干等站长平台的延迟数据。

结语

SEO 的效果反馈慢,但爬虫的行为反馈是即时的。日志里的每一条记录都在告诉你:爬虫来没来、拿到了什么、卡在哪里。与其反复猜测算法喜好,不如先把这套基于日志的观测体系建起来——把不可观测的问题变成可测量的数字,剩下的优化才有方向。

Last modification:September 20th, 2026 at 07:24 pm

Leave a Comment