凌晨两点,我盯着监控后台那条近乎垂直上升的曲线,第一反应是服务器被攻击了。但翻了一圈错误日志,没有数据库异常,也没有应用报错,CPU负载也平稳——这说明访问确实到了站,只是没有触发业务逻辑。问题出在访问来源上:我在HN(一个海外知名技术社区)发了一篇关于某个开源项目的介绍帖,帖子上了首页,然后我的网站就被AI bot流量淹没了。准确说,是连续几天被各种各样的爬虫轮番扫。
HN本身能给个人站点带来真实用户流量,这是很多人梦寐以求的曝光,但很少有人会提前告诉你:帖子一旦上榜,紧随其后涌进来的不只是读者,还有成建制的AI训练爬虫、SEO抓取工具、内容采集程序。这篇博文我想把完整的经历写下来——流量从哪来、怎么识别、怎么拦截、拦截之后效果如何,以及踩过哪些想当然的坑。无论是自建博客、个人项目页,还是小公司官网,只要你想公开某个URL,这份日志都有参考价值。
1. 一篇HN帖子引发的流量风暴:先从数据异常说起
1.1 发布前的预期与发布后的现实
发帖之前我是有心理准备的。HN的读者群体对技术内容向来热情,帖子上首页后短时间内涌入几千个PV都属于正常操作。所以当时我甚至提前给nginx多开了几个worker进程,想的是别让静态页面的响应速度拖后腿。
帖子发出后的头几个小时,数据确实符合预期:来源主要是HN的跳转,用户分布在全球各地,访问路径集中在项目主页、GitHub链接和文档页。但到了后半夜,情况开始不对了。访问量没有像正常用户流量那样衰减,反而保持着一个诡异的平稳高位,更像是一台机器在匀速压测。
真正让我警觉的是页面停留时长。真实用户从HN进来,就算随便扫一眼也会停留几十秒;但那一大堆会话,几乎全部是"打开主页-然后立刻离开",持续时长普遍不到5秒,部分甚至不到2秒。再看跳出率,直接逼近100%。这个模式太整齐了,整齐得像程序里跑出来的。
1.2 监控后台显示的第一个异常信号
我的监控栈比较简单:nginx日志配合一个实时分析面板,外加几行定时脚本统计top访问来源IP。异常出现时,面板上最显眼的是某个IP段在半小时内贡献了超过6000次请求,命中路径全是同一个URL——那正是我在HN帖子里贴的主页地址。
单IP高频率请求其实不一定有问题,搜索引擎的爬虫也会这样。但高峰期挤进来的一批IP段,归属地分布很有规律:一整个C段都来自同一家云服务商,而且AS号高度集中。正常HN用户基本都走家庭宽带或移动网络,不可能清一色来自数据中心。到这一步,我基本确定这些不是"来看帖子的人",而是顺着HN露出的链接过来抓内容的机器。
1.3 为什么HN会成为AI爬虫的高发入口
后来我仔细想了想这个场景,其实很好理解。HN有实时性、有内容权重、有大量外部链接,天然就是一个发现新URL的窗口。AI训练爬虫的任务就是尽可能多地发现新页面、抓取并归档内容,它们当然会优先盯这种信息聚合地。
更关键的是,HN域名本身的权重会让新发布的URL更快进入各种爬虫队列。你在自己的博客上发一篇文章,可能要过很久才会被某些bot抓到;但同一篇文章通过HN露出后,等于给了所有爬虫一个明显的信号:这里有一个新的、可能值得抓取的页面。于是,GPTBot、ClaudeBot、Bytespider、PerplexityBot这些叫得上名字的,还有大量叫不上名字的采集程序,几乎会在同一时间段涌进来。
这个过程我后来和朋友复盘时给了他一个比喻:HN帖子就像烧烤摊上刚端出来的肉串,爬虫就是闻到味道的猫。肉串放在那里一分钟,整条街的猫都知道了。
2. AI bot流量画像:我是怎么从访问日志里认出它们的
2.1 第一层识别:User-Agent里明晃晃的"自报家门"
最常规的识别方式就是看User-Agent(以下简称UA)。有些爬虫非常老实,UA里直接写着自己是干什么的。
| 爬虫标识 | UA片段里的关键词 | 通常抓取内容 |
|---|---|---|
| GPTBot | GPTBot, OAI-SearchBot | 模型训练语料、搜索索引 |
| ClaudeBot | ClaudeBot | 模型训练语料 |
| Bytespider | Bytespider | 模型训练语料、网页存档 |
| PerplexityBot | PerplexityBot | 实时问答知识库 |
| CCBot | CCBot | Common Crawl数据集 |
| archive.org | ia_archiver | 网页存档 |
| Amazonbot | Amazonbot | 产品数据、内容聚合 |
| Google-Extended | Google-Extended | AI训练控制扩展 |
我在日志里筛了一遍,仅GPTBot这一个UA,一晚上就打了几百次请求。它访问的路径和真实用户高度重合:主页、文档、博客归档、静态资源。除了不加载图片和CSS,它几乎会把每个HTML页面都抓一遍。
2.2 第二层识别:伪装成浏览器的爬虫怎么找出来
光看UA会漏掉相当一部分流量。我遇到的情况里,有大约三成请求的UA和正常Chrome浏览器完全一样,甚至连版本号都是最新的。这些伪装的bot如果只看UA,几乎不可能和真实用户区分开。
但它们会在别的地方露馅。首先是TLS指纹,正规浏览器和常见的爬虫框架(例如某些Python的HTTP客户端)在用TLS握手时,协商参数分布区间明显不一样。其次是行为模式:真实用户打开页面后会加载静态资源,而这些伪装bot通常只请求HTML、不拉js和css,或者拉取顺序和正常浏览器完全相反。
我在日志分析里加了一个简单规则:如果某个IP请求的页面全部是text/html类型,而图片、样式表、脚本的请求数为0,就标记为可疑。这个规则抓出了不少伪装bot。它们把"像是访客"演了个开头,却没耐心把静态资源请求也演完。
2.3 第三层识别:IP行为模式的规律性破绽
IP层面的特征更容易总结。真实用户群体访问时间的分布是松散的,有人半夜来,有人早上来,没有一个统一的节奏。而AI爬虫不一样,它们会把抓取任务切成并发队列,表现为短时间内集中请求、然后停一段时间、再集中请求——像潮水一样有规律地拍打服务器。
我统计了高峰期最活跃的十几个IP段,发现它们普遍具备以下特征:
- 归属都是知名云服务商或数据中心,没有家庭宽带类IP
- 请求频率呈均匀间隔分布,部分间隔精确到毫秒级
- 同一IP会在几小时内反复抓取同一个页面
- 几乎不访问站内搜索、登录、评论等动态接口
把这些行为综合起来之后,剩下的基本就能实锤是bot了。我后来给分析脚本加了三个阈值:单位时间请求数、HTML请求占比、静态资源请求比例。三个条件同时命中,就自动把相关UA和IP段追加到封禁名单里。
2.4 流量占比的真实数据:比想象中严重得多
被"淹没"这个说法一点不夸张。我把完整一周的nginx日志做了聚合,对比了HN引流前一周和后一周的访问数据。
| 指标 | 发帖前一周均值 | 发帖后一周均值 |
|---|---|---|
| 单日总请求数 | 2.3万 | 11.8万 |
| 疑似AI bot请求占比 | 约10% | 约67% |
| 人工浏览器请求占比 | 约90% | 约28% |
| 单日独立IP数 | 1,200 | 8,400 |
AI bot流量在一周内超过了全部正常流量的总和,而且是在帖子热度已经回落之后仍然持续。这说明爬虫的抓取任务一旦排上,不会因为你帖子掉下首页就停止。
3. 应对方案实测:从robots.txt到强制验证的层层加码
3.1 第一道防线:robots.txt能不能挡住它们
很多人的第一反应是修改robots.txt,直接在文件里声明"AI爬虫不要抓我"。这个方案成本最低,但效果非常依赖对方守不守规矩。
我在robots.txt里加的内容是这样的:
User-agent: GPTBot Disallow: / User-agent: OAI-SearchBot Disallow: / User-agent: ClaudeBot Disallow: / User-agent: Bytespider Disallow: /效果如何?有一半的爬虫确实退了。GPTBot和ClaudeBot在更新队列之后明显地停止了对我的站点抓取。但另一半根本不在乎robots.txt,尤其是那些伪装成浏览器UA的采集程序,它们压根不会去读这个文件。所以我在日志里观察到一个有趣的现象:robots.txt的请求量很高,但请求之后服务器压力一点没减。
3.2 第二道防线:在nginx层面直接拦截
与其指望对方自觉,不如直接在入口处拒绝。我在nginx里加了一段判断逻辑,根据UA关键字直接返回403。
map $http_user_agent $is_ai_bot { default 0; ~*GPTBot 1; ~*ClaudeBot 1; ~*Bytespider 1; ~*PerplexityBot 1; ~*CCBot 1; ~*Amazonbot 1; ~*imagesift 1; ~*dataforseo 1; } server { # ...其他配置... if ($is_ai_bot) { return 403; } }这段配置上线后,大约四成的AI bot流量在入口处被直接挡掉。注意我把if写法放在了server块里,放到location块里容易和rewrite规则产生冲突,这是nginx一个老坑了。
不过UA拦截对伪装bot无效。它们用的是浏览器UA,完全绕过这个规则。于是我又加了IP层面的封禁。
3.3 动态封禁:把行为异常IP段拉进黑名单
我写了一个小脚本,每10分钟跑一次,做的事情很简单:分析最近10分钟的nginx日志,找出请求数超过阈值且HTML请求占比超过80%的IP,然后把这些IP段追加到nginx的deny配置里。
#!/bin/bash # 简易bot封禁脚本,每10分钟运行一次 ACCESS_LOG="/var/log/nginx/access.log" BLOCKLIST="/etc/nginx/bot-ip.conf" tail -n 5000 "$ACCESS_LOG" \ | awk '{print $1, $7}' \ | sort \ | uniq -c \ | sort -rn \ | awk '$1 > 200 { split($2, ip, ":") print "deny " ip[1] ";" }' \ | sort -u > "$BLOCKLIST" nginx -s reload这脚本糙是糙了点,但胜在直观。它把单位时间内访问量巨大的IP直接拒之门外。不过这里有个教训:如果IP前端套了CDN,deny操作会把CDN节点的正常流量也一并误杀。我因为没注意这个,中途误伤过一段时间的真实访客。后来加了白名单机制,只对非CDN来源IP做动态封禁。
3.4 第三道防线:请求频率限制与JS挑战
纯静态页面即使被高频请求,对服务器压力也算可控。真正麻烦的是动态接口和搜索接口被爬虫调起来。我的项目页有一个数据统计接口,每次调用会查一次数据库,bots的频繁请求直接把这个接口的响应时间拉高了数倍。
给动态接口加限速是最直接的办法。我在nginx里配置了请求频率限制,按IP维度控制。
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=5r/s; server { location /api/ { limit_req zone=api_limit burst=10 nodelay; proxy_pass http://backend; } }这个配置表示每个IP每秒最多5个请求,允许突发10个请求。对正常使用来说完全够用,但能有效遏制bot的集中抓取。
JS挑战是更彻底的手段。通过CDN层开启Managed Challenge,对可疑访问先弹一个JavaScript验证,通过后才放行。真实浏览器几乎无感知,而大部分爬虫不会执行JS,会直接卡在验证环节。我用这个方案解决掉了剩余的大部分伪装bot。
3.5 四层方案的实际效果对比
| 防护层 | 拦截方式 | 流量下降比例 | 误伤风险 |
|---|---|---|---|
| robots.txt声明 | 协商式,靠对方自觉 | 约30% | 极低 |
| UA过滤 | 入口直接拒绝 | 约40% | 低,偶尔有兼容问题 |
| IP动态封禁 | 行为触发封禁 | 约50% | 中,容易误伤CDN |
| 频率限制+JS挑战 | 动态限速、计算验证 | 约70% | 低,但影响部分老旧浏览器 |
分层叠加之后,AI bot流量被挡住了大约九成。日志里还能看到它们在尝试访问主页,但大多数请求被策略拦截,已不构成实质压力。
4. 数据污染和成本问题:比流量上涨更值得重视的隐患
4.1 AI bot流量会让你的数据报表彻底失真
如果只是服务器多扛一点流量,这事还算不上多严重。真正让我头疼的是数据变得不可信了。
那段时间站点分析工具里的访客数暴涨,但如果直接拿这个数据去做内容复盘,会得出"帖子带来巨大真实流量"的错误结论。我把HN引来的真实用户流量单独拆分出来后,发现真实用户规模大约是原始统计数字的三分之一。这个偏差足以让任何基于数据的决策跑偏。
更要命的是那些伪装的bot也在消耗数据分析。它们不会触发页面事件,但会拉高PV数、拉低停留时长、拉高跳出率。久而久之,你对自己忠实读者规模、内容受欢迎程度的判断都会被污染。
4.2 成本账单:带宽费虽然不多,但积少成多
个人站点的图片、PDF、文档这些静态资源,每被完整抓取一次,就会产生真金白银的带宽成本。我用的服务器带宽包月有流量限额,那天晚上异常流量直接让我当月的流量配额用掉了七成。
我特意算了一下:那些bot对我主页和几个核心HTML页面的抓取总量并不算特别夸张,但如果它们把每篇文章附带的图片也全部拉一遍,成本就会快速上升。幸运的是大部分爬虫对图片兴趣有限,主要抓HTML。但确实有那类"整站镜像"型爬虫,会毫不留情地把所有资源都下载走。遇到那种爬虫,一个月流量配额可能在几个小时内清零。所以我的建议是,高流量时段提前在CDN控制台设置临时缓存规则,并要求静态资源走CDN回源,避免bot回源源站。
4.3 内容被拿去训练模型:一个绕不开的策略思考
流量问题解决到一半,我更纠结的问题浮出水面:这些内容真的愿意被别人抓走去当语料吗?
我项目里的不少技术文档是我花时间整理的,有的还包含独家的调试思路和踩坑记录。它们被AI模型收录后,相当于成了公开训练语料。这个问题没有标准答案,有人在网上公开内容就是希望被引用被传播,有人则介意自己的原创内容成为别人产品的一部分。
我的处理方式分了两层。公开的技术分享文章,完全不介意被收录,本来写出来就是给大家看的。但涉及内部细节的内容,比如特定项目的部署方案、带敏感信息的代码片段,我已经做了登录可见处理。这样即使爬虫来了,也只能看到公开展示的部分,进不了受限区。这也算是在开放和保守之间给自己留了一个缓冲地带。
4.4 认清一个现实:没有任何措施能100%挡住所有bot
我花了将近一周的时间调整各种策略,到最后发现一个无奈的事实:永远会有新的爬虫程序绕过你的防线。它们更新UA、模拟浏览器行为、使用分布式IP池,技术上防不胜防。
但这不意味着前面的工作白做了。真实目的明确:
- 过滤掉那些"讲规矩"的爬虫,让UA过滤承担最大的拦截量
- 用行为识别抓出伪装bot,压缩它们的攻击窗口
- 用限速策略压制单个IP的请求频率,防止单点瞬间打垮服务
- 让站点的真实统计回归可信区间
这套组合下来,你的站点对一般规模的AI bot流量就能做到基本免疫。真正的价值不是把bot清零,而是让服务器负载回到你可以掌控的状态。
5. 现在我会怎么做:一张长效运维清单
经历了这场流量风波之后,我再也没有裸奔过。现在我的习惯是每次对外发布内容之前,先检查一遍防护配置,并且给自己定了一份简单的清单。
第一,确认robots.txt是否覆盖最新出现的AI爬虫关键字。爬虫的种类更新很快,每季度我会重新搜一遍有哪些新出现的知名bot,把它们加进deny名单。
第二,nginx的UA拦截规则要持续更新。因为新bot出现速度很快,我把UA关键字列表单独放了一个配置文件,方便随时新增,不用动主配置。
第三,频率限制规则要覆盖所有动态接口,而不只是首页。爬虫最难驯服的地方在于它会反复访问同一个动态接口,只要把接口的限速做好,服务器压力就能被兜住。
第四,定期看日志里的UA分布和IP分布。我每个月固定抽一小时,把上个月的访问日志拉出来看看,重点对比AI bot请求量占比有没有上升。如果占比超过了五成,就说明防护策略需要重新调整了。
第五,建立正确的流量预期。HN或其他社区带来的真实流量是脉冲式的,来了很快会回落。如果一段时间内流量持续稳定却整齐划一,先别高兴太早,很可能拜访你的不是人,而是机器。
第六,在站点分析工具里把已知bot的流量单独过滤掉。现在我的面板默认只看真实用户解释的数据,bot流量只作为后台参考,避免它干扰内容趋势判断。
最后说说我的体会。一开始看到流量暴增,我内心是有点惊喜的,一度觉得是不是真的火了。冷静下来看到统计里那93%的跳出率和接近零的页面停留时间,才意识到来的都是机器人。处理完这波bot流量后,我反而对"流量"这件事有了更清醒的认识:当一个页面被公开到某个高流量入口后,服务器账单和日志里呈现的数据,很可能只代表了机器人的热情,而不是读者的热情。真正的指标,永远是那些愿意在你页面停留超过10秒、愿意点击链接继续翻下一页的真实访客。