登录态爬虫不猜密码也不刷接口,它拿着一个能正常登录的账号,按正常路径把会员内容一页页搬走。本文讲清它的账号从哪来、日志里怎么认出来、账号级限流与会话绑定怎么配、封号前怎么留证据不误伤。
Q:登录态爬虫是什么?和撞库、和普通爬虫差在哪?
- 普通爬虫是匿名访问:不登录,只拉首页、列表、文章页,日志里命中的是 200 的公开页。
- 登录态爬虫先完成一次真实登录,拿到会话凭据,之后每个请求都带着它。服务端看到的是一个合法会员在做合法操作。
- 和撞库的区别很关键:撞库是猜密码,日志里是海量 401/403;登录态爬虫是登录成功后慢速搬运,几乎全是 200,报警器根本不会响。
- 和接口签名防爬也不是一回事。签名解决的是「调用方是不是我的客户端」;登录态爬虫已经拿着你的客户端和合法凭据,签名对它完全无效。它要解决的是「这个账号是不是真人在用」。
- 最典型的特征:请求全部命中登录后才可见的深页,却几乎不请求 CSS、JS、图片。真人不会这样。
Q:这些能登录的账号都是哪来的?
- 会员共享与转卖:一个号多人用,最后被转手给采集工具。这类账号往往本身就是弱口令。
- 批量注册小号:接码平台 + 虚拟号段,一次注册几百个,只为了过登录墙。
- 试用与免费额度:7 天试用是最容易被批量薅的入口,试用期刚好够把整站搬一遍。
- 被盗账号:撞库成功后留下的账号,平时不用,只在采集时启用。
- 机构与企业账号外借:一个企业账号给外包「做数据迁移」,迁移完没回收。
- 打包套餐:采集工具直接卖「带会员账号的套餐」,账号就是服务的一部分。
Q:日志里怎么才能看见「账号」这一维?
- 默认的 combined 格式里没有账号信息,所以你只能看到一堆来自同一个 IP 的请求,看不出它们是同一个账号。第一步是把账号写进访问日志。
# /etc/nginx/nginx.conf 的 http 块
log_format with_uid '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" "$http_user_agent" '
'uid=$cookie_uid sid=$cookie_phpsessid '
'rt=$request_time urt=$upstream_response_time';
access_log /var/log/nginx/access.log with_uid;
- cookie 里的 uid 可以被用户自己改,做统计够用;做限流和封禁要用后端写出来的可信值。让应用在响应头里带一个
X-User-Id,然后取$upstream_http_x_user_id,这个值客户端改不了。 - 别用 session id 当账号维度。爬虫换个会话只要重新登录一次,而账号换不了。
Q:怎么从日志里算出某个账号在爬?
- 两个数最有用:单账号每分钟请求数和翻页深度。正常会员一天几十次请求,爬虫一分钟就几百次。
# 单账号分钟级请求数 TOP 20
awk '{uid="";for(i=1;i<=NF;i++) if($i ~ /^uid=/){uid=substr($i,5)}
if(uid!="" && uid!="-") print uid, substr($4,2,17)}' \
/var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# 单账号请求的翻页参数分布(看有没有从 1 一路翻到几百页)
grep "uid=10086" /var/log/nginx/access.log \
| awk '{split($7,a,"page="); if(a[2]) print a[2]+0}' \
| sort -n | uniq -c | tail -20
- 再补三个辅助判据:静态资源占比低于 5%(真人一般 60% 以上)、同一账号 5 分钟内跨三个省(拿
$remote_addr查归属地)、24 小时不停(真人一定有睡眠段)。 - 把账号维度和 IP 维度一起看:一个 IP 上挂着几十个账号轮流请求,也是典型特征,这种要按 IP 和账号双向处理。
Q:账号级限流怎么配?
- Nginx 自带的能力就够用,关键是 key 取账号而不是 IP。
limit_req_zone只能写在 http 块,且 key 为空字符串时规则完全不生效,所以要给个兜底。
# http 块
map $upstream_http_x_user_id $limit_key {
default $upstream_http_x_user_id;
"" $binary_remote_addr; # 未登录兜底,按 IP 限
}
limit_req_zone $limit_key zone=peruser:20m rate=2r/s;
limit_conn_zone $limit_key zone=peruser_conn:10m;
server {
location / {
limit_req zone=peruser burst=30 nodelay;
limit_conn peruser_conn 5;
limit_req_status 429;
}
# 导出、列表接口单独一档,收得更紧
location ~ ^/(api/v1/list|export|download) {
limit_req zone=peruser burst=10;
limit_req_status 429;
}
}
- 返回 429 而不是 444。444 直接断连,你看不到日志也拿不到证据,申诉时说不清;429 还能配合
Retry-After让正常用户知道该等一会。 - 阈值按近 14 天同时段单账号 P99 的 1.5 到 2 倍定,别拍脑袋。定完先跑一周观察模式,只记不拦。
Q:会话绑定该怎么做才不误伤?
- 登录时记下 UA 哈希 和 IP 的 /24 段,之后请求若两者都变了,不直接踢,要求一次二次验证(短信或邮箱验证码)。
- 单账号同时在线设备数设上限(比如 3 台),超出时踢最早的那台并通知本人。这是压共享号最有效的动作。
- 不要拿完整 IP 做绑定。手机切基站、公司出口漂移都会变,误伤率极高,用 /24 或 /16 更稳。
- 关键动作单独设门槛:批量下载、导出、修改密码前三件事,要求二次验证。正常用户一周碰不到一次,爬虫次次都要过。
Q:注册和试用环节能提前堵住多少?
- 手机号 + 设备指纹双维度限制:同一设备 24 小时内注册不超过 2 个号,同一手机号只能是本人。
- 试用账号前 24 小时降权:内容可见但限速,导出和批量下载直接关掉。真正有需求的用户感受不到,批量注册的成本立刻上升一个量级。
- 给新号设硬配额:每天可看条数、可导出条数、可并发数三个数分开写死,不要只限 QPS。
- 同一支付方式、同一收货地址、同一身份证下的多个账号要合并计数,否则换个号就重置配额。
Q:内容侧还能做什么,让抓走也没那么值钱?
- 关键字段服务端渲染。很多站把价格、联系方式放在 JS 变量里由前端渲染,爬虫抓一次接口就全拿到了,等于没设防。
- 列表页只给摘要,正文走鉴权接口并带频次限制;深分页设上限(比如 100 页),再往后要求精确搜索。爬虫最怕的就是「必须换关键词」,因为它不知道你的库里有什么。
- 图片加动态水印,水印里带账号标识和时间,事后能定位到泄露源。
- 导出内容埋暗记:零宽字符、错别字替换、字段顺序扰动。对方站点一出现这个标记,你就知道他手里的数据是从哪个号出来的。这招在维权和谈判时非常有用。
- 会员协议里明确禁止自动化批量抓取。没有这一条,后面想追责连依据都没有。
Q:确认是爬虫了,怎么处置才不误伤?
- 分五级走,别一步跳到封号:记录(只打标)→ 降速(429)→ 二次验证 → 临时封号 24 小时 → 永久封号。
- 封号前把证据落盘:账号、时间窗、请求总数、翻页深度、静态资源占比、并发 IP 数。这份记录既是内部复核依据,也是用户申诉时的答复素材。
- 误伤的六个常见来源要提前想好:公司 NAT 出口、家庭多设备同时看剧、手机与 PC 同步、第三方同步或笔记工具、代理商代操作、API 合作方。前四个靠设备数上限缓解,后两个靠白名单。
- 封号一定要能一键回滚,并且留申诉入口。误封一个年费会员的代价,比放过一个爬虫高得多。
Q:还有什么补充建议?
- 先量后拦。没有账号维度的基线数据,所有阈值都是猜的;基线至少要跑两周,覆盖工作日和周末。
- 别用 UA 黑名单对抗登录态爬虫。UA 一行字就能改,这类工具早就内置了主流浏览器的完整 UA。
- 把「单账号请求数」「单账号翻页深度」「导出调用次数」三个数做成周报,异常账号自然浮出来,比事后追查省力得多。
- 每月复核一次阈值和封禁名单,季节性业务(比如开学季、节假日)的峰值差异经常把静态阈值打成误封。