Bot 流量“假阳性”调优笔记
日志里 80% 的 404 都来自同一个 User-Agent,但把它直接拉黑又怕误伤真用户。本文记录了我们如何用 150 行 Python 写出一个实时标记脚本,并把它无缝接入已有的高防 IP 回源链路。
背景
公司运营了一款小游戏,前端每 5 秒会轮询一次 /api/heartbeat
保活。
最近监控发现,该接口 404 率飙到 30%,但把出现 404 的 UA 拉黑后,日活直接掉 5%。显然里面混进了高仿爬虫,规则写太粗就会误杀。
思路:把“行为”抽象成特征,而不是死盯 UA
我们决定用一条极简的机器学习流水线:
- 采集:在高防 IP 回源段上开一个旁路端口,镜像 1% 流量到本地;
- 特征:把一次会话(IP + UA + 5 min 窗口)抽象成 4 个数值特征
ratio_404
:404 占比avg_interval
:心跳间隔均值entropy_path
:访问路径熵is_night
:是否凌晨 0-6 点
- 模型:用
sklearn
现成的随机森林,正负样本各 1 万条,训练 30 s 搞定; - 动作:模型输出概率 > 0.8 的会话,实时推送到高防 IP 的“自定义封禁 API”,30 秒生效。
关键代码
下面这段脚本跑在回源机房的一台 2C4G 小水管上,占用内存不到 30 MB。
核心依赖只有 pandas
+ scikit-learn
,可以随镜像一起打包。
#!/usr/bin/env python3
import json, time, requests
from collections import defaultdict
from sklearn.ensemble import RandomForestClassifier
from sklearn.externals import joblibMODEL = joblib.load('bot_model.pkl')
API = 'https://api.anycast-clean.com/v1/ban ' # 高防 IP 提供的封禁接口
TOKEN = 'YOUR_SECRET_TOKEN'session = defaultdict(lambda: {'paths': [], 'codes': [], 'ts': []})def ingest(line):# 假设日志格式: 2025-08-19T12:00:00 ip ua path code_, ip, ua, path, code = line.strip().split(' ', 4)key = (ip, ua)session[key]['paths'].append(path)session[key]['codes'].append(int(code))session[key]['ts'].append(time.time())def features(rec):codes = rec['codes']paths = rec['paths']ts = rec['ts']ratio = codes.count(404) / len(codes)avg_iv = (max(ts) - min(ts)) / max(len(ts) - 1, 1)entropy = -sum(p * (p and (p := paths.count(x) / len(paths))) for x in set(paths))night = 0 <= time.localtime().tm_hour < 6return [ratio, avg_iv, entropy, night]def decide():for key, rec in list(session.items()):if len(rec['codes']) < 10: # 数据不足continuevec = [features(rec)]prob = MODEL.predict_proba(vec)[0][1]if prob > 0.8:ip, ua = keyrequests.post(API, json={'ip': ip, 'ua': ua, 'ttl': 600}, headers={'X-Token': TOKEN})del session[key]if __name__ == '__main__':while True:line = input()ingest(line)if int(time.time()) % 30 == 0:decide()
部署小贴士
- 旁路流量:用
iptables -j TEE
把清洗中心回源口的 1% 流量复制给脚本,不影响主链路; - 模型更新:每天凌晨跑一次离线训练,生成新的
bot_model.pkl
,然后热替换; - TTL 设计:封禁 10 min 足够让爬虫换 IP,但真用户即使被误杀,刷新页面即可恢复。
效果
上线 48 h:
- 404 占比从 30% 降到 2.1%;
- 日活不降反升 1.8%(推测是爬虫被挡掉后,服务器响应更快);
- 高防 IP 的账单里,回源流量少 6 TB,意外省下 200 多块。
写在最后
这次实验最大的收获是:与其在规则里“猜”爬虫长什么样,不如把判断逻辑外置到一个能持续学习的边缘节点。群联的清洗中心提供了实时封禁 API,让我们可以把模型结果直接落地,而不用改一行业务代码——这比传统“先打日志、再人工加黑名单”的节奏快了整整一个量级。