反反爬技术汇总

最后更新:2026-03-31 标准 requests/httpx 的 TLS Client Hello 与浏览器不同,部分服务(如 Cloudflare)会识别。 先识别反爬类型再选方案:IP 频率限制 → 代理池;设备指纹检测 → 浏览器模拟;签名验证 → JS 逆向;验证码 → 打码平台或 AI 识别;行为分析 → 无头浏览器 + 鼠标轨迹模拟。混合使用多种策略事半功倍。 代理 IP 质量决定成功率:数据中心 IP 被大量标记,住宅代理(Residential Proxy)最接近真实用户;移动代理(Mobile Proxy)绕过能力最强但最贵。按目

分享

官方文档:https://playwright.dev/python/docs/intro
适用场景:系统性绕过 Web 反爬机制,实现稳定数据采集

最后更新:2026-03-31


1. 反爬机制概览

反爬类型 检测维度 常见表现
IP 封禁 请求频率、IP 特征 429/403 响应、验证码
UA 检测 User-Agent 字符串 请求直接拒绝
JS 指纹 浏览器环境特征 返回空数据或重定向
验证码 行为挑战 滑块、文字点击、reCAPTCHA
签名校验 参数完整性 缺少 sign/token 返回错误
动态 JS 加密逻辑更新 定期换算法/混淆
TLS 指纹 TLS Client Hello 特征 非浏览器指纹被拒绝
行为分析 鼠标轨迹、点击间隔 机器行为触发封禁
Cookie 校验 Cookie 中含加密值 无 Cookie 或 Cookie 过期返回异常
账号风控 账号行为异常 账号封禁、降速

2. IP 反封禁

代理池策略

import random
import httpx

PROXIES = [
    "http://user:pass@proxy1:8080",
    "http://user:pass@proxy2:8080",
    "socks5://proxy3:1080",
]

def get_random_proxy():
    return random.choice(PROXIES)

async def fetch(url: str) -> dict:
    async with httpx.AsyncClient(proxy=get_random_proxy()) as client:
        resp = await client.get(url)
        return resp.json()

请求频率控制

import asyncio
import random

async def fetch_with_delay(url: str, min_delay: float = 1.0, max_delay: float = 3.0):
    """随机延迟,模拟人工浏览节奏"""
    await asyncio.sleep(random.uniform(min_delay, max_delay))
    async with httpx.AsyncClient() as client:
        return await client.get(url)

代理质量检测

async def check_proxy(proxy: str, timeout: float = 5.0) -> bool:
    try:
        async with httpx.AsyncClient(proxy=proxy, timeout=timeout) as client:
            resp = await client.get("https://httpbin.org/ip")
            return resp.status_code == 200
    except Exception:
        return False

3. 浏览器指纹对抗

WebDriver 特征清除

# Playwright
await page.add_init_script("""
    // 清除 webdriver 标记
    Object.defineProperty(navigator, 'webdriver', { get: () => undefined });

    // 伪造插件列表
    Object.defineProperty(navigator, 'plugins', {
        get: () => [
            { name: 'Chrome PDF Plugin', filename: 'internal-pdf-viewer' },
            { name: 'Chrome PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai' },
            { name: 'Native Client', filename: 'internal-nacl-plugin' },
        ],
    });

    // 伪造语言
    Object.defineProperty(navigator, 'languages', { get: () => ['zh-CN', 'zh', 'en'] });

    // 伪造 chrome 对象
    window.chrome = { runtime: {}, loadTimes: function(){}, csi: function(){}, app: {} };

    // 修复 permissions 查询
    const originalQuery = window.navigator.permissions.query;
    window.navigator.permissions.query = (parameters) => (
        parameters.name === 'notifications'
            ? Promise.resolve({ state: Notification.permission })
            : originalQuery(parameters)
    );
""")

Canvas 指纹对抗

# 在 Canvas 绘制时加入随机噪点,使指纹不固定
await page.add_init_script("""
    const originalGetImageData = CanvasRenderingContext2D.prototype.getImageData;
    CanvasRenderingContext2D.prototype.getImageData = function(x, y, w, h) {
        const imageData = originalGetImageData.call(this, x, y, w, h);
        for (let i = 0; i < 5; i++) {
            const idx = Math.floor(Math.random() * imageData.data.length / 4) * 4;
            imageData.data[idx] ^= 1;
        }
        return imageData;
    };
""")

WebGL 指纹对抗

await page.add_init_script("""
    const getParameter = WebGLRenderingContext.prototype.getParameter;
    WebGLRenderingContext.prototype.getParameter = function(parameter) {
        if (parameter === 37445) return 'Intel Inc.';           // VENDOR
        if (parameter === 37446) return 'Intel Iris OpenGL Engine';  // RENDERER
        return getParameter.call(this, parameter);
    };
""")

Audio 指纹对抗

await page.add_init_script("""
    const originalGetChannelData = AudioBuffer.prototype.getChannelData;
    AudioBuffer.prototype.getChannelData = function(channel) {
        const array = originalGetChannelData.call(this, channel);
        for (let i = 0; i < array.length; i += 100) {
            array[i] += Math.random() * 1e-7;
        }
        return array;
    };
""")

4. TLS 指纹对抗

标准 requests/httpx 的 TLS Client Hello 与浏览器不同,部分服务(如 Cloudflare)会识别。

使用 curl_cffi(推荐)

# pip install curl_cffi
from curl_cffi.requests import AsyncSession

async with AsyncSession(impersonate="chrome120") as session:
    resp = await session.get("https://example.com/api/data")
    print(resp.json())

# 支持的浏览器指纹
# chrome99, chrome100, ..., chrome120
# firefox91, firefox95, ...
# safari15_3, safari15_5, ...
# edge99, edge101

使用 tls-client(Python 绑定)

# pip install tls-client
import tls_client

session = tls_client.Session(
    client_identifier="chrome_120",
    random_tls_extension_order=True
)
resp = session.get("https://example.com/api")
print(resp.json())

5. 验证码处理

滑块验证码(基于轨迹模拟)

import asyncio
import random
import math

async def slide_captcha(page, slider_selector: str, track_distance: int):
    """模拟人工滑动轨迹"""
    slider = await page.query_selector(slider_selector)
    box = await slider.bounding_box()

    start_x = box["x"] + box["width"] / 2
    start_y = box["y"] + box["height"] / 2

    # 生成带缓动的轨迹点
    tracks = generate_track(track_distance)

    await page.mouse.move(start_x, start_y)
    await page.mouse.down()
    await asyncio.sleep(random.uniform(0.1, 0.3))

    current_x = start_x
    for move_x, delay in tracks:
        current_x += move_x
        jitter_y = start_y + random.uniform(-2, 2)
        await page.mouse.move(current_x, jitter_y)
        await asyncio.sleep(delay)

    await page.mouse.up()

def generate_track(distance: int) -> list[tuple[float, float]]:
    """生成类人工的加速-减速轨迹"""
    tracks = []
    current = 0
    # 加速阶段(前 60%)
    mid = distance * 0.6
    while current < mid:
        move = random.uniform(3, 8)
        current += move
        tracks.append((move, random.uniform(0.01, 0.02)))
    # 减速阶段(后 40%)
    while current < distance:
        move = random.uniform(1, 3)
        current += move
        if current > distance:
            move -= current - distance
            current = distance
        tracks.append((move, random.uniform(0.02, 0.05)))
    return tracks

图片验证码(OCR)

# pip install ddddocr
import ddddocr

ocr = ddddocr.DdddOcr()

async def solve_captcha(page, captcha_selector: str) -> str:
    # 截取验证码图片
    captcha_element = page.locator(captcha_selector)
    img_bytes = await captcha_element.screenshot()
    # OCR 识别
    result = ocr.classification(img_bytes)
    return result

第三方打码平台

import httpx

async def solve_with_2captcha(image_base64: str, api_key: str) -> str:
    """使用 2captcha 服务"""
    async with httpx.AsyncClient() as client:
        # 提交任务
        resp = await client.post("https://2captcha.com/in.php", data={
            "key": api_key,
            "method": "base64",
            "body": image_base64,
        })
        task_id = resp.text.split("|")[1]

        # 轮询结果
        for _ in range(20):
            await asyncio.sleep(5)
            resp = await client.get(f"https://2captcha.com/res.php?key={api_key}&action=get&id={task_id}")
            if resp.text.startswith("OK"):
                return resp.text.split("|")[1]
    raise Exception("验证码识别超时")

# 使用 httpx 的 CookieJar 自动管理 Cookie
async with httpx.AsyncClient(follow_redirects=True) as client:
    # 首次访问触发 Cookie 生成(如 __cfduid、acw_tc 等)
    await client.get("https://example.com")
    # 后续请求自动携带 Cookie
    resp = await client.get("https://example.com/api/data")
from DrissionPage import ChromiumPage

page = ChromiumPage()
page.get("https://example.com/login")
# ... 完成登录 ...

# 导出 cookies 给 requests/httpx 使用
cookies = {c["name"]: c["value"] for c in page.cookies()}

async with httpx.AsyncClient(cookies=cookies) as client:
    resp = await client.get("https://example.com/api/protected")

7. 反调试绕过

无限 debugger 绕过

// Hook Function 构造器,阻止 setInterval/setTimeout 中的 debugger
(function() {
    var _Function = Function;
    Function = function(...args) {
        var fn = _Function(...args);
        var body = fn.toString();
        if (body.includes('debugger')) {
            return function() {};
        }
        return fn;
    };
    Function.prototype = _Function.prototype;
})();
// 或直接覆盖 setInterval
var _setInterval = setInterval;
setInterval = function(fn, delay) {
    if (fn.toString().includes('debugger')) {
        return;
    }
    return _setInterval(fn, delay);
};

时间检测绕过

// 部分反调试通过计时判断是否有人工调试(调试时计时器走慢)
// 覆盖 Date.now 和 performance.now
var startTime = Date.now();
Date.now = function() { return startTime; };
performance.now = function() { return 0; };

8. 行为模拟

随机化操作时序

async def human_like_click(page, selector: str):
    """人工化点击:移动到元素附近 → 等待 → 点击"""
    element = page.locator(selector)
    box = await element.bounding_box()

    # 随机点击元素内部位置(非正中心)
    x = box["x"] + box["width"] * random.uniform(0.3, 0.7)
    y = box["y"] + box["height"] * random.uniform(0.3, 0.7)

    # 先移动到元素附近
    await page.mouse.move(x + random.uniform(-10, 10), y + random.uniform(-10, 10))
    await asyncio.sleep(random.uniform(0.1, 0.3))
    await page.mouse.move(x, y)
    await asyncio.sleep(random.uniform(0.05, 0.15))
    await page.mouse.click(x, y)

随机滚动模拟浏览

async def simulate_reading(page, duration: float = 5.0):
    """模拟用户阅读页面"""
    elapsed = 0.0
    while elapsed < duration:
        scroll_amount = random.randint(100, 400)
        await page.evaluate(f"window.scrollBy(0, {scroll_amount})")
        delay = random.uniform(0.5, 2.0)
        await asyncio.sleep(delay)
        elapsed += delay

9. 常用工具汇总

工具 用途 安装
curl_cffi TLS 指纹伪造 pip install curl_cffi
playwright-stealth Playwright 反检测 pip install playwright-stealth
ddddocr 验证码 OCR pip install ddddocr
mitmproxy 流量分析与修改 pip install mitmproxy
Frida 动态插桩 pip install frida-tools
2captcha 打码平台 API 服务
anticaptcha 打码平台 API 服务
隧道代理 IP 自动轮换 商业服务

10. 最佳实践

分层对抗策略

简单目标 → httpx / requests + 代理池
↓(有 JS 渲染)
DrissionPage / Playwright(浏览器模式)
↓(有强指纹检测)
curl_cffi + playwright-stealth
↓(有验证码)
ddddocr / 打码平台
↓(有 native 加密)
Frida 逆向 → unidbg 批量调用
↓(极难)
手动模拟 / RPC 远程调用

规避风险的基本原则

  • 使用真实 User-Agent,定期更新
  • 设置随机请求间隔(1~5 秒)
  • 每次请求随机化 Headers 顺序
  • 使用住宅代理(Residential Proxy)代替数据中心代理
  • 模拟正常浏览行为(访问首页 → 列表页 → 详情页)
  • Cookie 来自真实浏览器,而非手动构造

最佳实践

先识别反爬类型再选方案:IP 频率限制 → 代理池;设备指纹检测 → 浏览器模拟;签名验证 → JS 逆向;验证码 → 打码平台或 AI 识别;行为分析 → 无头浏览器 + 鼠标轨迹模拟。混合使用多种策略事半功倍。

代理 IP 质量决定成功率:数据中心 IP 被大量标记,住宅代理(Residential Proxy)最接近真实用户;移动代理(Mobile Proxy)绕过能力最强但最贵。按目标站点防御等级选择,不要盲目用最贵的。

Cookie 池维护比签名逆向更稳定:部分站点签名算法复杂(多版本混淆+WASM),维护成本高;若 Cookie 有效期长(几天到几周),维护一个大 Cookie 池(Playwright 自动登录+定期刷新)比破解签名更划算。

Playwright 的 stealth 模式用于绕过 headless 检测playwright-extra + puppeteer-extra-plugin-stealth 修补了 navigator.webdriverchrome.runtime 等常见 headless 检测点,比手动 patch 更完整。

建立完整指纹档案:User-Agent、Accept-Language、Canvas 指纹、WebGL 指纹、屏幕分辨率、时区要相互一致,各指纹组合构成设备档案,前后不一致会触发风控。


常见陷阱

陷阱:换了 IP 后仍然被封

现象: 切换代理 IP 后请求仍然报 403,且频率不高。
原因: 被封的不是 IP,而是设备指纹(Canvas/WebGL 指纹、TLS 指纹、浏览器特征);只换 IP 不换指纹,风控系统仍能识别同一设备。
解决: 同时更换 IP 和 User-Agent,清除/随机化 Cookie,使用不同浏览器配置(Profile)隔离指纹;严格场景用真实不同设备或浏览器实例。

陷阱:模拟点击后验证码仍弹出

现象: Playwright 模拟了鼠标移动和点击,但目标站仍然触发滑块验证。
原因: 行为分析不只看鼠标事件,还看事件时间间隔、触发顺序、Scroll 行为等综合指标;程序生成的事件在统计上和真人有差异。
解决: 在鼠标轨迹上叠加随机抖动(Bézier 曲线路径 + 随机速度变化);在提交前加入随机等待时间;考虑对接打码平台专门处理验证码。

陷阱:设置了随机延迟仍触发频率限制

现象: 每次请求间隔 1~3 秒,但几百次请求后 IP 仍然被限速。
原因: 频率限制基于滑动时间窗口(如每分钟不超过 30 次),偶尔连续快速请求(抖动低延时端)可能超出限制;或站点用分布式 Redis 统计跨 IP 的账号频率。
解决: 用令牌桶算法控制实际速率;对同一账号的请求做更严格的间隔控制,不只控制 IP 维度的频率。


参见

js逆向调试技巧
Frida基础
mitmproxy完全指南
补环境

阅读更多

Web 安全基础

1. HTML 转义(服务端渲染必须): 2. CSP(Content Security Policy): 3. HttpOnly Cookie:防止 JS 读取会话 Cookie: 4. 前端框架防护: 攻击者在第三方网站构造一个表单,诱导已登录用户提交,浏览器会自动携带目标站的 Cookie。 触发条件: 1. 用户已登录目标网站(Cookie 有效) 2. 目标 API 仅凭 Cookie 识别用户身份 3. 请求来源未验证 1. CSRF Token(推荐): 2. SameSite Cookie: 3. 验证 Origin/Referer 头:

By yellowdog

HTTP 协议深度指南

HTTP(HyperText Transfer Protocol)是 Web 的基础传输协议,基于 TCP/IP,采用请求/响应模型。 相关文档:Web安全基础(/web-an-quan-ji-chu/) FastAPI完全指南(/fastapi-wan-quan-zhi-nan/) Nginx完全指南(/nginx-wan-quan-zhi-nan/) 幂等性:多次执行相同请求,服务器状态结果相同。PUT /users/1 多次执行结果一致;POST /users 每次创建新资源,非幂等。 浏览器直接从本地缓存读取,不向服务器发送请求。 缓存命中时,状

By yellowdog

系统设计基础

SLA 对照表: 选择建议:无状态服务(Web 层、API 层)优先水平扩展;数据库初期垂直扩展,达到瓶颈后考虑分库分表或读写分离。 缓存穿透(查询不存在的 key,每次都打到 DB): 缓存击穿(热点 key 过期,瞬间大量请求打到 DB): 缓存雪崩(大量 key 同时过期,或缓存服务宕机): 令牌桶 Python 实现: Redis 实现分布式限流(滑动窗口): URL 命名规则: Cursor 分页响应格式: 雪花算法结构(64 bit): 定义:分布式系统不能同时满足以下三个特性: 在分布式环境中 P 是必须保证的,所以实际是 CP vs AP

By yellowdog

算法思路与模板

二分查找要求序列有序,每次将搜索范围缩减一半,时间复杂度 O(log n)。 两个指针从两端向中间收缩,常用于有序数组。 滑动窗口维护一个满足条件的区间 left, right,right 不断向右扩张,条件不满足时收缩 left。 滑动窗口通用框架: 1. 确定"子问题":原问题可以分解为哪些规模更小的同类问题 2. 定义 dpi 或 dpij 的含义,要足够清晰 3. 推导状态转移方程 4. 确定初始状态(边界条件) 5. 确定计算顺序(确保依赖的子问题先计算) 每件物品最多选一次。dpj = 容量为 j 时的最大价值,逆序遍历容量防止重复选取。 每

By yellowdog