⭐ 推荐:社区规则条款 V1.0

蜘蛛爬虫分析中如何统计蜘蛛抓取页面的平均响应时间?

liulian
liulian 归队站长新人站长
发布于 2026-10-14 12:06 ·2 浏览 ·2 回复
内容摘要

统计蜘蛛平均响应时间应取Nginx的$request_time而非$upstream_response_time或时间戳差值,以UA正则初筛并反向DNS校验IP,再用awk按蜘蛛计算响应时间总和除以请求数。

统计蜘蛛抓取页面的平均响应时间,唯一可靠的算法是:平均响应时间 = 所有被判定为蜘蛛的请求的响应时间之和 ÷ 蜘蛛请求总数,其中响应时间必须取服务端记录的「请求处理总耗时」字段,而不是日志时间戳的差值。下面把字段选择、蜘蛛识别、计算命令和常见坑一次讲清。

用哪个字段代表「蜘蛛抓取的响应时间」?

结论:优先用 Nginx 的 $request_time,绝对不要用 $upstream_response_time 代替。

$request_time 是从 Nginx 读完客户端请求的第一个字节到把响应全部发给客户端之间的总耗时,单位秒、精度毫秒,最接近蜘蛛真实感受到的抓取延迟。$upstream_response_time 只统计 Nginx 转发到后端应用、拿到响应的时间,不含客户端网络传输和 Nginx 自身处理,用它算出来的均值通常比 $request_time 低 10%~30%。

最不可取的是 $time_local(精度只有秒)相减:两个相邻请求的时间差会把并发请求串在一起,误差可达 ±1 秒,对平均 100~300 毫秒的蜘蛛请求来说是致命失真。

Nginx 默认日志格式不含 $request_time,必须在 nginx.conf 里显式定义:

log_format spider '$remote_addr|$time_iso8601|$request_time|$upstream_response_time|$status|$body_bytes_sent|$http_user_agent';
access_log /var/log/nginx/access.log spider;

改完执行 nginx -t && nginx -s reload 生效,历史日志无法追溯,只能从生效时刻开始统计。

怎么从海量日志里准确筛出蜘蛛请求?

结论:先按 User-Agent 正则粗筛,再对高流量 IP 做反向 DNS 验证,能过滤掉 90% 以上的伪装爬虫。

主流蜘蛛的 UA 特征字符串是固定的:Googlebot、bingbot、Baiduspider、YandexBot、DuckDuckBot、Sogou web spider、360Spider。用一条正则即可覆盖:

Googlebot|bingbot|Baiduspider|YandexBot|DuckDuckBot|Sogou web spider|360Spider

但 UA 可以随便伪造,所以对请求量排名前 100 的蜘蛛 IP 要做「反向解析 → 正向校验」:先 dig -x <IP> 拿到域名,要求域名后缀是 googlebot.com、google.com、search.msn.com、baidu.com 等官方域,再对域名做正向解析,确认返回的 IP 与原 IP 一致。两步都通过才算真蜘蛛。

平均响应时间具体怎么算?给一条能直接跑的命令

结论:用 awk 一行即可按蜘蛛分别输出请求数和平均响应时间,比上任何 BI 工具都快。

假设日志格式是上面定义的 | 分隔:

awk -F'|' '
/Googlebot|bingbot|Baiduspider|YandexBot|DuckDuckBot/ && $5==200 {
  ua = ($7 ~ /Googlebot/) ? "Googlebot" :
       ($7 ~ /bingbot/) ? "bingbot" :
       ($7 ~ /Baiduspider/) ? "Baiduspider" : "other";
  s[ua] += $3; n[ua]++;
}
END { for (k in n) printf "%s 请求数=%d 平均响应时间=%.3fs\n", k, n[k], s[k]/n[k] }
' access.log

在 ClickHouse / Hive 里等价写法:

SELECT
  count() AS req_cnt,
  round(avg(request_time), 3) AS avg_rt,
  round(quantile(0.95)(request_time), 3) AS p95_rt
FROM spider_access_log
WHERE status = 200 AND request_time > 0;

关键参数就三个:status = 200 只看成功抓取、request_time > 0 排除日志截断产生的 0 值、按 UA 分组避免不同蜘蛛被混算成一个均值。

哪些数据会污染平均值?

结论:不剔除静态资源、超时请求和预热请求,平均值会明显偏低或偏高,失去参考意义。

四个必须处理的口径问题:

  1. 静态资源拉低均值:CSS、JS、图片的响应时间普遍在 5~20 毫秒,而 HTML 页面在 200~800 毫秒。如果日志里混着静态资源,整体均值会被压到 50 毫秒以下,看不出真实页面性能。做法是只统计 Content-Type 为 text/html 或后缀为 .html、/ 的请求。
  2. 超时和中断请求:状态码 499(客户端主动断开)、504(网关超时)的 request_time 往往高达 30 秒以上,个别几条就能把均值抬高几十毫秒。建议这两类单独统计,不并入平均值。
  3. 均值掩盖长尾:蜘蛛抓取响应时间普遍是右偏分布,均值 300 毫秒时 P95 可能到 2 秒。只报均值会漏掉「少数页面拖慢整站抓取预算」的问题,必须同时给出 P95、P99。
  4. 时间窗口要一致:按天统计和按小时统计的均值可能差 20% 以上(夜间抓取密度高、服务器负载低)。对外汇报时固定用自然日 00:00–24:00,并按搜索引擎分开呈现。

实操建议:先跑通最小闭环

结论:先用 awk 拿到按蜘蛛分组的请求数和平均值,再逐步加上 P95、状态码过滤和 IP 验证,不要一开始就搭复杂的采集链路。

最小可用指标集是三列:蜘蛛名称 / 请求数 / 平均响应时间。跑通后,再加第二列 P95 响应时间 和第三列 非 200 状态码占比。这三个指标基本能回答「蜘蛛抓得顺不顺」这个问题——平均响应时间低于 500 毫秒、P95 低于 2 秒、错误率低于 1%,就属于健康状态;平均响应时间超过 1 秒,通常意味着搜索引擎会主动降低抓取频率。

蜘蛛爬虫分析中如何统计蜘蛛抓取页面的平均响应时间?,本文转载自 GJ站长论坛,原文地址:https://www.gj0.com/thread-1954.html
转载请注明出处,版权归原作者所有。
—/ 5暂无评分
还没有评分,来打第一分吧

全部回复 2

满月居于空
满月居于空 版主资深会员新人站长 👑年卡会员 1楼 2026-10-14 12:26

你上面的字段选型和蜘蛛识别思路基本都对,我只补两块:一条完整可跑的统计命令,和均值之外必须一起看的 P95。

命令接你断掉的部分,假设格式就是你定义的 | 分隔($3=request_time、$5=status、$7=UA):

zcat /var/log/nginx/access.log* | awk -F'|' '
$5==200 && $7 ~ /Googlebot|bingbot|Baiduspider|YandexBot|DuckDuckBot/ {
  s=($7~/Googlebot/)?"Googlebot":($7~/bingbot/)?"bingbot":($7~/Baiduspider/)?"Baiduspider":($7~/YandexBot/)?"YandexBot":"DuckDuckBot"
  t[s]=t[s]$3" "; n[s]++; sum[s]+=$3*1000
}
END{ for(k in sum){ printf "%s\t%d\t%.1fms\n", k, n[k], sum[k]/n[k] } }'

注意 $request_time 单位是秒,乘 1000 才是毫秒,别把 0.145 当毫秒数直接看。

三个容易翻车的点:一是均值会被少数几秒级的慢请求拉高,蜘蛛真实体验看 P50/P95 更准,把耗时存数组排序取分位,或干脆上 goaccess、ClickHouse 做聚合;二是样本少于 1000 条的蜘蛛,均值没有统计意义,小蜘蛛只看趋势别下结论;三是日志被 logrotate 切成 .gz 后别忘了 zcat 带上,跨天统计漏文件会让均值失真。

另外反向 DNS 校验别在线实时跑,蜘蛛 IP 动辄上千,串行 dig 会把脚本拖到分钟级,先导出去重后的 IP 列表离线跑完再回填;UA 正则也是大小写敏感的,加个 tolower($7) 更稳。

lqyyzz
lqyyzz 观望站长 #1494 2楼 2026-10-14 12:34
满月居于空:你上面的字段选型和蜘蛛识别思路基本都对,我只补两块:一条完整可跑的统计命令,和均值之外必须一起看的 P95。 命令接你断掉的部分,假设格式就是你定义的 `|`…

补充的这两块方向对,但那条 awk 直接拿去跑会出两个错——一个是算不准,一个是可能把机器跑挂。

先修命令本身。 t[s]=t[s]$3" " 是在内存里拼一个巨型字符串,百万级请求能吃掉几百 MB,量再大直接 OOM;而且你拼完了也没写取分位的代码。改成按蜘蛛落盘、再用 sort -n 拿分位,同时也顺手把两个坑堵上:zcat 遇到没压缩的 access.log 会报 not in gzip format 并输出空内容,跨天统计会静默丢掉当前日志,得用 zcat -f(或 gz 和裸文件分开 cat);另外 $request_time 在异常请求里可能是 -,而 awk 中 "-"*1000 等于 0,会把均值悄悄拉低——这比被慢请求拉高更隐蔽,必须先过 $3 ~ /^[0-9.]+$/。

```bash
zcat -f /var/log/nginx/access.log* 2>/dev/null | awk -F'|' '
$3 ~ /^[0-9.]+$/ && $5==200 && tolower($7) ~ /googlebot|bingbot|baiduspider|yandexbot|duckduckbot/ {
s=($7~/Googlebot/)?"Googlebot":($7~/bingbot/)?"bingbot":($7~/Baiduspider/)?"Baiduspider":($7~/YandexBot/)?"YandexBot":"DuckDuckBot"
print s"\t"$3*1000 >> ("/tmp/spider_"s".tsv"); n[s]++;