常见反爬手段与应对思路
反爬不是一个开关,而是站点为了保护带宽、数据资产与业务公平性逐步叠上去的一层层门槛。本章按"识别—为什么—合规应对"逐类拆解常见手段,并把红线写清楚:能用官方接口、降频或申请授权解决的,就不要去研究怎么绕过。
先分清三层反爬
| 层次 | 目的 | 典型手段 | 正常用户的感受 |
|---|---|---|---|
| 限流类 | 控制资源消耗 | IP/账号频率限制、并发限制、直接返回 429 | 偶尔需要等待或重试 |
| 身份类 | 区分人与脚本 | UA/Referer 校验、Cookie/Token 校验、验证码、行为检测 | 需要登录,偶尔要滑块 |
| 内容类 | 让脚本拿不到可用数据 | 动态渲染、字体混淆、图片替换、参数加密、蜜罐字段 | 几乎无感 |
识别顺序照这张表走:先怀疑限流(最常见),再怀疑身份,最后才怀疑内容混淆。
手段清单:表现、识别与合规应对
| 手段 | 典型表现 | 怎么识别 | 合规应对 |
|---|---|---|---|
| 频率与并发限制 | 429、403、响应变慢、内容变空 | 看状态码,等一段时间重试是否恢复 | 降频、加间隔、错峰、限制并发 |
| UA/Referer 校验 | 直接请求 403,带上常规请求头就正常 | 对比有无请求头时的响应差异 | 使用可识别的自定义 UA,不冒充他人身份 |
| Cookie/Token 校验 | 302 跳登录页,或返回未登录 JSON | 逐步补上 Cookie 复现请求 | 只自动化自己的账号,不共享凭据 |
| 验证码 | 出现滑块、点选或图形码 | 响应体里出现验证码组件 | 停止自动请求,改人工或走官方接口 |
| 蜜罐字段 | 抓到的数据里混入诱饵条目 | 与页面实际展示内容比对 | 忽略隐藏节点,不做批量提交 |
| 动态渲染 | 源码里没有数据,浏览器里有 | 源码中搜不到字段关键字 | 走 XHR 接口,或用浏览器渲染 |
| 字体/图片混淆 | 数字显示正常,复制出来是乱码 | 对比页面显示与源码文本 | 使用官方导出功能,不做字形还原 |
| 参数加密 | 请求参数是长串哈希或签名 | Payload 里出现签名类参数 | 不做逆向,改走官方开放接口 |
| 行为检测 | 无头浏览器被拦、要求人工交互 | 换真实浏览器即可访问 | 尊重站点策略,改走官方渠道 |
限流:最常见,也最容易处理
限流几乎一定会遇到,处理姿势只有一条:听劝。
import random
import time
import requests
session = requests.Session()
session.headers["User-Agent"] = "RuilinToolsBot/1.0 (+https://example.com/bot; contact@example.com)"
def fetch(url: str, max_attempts: int = 4) -> str | None:
"""遇到 429/5xx 指数退避;连续失败就放弃,不硬顶。"""
for attempt in range(max_attempts):
try:
resp = session.get(url, timeout=(3, 10))
except requests.RequestException as exc:
print(f"网络异常 {exc},第 {attempt + 1} 次")
else:
if resp.status_code == 200:
return resp.text
if resp.status_code in (429, 500, 502, 503, 504):
# Retry-After 是站点给出的明确等待时间,优先级最高
delay = float(resp.headers.get("Retry-After") or 0) or min(2 ** attempt, 30)
else:
print(f"不可重试状态码 {resp.status_code},跳过:{url}")
return None
time.sleep(delay * random.uniform(0.8, 1.2))
print(f"多次重试仍失败,放弃:{url}")
return None
Retry-After 比任何自研退避算法都权威:站点点名要你等多久,就等多久。
UA 与 Referer:标明自己,而不是伪装别人
- 自定义 UA 写清用途与联系方式,是"可被联系"的礼貌,也是对方按组放行的依据。
- 站点校验
Referer,说明它不希望资源被外部直接引用;此时优先找官方接口,而不是拼一个假 Referer。 - 浏览器 UA 白名单检测很难对抗,也不值得对抗:被判为脚本不是技术难题,是对方明确的态度。
Cookie 与 Token:只代表你自己
登录态只用于自动化自己的账号,例如定期导出自己后台的报表。不要共享账号,不要批量注册,不要用他人凭据访问付费内容。检测登录失效的做法很简单:状态码 401/403,或响应体里出现"未登录"标识,就停下来重新登录,并把这次失效记进日志。
验证码:出现即停止
验证码是站点明确表达"此处不接受自动化"。任何以自动识别为目的的打码方案或第三方打码平台,都是在绕过访问控制,不在本教程讨论范围内。合规路径只有三条:
1. 改用官方开放接口(多数平台对开发者开放同类数据,且带配额与文档)
2. 人工完成一次验证后,在自己的账号内做低频操作
3. 联系站点说明用途,申请数据合作或批量导出
动态渲染与内容混淆
| 现象 | 背后的手段 | 正确做法 |
|---|---|---|
| 源码里没有商品价格 | JS 异步填充 | 找 XHR/Fetch 接口,直接取 JSON |
| 数字复制出来是方框或别的字 | 自定义字体映射 | 使用官方导出、报表或开放数据 |
| 关键字段是图片 | 图片替换文字 | 不做图片识别,找结构化接口 |
| 参数是一长串哈希 | 签名或加密 | 不逆向,申请 API Key 走官方通道 |
| 页面里有看不见的链接 | 蜜罐陷阱 | 抓取时过滤掉隐藏节点 |
明确不该做的事
| 不该做 | 原因 |
|---|---|
| 逆向他人签名与加密参数 | 涉嫌绕过技术措施,且改版即失效,维护是无底洞 |
| 自动识别验证码或使用打码平台 | 直接绕过访问控制,性质比高频抓取严重得多 |
| 伪造 UA 冒充搜索引擎或他人身份 | 可能构成不正当竞争与欺诈性访问 |
| 用代理池隐藏身份后继续高频抓取 | 只是把风险外推,行为性质没有改变 |
| 抓取并转卖付费内容与图文作品 | 侵犯著作权,可能承担赔偿责任 |
合规替代路径
- 官方 API:最省事,有配额、有文档、字段稳定。
- 开放数据:政府开放数据平台、公开数据集、学术数据仓库。
- 购买授权数据:数据交易所或授权明确的数据服务商。
- 合作授权:直接联系站点,谈数据合作、镜像或批量导出。
- 自己生产数据:问卷、众包、自家业务埋点,最干净也最可控。
- 法律底线:《个人信息保护法》《数据安全法》《著作权法》约束的是数据本身与使用方式,不因为改用了官方接口就消失;采集前先确认合法性基础与使用边界。
常见坑
| 坑 | 说明 |
|---|---|
| 把 403 一律当成"需要加请求头" | 也可能是频率超限或已被封,先看响应体与重试结果 |
| 无头浏览器被识别后加各种"反检测" | 越描越黑,先确认站点是否允许自动化访问 |
| 只统计成功数不记失败原因 | 无法判断是改版、限流还是封禁 |
| 单次试跑通过就放开并发 | 试跑成功不代表批量安全,要留出余量 |
小结:反爬手段按限流、身份、内容三层识别,应对姿势只有降频、标明身份、走官方接口三条正路;验证码、签名逆向、代理隐藏这几类,识别出来就该停下并改走合规渠道。