给一个日更50篇文章的站做了一套防采集方案,从Nginx到JS前端总共上了5层拦截,蜘蛛没误伤采集量降了80%
一个做内容站的同行上个月发现,自己花了三个月写的300多篇原创文章,被人用爬虫整站搬走了。对方只改了标题和开头几句,剩下全文照搬,百度还把那篇文章排到了他前面。他来找我的时候问的第一句话是:"有没有什么办法让对方采不到我的内容?"
办法是有的,但防采集这件事有个最大的坑:你防采集的时候把百度蜘蛛也拦了。对方没采到你的内容,你的收录也掉了——这比被采集还惨。所以防采集方案的核心不是"防得越严越好",而是在正常用户和搜索引擎蜘蛛不受影响的前提下,最大化采集者的成本。
防采集五层拦截架构一览
| 1 | Nginx频率限制 | 同一IP超过正常浏览频率直接拒绝,蜘蛛白名单放行 |
| 2 | 请求头校验 | 检查UA、Referer、Accept-Language等请求头是否完整 |
| 3 | 行为分析 | 访问间隔、页面停留时间、鼠标轨迹等行为特征识别 |
| 4 | 前端混淆 | HTML结构动态变化、随机class名、干扰句植入 |
| 5 | 内容水印+监控 | 零宽字符水印、特征句监控、采集溯源取证 |
一、第一层:Nginx频率限制,拦住99%的初级爬虫

大部分内容采集脚本有一个共同特征:请求速度远快于正常人类浏览。一个人看一篇文章至少30秒到几分钟,爬虫可能一秒发几十个请求。Nginx的limit_req模块就是干这个的——检测到某个IP在短时间内发出过多请求,直接返回503或429。
核心配置就这几行:
# 在http块中定义限流区域limit_req_zone $binary_remote_addr zone=article_limit:10m rate=5r/s;# 在server或location块中应用location / {limit_req zone=article_limit burst=10 nodelay;# 关键:放行搜索引擎蜘蛛if ($http_user_agent ~* "Baiduspider|Googlebot|bingbot|Sogou|360Spider|YisouSpider") {set $limit_rate 0;}}rate=5r/s意味着每秒最多5个请求,burst=10允许瞬间突发10个。正常用户翻网页一秒最多点一两次,5r/s已经很宽松了,但对爬虫来说就是一道硬墙。
最容易踩的坑:CDN回源IP问题
limit_req用$binary_remote_addr做key,按客户端IP限速。如果网站前面套了CDN(Cloudflare、阿里云CDN等),Nginx拿到的IP是CDN的回源IP,不是真实用户IP。所有用户共享一个限速额度,正常用户也会被误伤。解决方案是配置real_ip模块,从X-Forwarded-For或CF-Connecting-IP头里取真实IP。
还有limit_conn模块,限制同一个IP的并发连接数。正常浏览器同时开6-8个连接下载资源是合理的,爬虫可能开到几十个。设置limit_conn per_ip 10,超过的直接拒绝。
蜘蛛白名单:UA判断够用吗?
只靠UA判断蜘蛛是不够的,UA可以伪造。更严谨的做法是用搜索引擎官方公布的真实IP段做反向DNS验证。但在中小站点上,UA白名单+频率宽松策略已经够用,过度追求验证精度反而会增加维护成本。如果你用的是Cloudflare或阿里云WAF,它们内置的合法爬虫识别功能比手工配置准得多。
二、第二层:请求头校验,过滤掉"裸奔"的采集脚本
很多初级爬虫只发了GET请求,连浏览器最基本的请求头都不带。正常的浏览器请求会携带Referer、Accept-Language、Accept-Encoding等头部信息。
| 检测项 | 正常浏览器 | 低端爬虫 | 处理策略 |
|---|---|---|---|
| User-Agent | 完整的浏览器UA字符串 | 空的、Python-urllib、curl等 | 拒绝空UA和非浏览器UA |
| Referer | 站内页面或搜索引擎 | 空或固定值 | 直接访问放行但频率异常时拦截 |
| Accept-Language | zh-CN,zh;q=0.9等 | 空 | 缺少该头打上低信任标记 |
| Cookie | 携带会话Cookie | 不携带 | 无法通过JS挑战 |
在Nginx中做请求头校验:
# 拒绝明显的爬虫UAif ($http_user_agent ~* "python|curl|wget|scrapy|httpclient|okhttp|Java") {return 403;}# 检查Accept-Language是否存在location /article/ {if ($http_accept_language = "") {return 444; # 直接关闭连接}}但要注意:有些合法工具也会用curl或Python请求,比如你自己的监控脚本。需要给内网IP或特定UA加白名单。另外搜索引擎蜘蛛访问文章页时也可能没有Accept-Language头,蜘蛛的UA必须在这个规则之前被放行。
三、第三层:行为分析,区分人和机器最关键的一步
频率限制和请求头校验都是"规则层"的防御,写爬虫的人花十分钟就能绕过——换个正常UA、加个Referer、降低请求频率到每秒一两次。这时候需要上行为分析了。
人和机器在访问行为上有几个本质差异:
页面停留时间
人看完一篇2000字文章至少30-60秒,爬虫抓完HTML只要0.1秒。如果在文章页上停留时间小于3秒就跳到下一篇,大概率是机器。

页面访问顺序
人浏览网站有逻辑路径:首页→列表页→文章页→相关文章。爬虫通常按ID顺序或sitemap直接访问文章页,不会请求CSS/JS/图片。
鼠标轨迹和滚动
人阅读时会滚动、会有鼠标移动。爬虫没有这些行为。前端可以埋一段JS,检测页面是否有滚动事件和鼠标移动事件。
资源加载完整性
浏览器会加载HTML中引用的CSS、JS、图片等。爬虫通常只请求HTML本身。一个IP连续访问多篇文章但从不请求静态资源,基本可以判定为爬虫。
实现方式上,中小站不需要自己开发行为分析系统。Cloudflare的Bot Management、阿里云WAF的Bot管理都内置了行为分析能力,开箱即用。免费方案可以在前端埋一个简单的JS探针:
// 前端探针:检测真实用户行为(function() {var interacted = false, startTime = Date.now();['scroll','click','mousemove','keydown'].forEach(function(e) {window.addEventListener(e, function(){interacted=true;},{once:true});});setTimeout(function(){if(!interacted && (Date.now()-startTime)<3000){fetch('/api/flag_suspicious',{method:'POST'});}},5000);})();行为分析的副作用
行为分析依赖JavaScript执行。如果用户浏览器禁用了JS,或者使用了阅读模式、文本浏览器,就会被误判为爬虫。这也是为什么行为分析适合做"辅助判定"而不是"硬拦截"——对行为异常的请求可以先返回验证码或降低响应优先级,而不是直接403。
四、第四层:前端混淆,让采到的内容没法直接用
前面三层都是"不让对方拿到HTML",但如果对方用headless浏览器(Puppeteer、Playwright)来采集,前面三层基本全废——它有正常的UA、正常的请求头、甚至有真实的渲染行为。这时候需要前端混淆,核心思路是:内容可以让你拿到,但拿到的是乱序的、缺少关键信息的、掺杂了干扰内容的。
| 混淆方式 | 原理 | 效果 | 对SEO影响 |
|---|---|---|---|
| CSS反爬文字 | 关键文字用CSS伪元素content渲染,HTML中不直接出现 | 爬虫抓到的HTML缺关键词 | 高风险 |
| 动态HTML结构 | 每次请求,div顺序、class名随机变化 | 采集方无法用固定CSS选择器提取 | 低风险 |
| 蜜罐文字/链接 | HTML中插入仅爬虫可见的隐藏文字 | 采集内容混入垃圾信息 | 高风险 |
| 内容JS动态加载 | 正文通过AJAX异步加载,HTML中只有占位div | 不执行JS的爬虫只能拿到空壳 | 高风险 |
| 文字转图片/SVG | 关键段落渲染为图片,不是文本 | 爬虫拿不到文字内容 | 极高风险 |
这个表格里最值得关注的是"对SEO影响"这一列。很多前端混淆方案看起来很厉害,但搜索引擎也看不到了。做SEO的站,CSS反爬文字、蜜罐文字、JS动态加载这三类都要慎用,甚至不用。
相对安全的前端混淆方式有两个:一是动态HTML结构——服务器端每次输出文章时随机变换div包裹层级和class名,不影响正文内容;二是内容中加随机干扰句——每篇文章自动插入1-2句与主题无关但语法正确的句子,采集方不仔细读发现不了,读者扫过去也不违和。
一个实用的轻量混淆方案
服务端在渲染文章模板时,随机选择3-5种不同的HTML结构模板(区别在于div嵌套层数、section标签使用与否、段落间是否插入空div等),每次请求随机选一种。再加上随机class名(用hash生成,如class="p_a3f2b1"),对方即使写好了CSS选择器,换个页面就失效了。这种方案不改变正文内容,对SEO零影响。
五、第五层:内容水印和采集监控,发现被采了能追责
不管前面四层做得多好,总有人能找到办法绕过。比如对方用住宅代理IP池、每个IP只访问几篇文章、headless浏览器完整渲染页面——这种情况下的采集几乎和真实用户没区别。这时候就需要"后手"了:让对方采到的内容带着你的标记,同时你有手段发现谁采了你的内容。
零宽字符水印
在文章特定位置插入Unicode零宽字符(U+200B、U+200C、U+200D等),肉眼完全不可见,复制粘贴后会保留。用不同零宽字符组合编码访问时间和IP,对方发布后能证明来源。
特征句+搜索监控
每篇文章随机插入一句独一无二的"特征句",定期在百度里搜索这句原文。出现在其他网站就是被采了。

图片暗水印
配图嵌入不可见的数字水印(频域水印),即使对方截图或轻微压缩也能提取,被采集后可作为版权证据。
访问日志分析
定期分析Nginx访问日志,找出"短时间内按ID顺序访问大量文章页"的IP段。跨IP的访问模式也能暴露采集行为。
零宽字符水印的实现非常简单,服务端渲染时嵌入:
# Python:生成零宽字符水印def encode_watermark(ip, timestamp):data = f"{ip}|{timestamp}"binary = ''.join(format(ord(c),'08b') for c in data)chars = {'0':'\u200b','1':'\u200c','_':'\u200d'}return ''.join(chars.get(b,'\u200d') for b in binary.replace(' ','_'))# 在文章第一段第三个字后插入article_html = article_html[:pos] + encode_watermark(ip, time.time()) + article_html[pos:]六、不同规模网站的防采集方案怎么选
五层方案不是每层都要上。一个日访问量几百的博客,和日更几百篇文章的站群,投入产出比完全不同。
| 网站规模 | 推荐方案 | 月成本 | 效果预期 |
|---|---|---|---|
| 个人博客/小站 日更1-3篇 | 第一层(Nginx限速)+ 第五层(水印监控) | 0-50元/月 | 拦住脚本类爬虫,被采后能溯源 |
| 中型内容站 日更10-30篇 | 第一层+第二层+第三层(CDN WAF) | 200-500元/月 | 拦住80%以上采集尝试 |
| 大型站群 日更50+篇 | 五层全上+独立WAF+专业Bot管理 | 500-2000元/月 | 接近企业级防护,大幅提高采集成本 |
七、容易被忽略的"采集预防"——从根源减少被采集的动机
技术手段能挡住"能不能采",但不能解决"为什么采你"。如果你的网站SEO做得好、排名靠前,即使上满五层防御,依然有人愿意花大价钱绕过。反过来,如果内容质量一般、排名一般,被采集的概率天然就低。
发布时间前置
文章写完第一时间推送到百度API和IndexNow,抢在被采集之前完成索引。搜索引擎看到你的页面更早,即使后面被采了也不会被判定为重复。
差异化内容结构
同一主题的文章,每个站用不同的角度和结构来写。比如讲"防采集",A站侧重Nginx配置、B站侧重CDN方案、C站侧重代码层面。采集方搬走A站内容,放到他站上和其他站文章风格不统一,质量大打折扣。
主动监控+快速投诉
定期在百度搜索自己文章的特征句,发现被采集后立刻在百度站长平台提交原创保护投诉。处理速度比你想象得快,尤其是百度已经在推原创保护机制。
用UC建站系统管理多站内容时,这几个"预防"动作可以做到系统化:文章发布时自动通过双通道(百度API + IndexNow)推送索引,多站内容通过中台做差异化重组确保同一主题在不同站上有不同结构和表达,配合多站看板的统一索引监控,第一时间发现哪个站的内容被搜索引擎收录后又被其他站点抢排。
八、一份可以直接用的Nginx防采集完整配置模板
把前面讲的几层方案整合成一份可以直接放到nginx.conf里用的配置。注意这份配置里蜘蛛白名单的UA列表需要根据你实际关注的搜索引擎来调整。
# ===== 防采集配置模板 =====http {# 1. 限流区域定义(10M内存约可存16万IP)limit_req_zone $binary_remote_addr zone=page_limit:10m rate=3r/s;limit_conn_zone $binary_remote_addr zone=conn_limit:10m;# 2. 蜘蛛UA白名单变量map $http_user_agent $is_spider {default 0;~*Baiduspider 1;~*Googlebot 1;~*bingbot 1;~*Sogou 1;~*360Spider 1;~*YisouSpider 1;}server {# 3. 直接拒绝已知爬虫UAif ($http_user_agent ~* "python-requests|scrapy|wget|curl|Java/1.") {return 403;}location / {# 4. 蜘蛛不限制if ($is_spider = 1) { set $limit_rate 0; }# 5. 普通用户限速:每秒3请求,突发5limit_req zone=page_limit burst=5 nodelay;# 6. 并发连接限制:每IP最多10个limit_conn conn_limit 10;}# 7. 文章页额外校验location /article/ {if ($is_spider = 1) { set $limit_rate 0; }if ($http_accept_language = "") { return 444; }limit_req zone=page_limit burst=3 nodelay;}}}上线前必须测试
配置改完不要直接reload,先用nginx -t检查语法,然后逐步放开——先把limit_req的rate调大(比如10r/s),观察几天看看有没有误伤正常用户和蜘蛛,再慢慢收紧。尤其要关注百度站长平台的抓取频次和抓取异常报告,一旦出现蜘蛛抓取失败率上升,立刻回滚。
最后说一个很多人忽视的点:防采集这件事,技术手段只是50%,另外50%在内容策略上。如果你的文章写得有特点、有辨识度、有独家数据和观点,对方采集过去也不好直接用——改头换面成本太高。反过来,如果你的文章就是标准SEO模板文(三段论+堆关键词),那采集成本几乎为零,上再多技术防护也只是延长了几秒钟的采集时间而已。
做防采集的正确心态是:让采集你的内容这件事成本高到不划算,而不是追求100%防住。因为技术上不存在绝对防住的方案——只要是人能看到的内容,机器就能拿到。你能做的是让99%的采集者放弃你,去采别人的。
