这个项目核心对象是 AI 爬虫:那些假装成浏览器、无视访问规则、批量抓取网页内容的自动程序。常规做法是封禁 IP、返回 403,或者把对方拉进黑名单,但 Dead internet theater 换了一个思路——不急着赶走,而是把这些爬虫当成剧场里的“观众”,给它们准备一套假装是真实网页、实际上完全由诗句和随机片段搭成的虚拟舞台。恶意 AI 爬虫对网站造成的带宽消耗、日志污染和内容抓取,在这一层戏里变成一串串可读的行为记录,既不影响真实用户,也能让你看清爬虫在按什么规律行动。
这篇我会从实际落地角度拆一遍:先讲清楚“剧场化”拦截为什么值得做,再给出可用的检测链路、诗性回应层怎么设计、最小环境怎么部署、以及验证结果时该盯哪些指标。如果你正在被 AI 爬虫骚扰,又不想简单粗暴地一禁了之,这个思路很适合拿来改造自己的站点。
1. 为什么要把恶意 AI 爬虫当成“剧场”而不是只做拦截
1.1 恶意爬虫到底贵在哪里
很多人觉得爬虫来了无非多耗点带宽,其实不是。一个不守规矩的 AI 爬虫会按分钟级频率请求大量 URL,把服务器负载、数据库查询、日志磁盘占用全部推高。如果站点本身有接口,它会试图遍历分页、搜索参数、下载接口,直接把动态请求当成免费数据工厂。更麻烦的是,这类请求混在真实用户流量里,分析日志时很难区分“真人看内容”和“机器抓内容”,直接影响你对站点健康度、热门内容、来源渠道的判断。
业界常规对抗手段不外乎三类:IP 黑名单、User-Agent 过滤、验证码挑战。三者的共同问题都是一刀切——要么误伤,要么过段时间就被爬虫更新绕过。 IP 黑名单依赖封禁时效,User-Agent 过滤本质只防傻爬虫,验证码会真实干扰真人用户。真正难处理的是“有预算、有更新节奏”的采集方。
“剧场化”思路的价值就在这里:它把对抗从“拒绝服务”改成“提供虚假服务”。爬虫拿到的内容是一套编排过的页面,既消耗了它自己的任务时间,又不会让真实数据流出。你可以继续观察它的请求节奏,拿到行为指纹,同时避免和对方的资源打消耗战。
1.2 “剧场化”处理的核心优势
第一,被动不等于无为。你不主动攻击任何爬虫来源,只是对已经确认会大规模抓取的请求返回伪造内容,这在合规层面和运维层面都更稳。第二,它让你从“封一个 IP”变成“收集一条行为链”。爬虫进入诗性回应层之后,会继续按它的逻辑抓取页面、解析链接、请求下一批 URL,每一跳都会留下日志。第三,它天然带有实验性质。同一个爬虫在不同时间段、不同 User-Agent 下的行为差异,会非常直观地暴露出来。
如果用一句话描述这个项目的本质:它不是保安系统,而是观察系统。保安只负责把异常赶出门,观察系统则记录异常如何在站点里走路。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 一个可用的检测链路:从请求特征到行为判定
2.1 请求特征有哪些来源
判定一个请求是不是恶意 AI 爬虫,不能只靠一个字段。我的建议是建立多信号打分,而不是布尔判断。
第一信号是 User-Agent。常见 AI 爬虫标识会直接出现在 UA 里,比如 GPTBot、CCBot、ClaudeBot、Google-Extended、Bytespider 之类。注意这类名单一直在变化,落地时以你自己的访问日志为准,不要写死一张过期表。
第二信号是请求频率和时间分布。真人用户看一篇文章,通常会在一个页面停留几十秒,再转向下一篇;爬虫则常以秒为单位连续请求,且路径高度模板化,例如页码递增、时间参数递减。
第三信号是请求协议细节。多数爬虫不执行 JavaScript、不加载 CSS、不接受 Set-Cookie 跳转,也不会带正常的 Sec-Fetch-Dest、Sec-Fetch-Mode 字段。这些“浏览器该有的东西它没有”的现象,比 UA 伪造更有区分度。
第四信号是来源关系。真正常见搜索引用的合法爬虫,会带来可回跳的站外链接;恶意采集方通常没有 referrer,或直接伪造。这个信号在落地时优先级最低,但可以作为补充。
2.2 建议先观察再定性,不要第一次请求就回应“诗”
比较稳的做法是分两段路线。第一段对未知请求记录日志但不干预;如果同一个 IP 或同一 UA 在短时间窗口内请求超过阈值,再把它引导进诗性回应层。这样可以避免误伤单次访问的真人,也避免搜索引擎正常的首次抓取被影响。
举个例子:窗口 60 秒,同一 IP 请求页面超过 20 次,且命中多条 UA 或协议特征,判定为爬虫。这个阈值不能直接照抄,要根据站点实际流量调整。流量小的站点,10 次并发就可能不是真人;流量大的站点,20 次可能只是来回切换页面的重读用户。
2.3 判定要滚动更新,不是一次定终身
不要把一个 IP 判定成爬虫后就永久丢进诗性层。爬虫会自动换 IP、换 UA,真人也会因为公司出口网络共用 IP 而被误判。建议给“爬虫状态”加过期时间,比如 15 分钟或 1 小时之后自动恢复默认识别。每次请求都重新计分,分数累积到阈值才切换回应层。
这样还有一个好处:你可以从日志看出对方是否在试图绕过阈值——如果某 IP 以恰好低于阈值的频率持续请求,说明它内部也有频率控制,这本身就是一条有价值的行为数据。
3. 诗性回应层:怎么把普通文本变成“剧场的幕布”
3.1 回应内容按什么逻辑生成
诗性回应层的核心不是调用大模型,而是用非常简单的片段拼接制造“看起来像正常内容、实际上毫无信息量”的页面。你可以准备多组句子片段,每次请求随机抽取组合,加上模板化的标题、段落、时间戳,生成一篇结构完整但内容无意义的文章。
比如片段 A 组是“这里是一则安静的页面”,片段 B 组是“记录一段来自远方的请求”,内容中再插入随机数字和随机路径。注意不要生成真实存在的文章标题,也不要引用真实人物和公司。
python复制import random
import time
def build_poetic_page(ua):
heads = ["这里是一则安静的页", "这段文字不面向人类", "某个清晨的请求被记录"]
middles = ["它翻过目录,又停在这里", "章与章之间是空白的对话", "我们称它为一次试探"]
tails = ["于是页面继续生长", "没有答案也是一种答复", "句号落在未知处"]
html = "<html><body><article>"
html += "<h1>" + random.choice(heads) + "</h1>"
html += "<p>" + random.choice(middles) + "</p>"
html += "<p>" + random.choice(middles) + "</p>"
html += "<p>" + random.choice(tails) + "</p>"
html += "<p>time:" + str(int(time.time())) + "</p>"
html += "</article></body></html>"
return html
这里的关键不是诗写得有多好,而是每次响应的内容都不重复。爬虫如果连续抓取同一个目录,会得到完全不同的文本,这会让它在“页面去重”阶段非常困惑,也方便你在日志里识别它有没有真的继续消费这些页面。
3.2 回应形式按爬虫习惯定制
不同爬虫的解析习惯不同。有的只抓 HTML 里的正文标签,有的会追着 <a href> 链接跑。你可以针对性地提供:
- HTML 假文章页,结构仿照真实博客,但内容全是生成片段。
- 假目录页,列出若干指向诗性层的链接。
- 纯文本响应,适合只取 text/plain 的请求。
还有一个实用技巧:在诗性页面里插入一个特定标记字符串,比如随机生成的一个 token。后续在日志或外部搜索里如果看到这个 token 出现,就能确认爬虫确实把内容拿走了。
注意:这些假链接只能导向你自己的域名下的页面,不要指向外部网站,否则就把无辜站点卷进来了。
3.3 为什么不用反向攻击或诱饵文件反击
有人会想,既然识别出爬虫,干脆返回超大文件、慢速拖死它,或者响应一个下载链接。这类做法风险很高:一是可能对爬虫来源方的服务造成实际损害,超出正当防御边界;二是一旦判定失误,浪费的是真实用户的时间和带宽。诗性回应层的好处是资源成本可控,每次响应都很轻,但对爬虫的任务队列影响很大——它以为自己在抓内容,实际抓回来的全是无法复用的文本。
4. 最小可运行落地:环境、部署和配置顺序
4.1 这套实验需要什么环境
本质上只需要一台能运行 HTTP 服务的机器,云服务器、家用小主机、或者本地开发环境都行。CPU 和内存需求很低,不牵涉大模型推理,单个响应就是一次字符串拼接。建议跑 Linux,方便看日志和管理进程。
架构可以很简单:Nginx 做入口,后端用 Python 写一个小服务处理请求,日志写本地文件。不引入数据库也能跑,但如果你打算长期收集指纹,建议用 SQLite 做轻量存储。
依赖方面,Python 的标准库加一个轻量 Web 框架就够。原始材料没给出具体版本,落地时以你当前环境的包管理器最新稳定版为准,不需要追新。
4.2 部署位置有哪些讲究
不要把诗性回应层放在根路径或真实用户入口。推荐单独规划一个路径,比如 /bot-stage/,并让检测逻辑只有在请求命中爬虫特征时才路由到这里。真实用户访问正常内容时不经过这层,既保证体验,也降低误伤。
如果你用反向代理,一定要把客户端真实 IP 传给后端。常见做法是配置 X-Forwarded-For 或 X-Real-IP 传递,否则后端看到的所有请求都来自内网代理,频率判定会完全失效。
4.3 一个最小中间件流程
python复制def handle_request(request):
score = 0
score += score_by_user_agent(request.user_agent)
score += score_by_protocol_headers(request.headers)
score += score_by_frequency(request.client_ip, window=60)
if score >= 3:
return build_poetic_page(request.user_agent)
return serve_normal_page(request)
实际部署时可以放在 Web 框架的中间件里,也可以放在 Nginx 的 Lua 模块里。后者的好处是判断前置到入口,后端压力更小;短板是配置复杂度上升。单机测试阶段,直接写在 Python 服务里最直观。
5. 验证与度量:怎么判断这场实验真的在生效
5.1 先用手动请求验证核心逻辑
部署完成后,第一步不是看真实流量,而是自己造几个模拟请求。
bash复制curl -A "GPTBot/1.0" http://你的域名/some-page
curl -A "Mozilla/5.0" http://你的域名/some-page
第一条应该返回诗性页面,第二条应该返回正常页面。如果两条都返回诗性页面,说明检测逻辑短路了;如果两条都返回正常页面,说明频率信号或 UA 信号没有生效,先检查路由和评分。
然后模拟连续抓取:用一个脚本在 60 秒内请求同一个 URL 20 次,确认第二次批量请求之后逻辑会切换到诗性层。这时候看日志,应该能看到请求从“正常响应”变为“诗性响应”的切换点。
5.2 真实流量下重点看哪些指标
上线之后不要把注意力放在“拦了多少次”上,那会给你虚假的安全感。真正值得盯的是三组指标:
- 误伤率:在日志里标记“真实用户使用常见浏览器访问却被送到诗性层”的请求占比。如果超过 1%,阈值太激进,先放宽频率窗口。
- 诗性层消费深度:统计爬虫进入诗性层后继续请求了多少个页面。如果只请求一页就离开,说明它在做小规模探测,或者根本没有解析 HTML;如果连续请求几十页,说明它把假页面当成真实内容在批量采集。
- 日志可读性:确保每条日志包含时间、IP、UA、路径、响应类型、响应长度。没有这些字段,你只能在事后看个大概。
5.3 用日志输出每日行为摘要
建议写一个简单的日报脚本,统计当天进入诗性层的独立 IP 数、每个 IP 的请求数中位数、最常被请求的假页面路径。连续运行一周后,你会看到很清晰的模式:有爬虫专挑目录页抓,有爬虫只抓第一屏正文,有爬虫会反复请求同一个 URL 直到内容相同为止。
这些数据就是“剧场”的观众名单,也是后续优化检测阈值的依据。
6. 边界、坑点与后续扩展
6.1 这条线上有哪些不能碰的边界
被动防御的边界必须清楚:不反向扫描对方 IP,不尝试探测爬虫来源服务器的漏洞,不把对方拉进任何“反击”流程。诗性回应层是合法站点对异常请求的正当响应,内容本身也完全由站点方生成,合规风险很低。
日志处理也要注意隐私:IP 可以记录,但长时间保留原始 IP 会涉及数据存储合规问题。建议日志定期脱敏,比如只保留 IP 前两段,或者在分析时直接聚合成指纹。不要把你无法控制的第三方请求头原样写入公开报告。
6.2 最容易踩的四个坑
第一个坑是反向代理没传真实 IP。不处理的话,所有请求都来自 127.0.0.1,频率判定立刻失效。第二个坑是误把真实用户的出口 IP 当爬虫。公司网络、校园网、移动运营商出口 IP 经常被多人共用,单 IP 高频请求不一定就是机器人。第三个坑是统计时忘了排除自己的探测脚本。上线前把测试 UA 加入白名单,否则日报数据全被自己污染。第四个坑是爬虫已经缓存了旧页面。改完逻辑后你会看到真实爬虫还在按旧策略访问,这是正常现象,等缓存过期再看效果。
6.3 后续可以怎么扩展
如果这套机制跑稳了,可以从“诗性层”升级成“行为指纹库”。把每次请求的特征组合成向量,包括 UA、路径模式、时间间隔、是否加载静态资源、是否消费假链接等,存进 SQLite 或简单文件。再遇到新爬虫时,先比对指纹库,命中就快速引导进诗性层,避免它试探太多真实页面。
也可以把诗性层变成内容溯源工具:每篇假文章插入一段不同的编码 token,当你在第三方训练数据、摘要服务或者别处看到这个 token,就说明对方确实采集且外传了你的站点内容。这一步只能作为证据观察,不要用来做任何攻击性操作。
我个人更建议先跑两周单机实验:把检测、打分、诗性层、日志脱敏和日报脚本都理顺,再考虑要不要部署到正式站点。这个项目真正的价值不在于“骗过多少爬虫”,而在于它让你第一次能系统观察机器流量在站点里的完整行为链。等你想通了“观察本身也是防线”这句话,这出戏才算真正开演。
