很多站长发现,封 IP、封 User-Agent 之后爬虫照样在抓——因为现在的采集器会轮换代理 IP、伪造浏览器 UA、甚至模拟点击间隔。要抓住这类”伪装成正常用户”的爬虫,只能从行为特征入手。本文用 8 个问答讲清行为识别爬虫的完整实战方法。
Q:爬虫为什么要伪装成正常用户?
因为最简单的拦截手段都基于”身份特征”,改掉身份就绕过了:封 IP 就换代理池,封 UA 就抄一个 Chrome 的 UA 字符串,限频就降速到单 IP 看起来正常。身份可以伪造,行为规律很难伪造——真人访问是跳跃、随机、有取舍的;程序采集是遍历、均匀、全量的。行为识别就是抓这个差异。
Q:从哪些维度能区分真人访问和伪装爬虫?
核心看三个维度,命中两个以上基本可以确认:
- 访问节奏:真人请求间隔忽长忽短,且不会在凌晨 3 点还保持匀速;爬虫间隔高度均匀(比如固定 300ms 或 1s),或者取消掉间隔后并发爆发;
- 路径规律:真人从入口页进,浏览热门内容后跳走;爬虫按 ID 顺序、翻页参数连续递增地遍历,或者直接请求大量详情页却从不访问首页;
- 资源加载:真人会加载 CSS/JS/图片/字体;纯 HTTP 采集器通常只抓 HTML 和接口,日志里只有
.html请求,没有任何静态资源记录。
这三个维度里,”从不加载静态资源”是最硬的一条证据,也是最容易在日志里看到的。
Q:日志里需要哪些字段才能做行为识别?
默认日志有 IP、时间、请求、UA、状态码、大小,但缺少”请求间隔”和”响应耗时”,分析会麻烦。建议在 Nginx 里加上这两个字段:
log_format behavior '$remote_addr | $time_iso8601 | $request | '
'$status | $body_bytes_sent | '
'$request_time | $upstream_response_time | '
'"$http_user_agent" | "$http_referer"';
access_log /var/log/nginx/access.log behavior;
关键字段说明:$request_time 是本次请求耗时,被采集的页面通常响应偏慢;$upstream_response_time 是后端处理耗时,能看出是否在打数据库;$http_referer 为空说明是直接拼 URL 访问,真人在站内跳转一般带来源页。配好后 nginx -s reload 生效。
Q:怎么用命令快速找出”节奏过于均匀”的爬虫?
先按 IP 统计请求量,找出头部可疑对象:
awk -F' \| ' '{print $1}' access.log | sort | uniq -c | sort -rn | head -20
再看某个可疑 IP 的请求时间分布,观察间隔是否均匀:
grep "1.2.3.4" access.log | awk -F' \| ' '{print $2}' | head -40
如果秒级时间戳之间的差值几乎恒定(例如永远是 0.3 或 1.0 秒),就是典型的程序节流特征。反过来,真人访问的时间差是杂乱分布。再补一条判断资源加载的命令:
# 看该 IP 有没有请求过静态资源
grep "1.2.3.4" access.log | grep -cE '\.(css|js|png|jpg|gif)'
# 结果为 0 或极低,基本可判定为采集器
Q:User-Agent 和 Referer 还能不能用来辅助判断?
能,但只能当辅助证据,不能单独下结论。实战中看三类异常:
- UA 与行为矛盾:声称是 Chrome,但从不加载 CSS/JS,从来不带 Referer;
- UA 版本老旧或异常:大量请求使用几年前的老版本 UA,或 UA 里出现 python-requests、Go-http-client、curl 等工具标识;
- UA 全局统一:几十万请求只有一个 UA,且该 UA 与真实浏览器版本占比明显不符。
确认后可以先用黑名单止血(放在 server 块最前面,命中即拒绝,开销最小):
if ($http_user_agent ~* "(python-requests|Go-http-client|curl|wget|scrapy|java/)") {
return 403;
}
# 或直接封 IP 段(注意用真实网段,别照抄示例)
deny 1.2.3.0/24;
注意 if 在 Nginx 里属于低效写法,高流量站点应改用 map 或 geo 预先判定,或直接放到 CDN/WAF 规则里,避免每次请求都做正则匹配。
Q:识别出爬虫之后,应该怎么处置?
不建议一律 403,分级处置对 SEO 和业务更友好:
- 一级(疑似):只记录不拦截,标记 IP 进入观察名单,方便做趋势分析;
- 二级(轻度):限速,用
limit_req把该来源压到很低频率,让它抓得极慢:
limit_req_zone $binary_remote_addr zone=botzone:10m rate=5r/m;
location / {
limit_req zone=botzone burst=10 nodelay;
limit_req_status 429;
}
- 三级(确认为采集):返回假数据或空页面,让抓到的内容没有价值,同时对核心内容加登录可见;
- 四级(恶意高强度):直接封 IP / IP 段,并在边缘节点拦截,避免消耗源站资源。
分级的意义在于:真正要防的是”批量、高频、全量”的采集,而不是偶发的一两次抓取,处置过重反而增加自己的运维成本。
Q:伪装成百度蜘蛛的采集器单靠行为能识别吗?
行为特征能帮上忙,但最终要靠身份核验。做法是先按行为筛出可疑对象,再做身份反查:真搜索引擎蜘蛛会做反向 DNS 解析,主机名落在官方域名下(如 baiduspider 对应的官方域),且正向解析能回到原 IP;伪造者通常只能改 UA 字符串,反向解析对不上。核验命令示例:
# 反向解析出主机名
dig -x 1.2.3.4 +short
# 再正向解析该主机名,看是否回到同一 IP
dig +short <上一步返回的主机名>
正反解都能对上,才认定为真蜘蛛并加入白名单;对不上的,即使 UA 一模一样也按普通爬虫处置。核验结果建议定期批量跑一遍,因为伪装的 UA 会不断更新。
Q:还有什么补充建议?
三点经验。第一,先量化再动手:不查日志就封 IP,很容易误伤真实用户和搜索引擎,先跑一周统计,把 Top IP、UA 分布、静态资源请求率三个指标摸清,再定规则。第二,把拦截往边缘挪:源站做行为识别要消耗服务器资源,而 CDN/WAF 节点可以在边缘直接完成频率、指纹、行为判断,源站只收干净流量,被采集时也不影响正常访客。第三,接受”防不干净”这个事实:有能力组建代理池和指纹池的采集方,技术上很难彻底拦住,防护的合理目标是让采集变慢、变贵、变残缺,而不是追求 100% 拦截。把核心数据做权限分级、把高频页面做缓存,往往比加更复杂的规则更有效。