> ## Content Index
> Fetch the complete content index at: https://blog.vercanti.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# 反反爬技术汇总
- URL: https://blog.vercanti.com/fan-fan-pa-ji-zhu-hui-zong/
- Published: 2026-08-28T14:35:11.000Z
- Updated: 2026-08-28T14:58:16.000Z
- Description: 最后更新：2026-03-31 标准 requests/httpx 的 TLS Client Hello 与浏览器不同，部分服务（如 Cloudflare）会识别。 先识别反爬类型再选方案：IP 频率限制 → 代理池；设备指纹检测 → 浏览器模拟；签名验证 → JS 逆向；验证码 → 打码平台或 AI 识别；行为分析 → 无头浏览器 + 鼠标轨迹模拟。混合使用多种策略事半功倍。 代理 IP 质量决定成功率：数据中心 IP 被大量标记，住宅代理（Residential Proxy）最接近真实用户；移动代理（Mobile Proxy）绕过能力最强但最贵。按目
- Author: yellowdog
- Tags: js逆向, 技巧步骤

> 官方文档：<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 反封禁

### 代理池策略

```python
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()

```

### 请求频率控制

```python
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)

```

### 代理质量检测

```python
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 特征清除

```python
# 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 指纹对抗

```python
# 在 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 指纹对抗

```python
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 指纹对抗

```python
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（推荐）

```python
# 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 绑定）

```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\. 验证码处理

### 滑块验证码（基于轨迹模拟）

```python
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）

```python
# 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

```

### 第三方打码平台

```python
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("验证码识别超时")

```

---

## 6\. Cookie 反爬对抗

### Cookie 自动维护

```python
# 使用 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")

```

### 浏览器 Cookie 导出给 requests

```python
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 绕过

```javascript
// 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;
})();

```

```javascript
// 或直接覆盖 setInterval
var _setInterval = setInterval;
setInterval = function(fn, delay) {
    if (fn.toString().includes('debugger')) {
        return;
    }
    return _setInterval(fn, delay);
};

```

### 时间检测绕过

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

```

---

## 8\. 行为模拟

### 随机化操作时序

```python
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)

```

### 随机滚动模拟浏览

```python
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.webdriver`、`chrome.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逆向调试技巧](https://blog.vercanti.com/js-ni-xiang-diao-shi-ji-qiao/)  
[Frida基础](https://blog.vercanti.com/frida-ji-chu-zhi-nan/)  
[mitmproxy完全指南](https://blog.vercanti.com/mitmproxy-wan-quan-zhi-nan/)  
[补环境](https://blog.vercanti.com/bu-huan-jing/)