网站数据被爬虫批量抓走?防采集实战方案

网站流量不大、服务器也不卡,但内容总被同行抢先发、报价表被人整站搬走——这往往是恶意爬虫在批量采集数据。采集类爬虫的目标不是打垮网站,而是悄无声息地把数据搬空,所以防护重点不是”封 IP 止血”,而是提高采集成本。本文用 8 个问答讲清网站数据防采集的完整实战方案。

Q:怎么判断网站数据正在被批量采集?

看访问日志的三个特征。第一,单个 IP 的请求量远超正常用户,正常访客一天几百次顶天,采集器一天几万次很常见:

awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -20

第二,URL 呈规律遍历,比如按 ID 顺序递增、翻页参数连续,把某个可疑 IP 的 URL 单独拉出来看:

grep "1.2.3.4" access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -30

第三,User-Agent 高度统一,几万个请求全是同一个 UA,且不加载 CSS/JS/图片,只抓 HTML 和接口。三个特征中命中两个,基本可以确认是采集爬虫。

Q:为什么只封 IP 挡不住采集?

因为专业采集早就用上了代理 IP 池。封掉一个 IP,对方立刻换下一个,手动封 IP 完全跟不上节奏。所以封禁只能作为应急止血,不能作为方案。应急时两层一起上,系统层比应用层更省资源:

# 系统层封 IP(最省资源)
iptables -I INPUT -s 1.2.3.4 -j DROP
iptables -I INPUT -s 5.6.7.0/24 -j DROP

# Nginx 层封 IP(改完 nginx -s reload)
deny 1.2.3.4;
deny 5.6.7.0/24;

真正的解法是让”换 IP 也没用”,下面几条就是干这个的。

Q:蜜罐陷阱链接怎么部署?能不能识别伪装成正常用户的爬虫?

可以,这是性价比最高的手段之一。原理很简单:在页面里放一个隐藏链接——正常用户看不到、也不会点,但爬虫解析 HTML 时会顺着链接抓取。谁访问了这个链接,谁就是爬虫:

<!-- 正常用户不可见,爬虫会跟着抓 -->
<a href="/trap-honeypot-8f3a/" style="display:none" rel="nofollow">更多内容</a>

对应的 Nginx 规则:访问到陷阱路径直接拒绝,同时记录 IP 供后续批量封禁:

location = /trap-honeypot-8f3a/ {
    access_log /var/log/nginx/honeypot.log;
    return 403;
}
location ~* ^/trap-honeypot- {
    return 403;
}

进阶玩法是多级陷阱:第一级用隐藏链接,第二级再放一个需要 JS 才能生成的链接(正常浏览器点得到、单纯 HTTP 请求拼不出来),两级串联后能筛掉绝大多数中级采集器。

Q:接口数据被抓,动态 Token 和签名怎么防?

接口是最容易被批量刷的地方,因为它没有页面渲染的门槛。核心思路是让请求带上有时效性的签名,让采集方无法直接照着 URL 复刷:

# 签名示例(服务端校验)
sign = md5(uid + timestamp + nonce + secret)
# 服务端要求:
# 1. timestamp 与服务器时间相差不超过 300 秒
# 2. nonce 5 分钟内不可重复(Redis SETNX 记录)
# 3. sign 必须匹配,否则 403

再配合 Redis 计数限频,按用户或按 IP 限制接口调用频率:

# 单个 uid 每分钟最多 60 次调用
INCR api:rate:{uid}:{minute}
EXPIRE api:rate:{uid}:{minute} 60
# 超过 60 直接返回 429

签名解决”别人能不能照着复刷”,限频解决”刷得有多快”,两者必须同时上。只做签名不做限频,对方拿到合法签名后照样能高频刷。

Q:内容脱敏和数据水印有什么用?

这是”事后追溯”和”降低价值”的手段。前者让你被抄了之后能证明是对方抓的,后者让抓到的数据不值钱:

  • 数据水印:给每个用户可见的内容打上微小差异(如隐形字符、顺序微调、金额小数位差异),对方整站搬运后,你能从差异定位到泄露源;
  • 内容脱敏:页面上只展示必要字段,完整数据(如完整手机号、完整报价细项)必须登录或申请后才能查看,把”抓取即拿全”变成”抓取只拿到残缺数据”;
  • 图片加水印:图片被搬走时带水印,降低对方直接复用的意愿。

水印的价值在于威慑和取证,它不是拦截手段,但配合前面的蜜罐能形成完整证据链。

Q:Nginx 层面的限速和连接数限制怎么配?

这是全站兜底配置,任何采集都要按 IP 或按连接受限制。放在 http 块定义区域,在 server/location 应用:

http {
    # 每 IP 请求频率限制
    limit_req_zone $binary_remote_addr zone=anti_crawl:10m rate=20r/m;
    # 每 IP 并发连接限制
    limit_conn_zone $binary_remote_addr zone=perconn:10m;
    limit_req_status 429;
    limit_conn_status 429;

    server {
        location / {
            limit_req zone=anti_crawl burst=30 nodelay;
            limit_conn perconn 15;
        }
        # 接口路径限得更死
        location /api/ {
            limit_req zone=anti_crawl burst=10 nodelay;
        }
    }
}

参数含义:rate=20r/m 是每 IP 每分钟 20 个请求,正常浏览足够,采集会被大量限流;burst=30 允许突发排队;nodelay 让突发请求立刻处理,避免正常用户被拖慢;limit_conn 15 限制单 IP 同时 15 个连接,专门治多线程采集。注意接口路径要比页面限得更严,因为页面用户会慢慢看,接口采集是毫秒级连刷的。

Q:遇到会换 IP、会伪造 UA 的”对抗型”爬虫怎么办?

到这一步说明对方是定向采集,单点手段都不够了,要升级成分级验证

  • 一级:JS 质询——正常浏览器能执行 JS 拿到通行 Cookie,纯 HTTP 采集脚本不行,开启后能筛掉大部分初级采集;
  • 二级:行为特征——同一会话内访问路径是否符合人类规律、鼠标/滚动事件是否存在、有无 CSS/JS 加载,异常的直接打回;
  • 三级:验证码加严——对高频访问的来源弹验证码,正常用户偶尔一次可接受,采集方成本陡增;
  • 四级:边缘拦截——上 CDN/WAF,把频率异常、指纹可疑的请求在边缘节点就拦掉,源站只收干净流量,同时把高频页面做成缓存,采集只消耗缓存不碰数据库。

这四级不是逐级替换,而是叠加使用:前三级解决”能不能识别”,第四级解决”识别后由谁承担开销”。

Q:还有什么补充建议?

记住一句话:防采集的目标不是封干净,而是让采集成本高于收益。按成本从低到高排一套落地顺序:先查日志确认采集特征(零成本);加蜜罐陷阱和 Nginx 全局限速(低成本、见效快);再上接口签名 + Redis 限频(改造量中等);最后按数据价值决定是否上 CDN/WAF 和分级验证。另外建议做两件长期功课:一是每天跑一次 Top IP 统计脚本,请求量突然翻十倍就告警;二是把核心数据做权限分级,能在页面上少露的就少露,数据本身不暴露,就没有被采集的价值。