CloakBrowser Humanize 行为层深度解析

深入分析 CloakBrowser 的 humanize 功能源码,揭示贝塞尔鼠标曲线、逐字符按键时序、滚动物理模拟以及动作性预检查的实现原理。

亿牛云技术团队2026年7月12日9 分钟阅读

概述

浏览器自动化面临的挑战不仅来自静态指纹检测(Canvas、WebGL、navigator.webdriver),还来自行为分析。现代反机器人系统如 DataDome、Shape Security 和 Akamai 不仅检查"你是谁",还分析"你如何操作"——鼠标移动路径、按键节奏、滚动模式、点击位置分布等。这些行为信号即使浏览器指纹完美,也能将自动化程序与真实用户区分开来。

CloakBrowser 的 humanize=True 功能正是为了解决这一问题而设计。它不是一个简单的"随机延迟"方案,而是一个完整的行为模拟层,在 Playwright/Puppeteer 的页面方法之上进行了深度修补。当启用 humanize=True 时,所有鼠标、键盘和滚动交互都会被自动替换为模拟人类行为的实现。

本文将通过分析 CloakBrowser 源码中 cloakbrowser/human/ 目录的源代码,深入解析其实现原理。

架构概览

Humanize 模块位于 cloakbrowser/human/ 目录下,包含以下核心文件:

文件职责
config.py配置数据类、预设(default/careful)、参数合并工具
mouse.py / mouse_async.py贝塞尔曲线鼠标移动、点击模拟(同步/异步)
keyboard.py / keyboard_async.py逐字符输入、错字模拟、Shift 符号处理(同步/异步)
scroll.py / scroll_async.py加速-巡航-减速滚动、过冲与修正(同步/异步)
actionability.py / actionability_async.py动作性预检查:可见、稳定、启用、指针事件通过
__init__.py入口点:将页面方法修补为 humanize 版本

修补机制

当您调用 launch(humanize=True) 时,CloakBrowser 的修补流程如下:

  1. 浏览器级别:在 __init__.pypatch_page()patch_page_async() 函数中,将页面对象的 clickfilltypehovercheck 等方法替换为 humanize 版本
  2. 保留原始方法:原始方法保存在 page._original 中,供需要原始速度的场景使用
  3. Locator 类修补:通过 _patch_locator_class_sync()_patch_locator_class_async() 在类级别修补 Playwright 的 Locator
  4. Frame 支持:自 v0.4.10 起,iframe 内的交互也使用 humanize 行为(修复 Issue #428)
# 来自 __init__.py 的修补流程简化示意
page._original = originals  # 保存原始方法
page._human_cfg = cfg       # 保存 humanize 配置
page.click = _human_click   # 替换为 humanize 版本
page.fill = _human_fill
# ... 更多方法替换

HumanConfig:所有可调参数

config.py 中的 HumanConfig 数据类集中了所有行为参数。以下是核心参数及其含义:

键盘参数

参数默认值说明
typing_delay70ms每字符基础延迟
typing_delay_spread40ms延迟随机偏差范围
typing_pause_chance0.1 (10%)打字中途"思考暂停"的概率
typing_pause_range(400, 1000)ms思考暂停持续时间
mistype_chance0.02 (2%)打错字的概率
mistype_delay_notice(100, 300)ms发现错字后的延迟
mistype_delay_correct(50, 150)ms修正错字后的延迟
field_switch_delay(800, 1500)ms切换输入字段的时间
key_hold(15, 35)ms按键保持时间

鼠标参数

参数默认值说明
mouse_steps_divisor8控制移动步数的除数
mouse_min_steps25最小移动步数
mouse_max_steps80最大移动步数
mouse_wobble_max1.5px移动路径抖动幅度
mouse_overshoot_chance0.15 (15%)过冲概率
mouse_overshoot_px(3, 6)px过冲像素范围
mouse_burst_size(3, 5)突发移动包大小
mouse_burst_pause(8, 18)ms突发包间暂停
click_aim_delay_input(60, 140)ms输入元素点击瞄准延迟
click_aim_delay_button(80, 200)ms按钮点击瞄准延迟
click_hold_input(40, 100)ms输入元素点击保持时间
click_hold_button(60, 150)ms按钮点击保持时间

滚动参数

参数默认值说明
scroll_delta_base(80, 130)基础滚动增量
scroll_delta_variance0.2增量随机变化率
scroll_pause_fast(30, 80)ms快速滚动暂停
scroll_pause_slow(80, 200)ms慢速滚动暂停
scroll_accel_steps(2, 3)加速步骤数
scroll_decel_steps(2, 3)减速步骤数
scroll_overshoot_chance0.1 (10%)滚动过冲概率
scroll_overshoot_px(50, 150)px过冲像素范围
scroll_settle_delay(300, 600)ms滚动稳定延迟

预设对比:default vs careful

# careful 预设的关键区别(来自 _careful_config() 函数)
careful = HumanConfig(
    typing_delay=100,          # 更慢打字
    typing_delay_spread=50,
    typing_pause_chance=0.15,  # 更多思考暂停
    mouse_overshoot_chance=0.10,
    click_aim_delay_input=(80, 180),   # 更长瞄准时间
    click_aim_delay_button=(120, 280),
    scroll_pause_fast=(100, 200),      # 更慢滚动
    idle_between_actions=True,          # 动作间空闲微移动
    idle_between_duration=(0.4, 1.0),  # 空闲时长(秒)
)

careful 预设的关键理念:更慢、更谨慎、更多深思熟虑的暂停。适合高安全目标如 DataDome 保护下的网站。

贝塞尔曲线鼠标移动

鼠标移动的 humanize 实现位于 mouse.py_bezier() 函数和 human_move() 函数中。

三次贝塞尔曲线

def _bezier(p0: Point, p1: Point, p2: Point, p3: Point, t: float) -> Point:
    """计算三次贝塞尔曲线上的点。"""
    u = 1 - t
    uu = u * u
    uuu = uu * u
    tt = t * t
    ttt = tt * t
    return Point(
        uuu * p0.x + 3 * uu * t * p1.x + 3 * u * tt * p2.x + ttt * p3.x,
        uuu * p0.y + 3 * uu * t * p1.y + 3 * u * tt * p2.y + ttt * p3.y,
    )

CloakBrowser 使用三次贝塞尔曲线模拟鼠标路径,包含两个控制点(p1, p2):

  • 起点 (p0):当前鼠标位置
  • 终点 (p3):目标元素位置
  • 控制点 (p1, p2):在 _random_control_points() 中根据起点和终点的方向随机生成,引入曲率和方向偏差

控制点生成

def _random_control_points(start: Point, end: Point) -> Tuple[Point, Point]:
    dx = end.x - start.x
    dy = end.y - start.y
    dist = math.hypot(dx, dy) or 1
    px = -dy / dist      # 垂直方向单位向量
    py = dx / dist
    bias1 = rand(-0.3, 0.3) * dist  # 随机偏移量
    bias2 = rand(-0.3, 0.3) * dist
    return (
        Point(start.x + dx * 0.25 + px * bias1, start.y + dy * 0.25 + py * bias1),
        Point(start.x + dx * 0.75 + px * bias2, start.y + dy * 0.75 + py * bias2),
    )

这种方法的精妙之处:

  • 控制点沿路径的 25% 和 75% 位置偏移
  • 偏移方向垂直于路径方向(px, py),产生自然的曲线
  • 偏移量随机(-30% 到 +30%),确保每次路径不同

缓动函数

def _ease_in_out(t: float) -> float:
    """三次缓入缓出函数。"""
    if t < 0.5:
        return 4 * t * t * t
    return 1 - pow(-2 * t + 2, 3) / 2

_ease_in_out() 函数使鼠标移动在起点和终点处速度较慢,中间较快——这与人类手部运动的加速度模式一致。

抖动与过冲

# 在路径点添加随机抖动
wobble_amp = math.sin(math.pi * progress) * cfg.mouse_wobble_max
wx = pt.x + (random.random() - 0.5) * 2 * wobble_amp
wy = pt.y + (random.random() - 0.5) * 2 * wobble_amp

抖动幅度在路径中间最大(sin(pi * progress)),模拟人类手部运动的不稳定性。

# 过冲:有 15% 概率略微超过目标再拉回
if random.random() < cfg.mouse_overshoot_chance:
    overshoot_dist = rand_range(cfg.mouse_overshoot_px)
    angle = math.atan2(end_y - start_y, end_x - start_x)
    raw.move(round(end_x + math.cos(angle) * overshoot_dist),
             round(end_y + math.sin(angle) * overshoot_dist))
    sleep_ms(rand(30, 70))
    raw.move(round(end_x + (random.random() - 0.5) * 4),
             round(end_y + (random.random() - 0.5) * 4))

过冲模拟了人类瞄准时经常会略微超过目标然后修正的行为——这是行为检测系统常用的判断信号之一。

突发暂停

鼠标移动不是连续平滑的,而是分为"突发包":

burst_counter += 1
if burst_counter >= burst_size and i < steps:
    sleep_ms(rand_range(cfg.mouse_burst_pause))
    burst_counter = 0

这模拟了人类鼠标移动中的微暂停——人类的手部运动本质上是由一系列快速微运动组成的。

键盘输入的逐字符模拟

键盘 humanize 实现位于 keyboard.pyhuman_type() 函数中。

逐字符时序

def human_type(page, raw, text, cfg, cdp_session=None):
    for i, ch in enumerate(text):
        # 非 ASCII 字符(Cyrillic, CJK, emoji)— 使用 insertText
        if not ch.isascii():
            sleep_ms(rand_range(cfg.key_hold))
            raw.insert_text(ch)
            if i < len(text) - 1:
                _inter_char_delay(cfg)
            continue
 
        # 错字模拟
        if random.random() < cfg.mistype_chance and ch.isalnum():
            wrong = _get_nearby_key(ch)
            _type_normal_char(raw, wrong, cfg)
            sleep_ms(rand_range(cfg.mistype_delay_notice))
            raw.down("Backspace")
            sleep_ms(rand_range(cfg.key_hold))
            raw.up("Backspace")
            sleep_ms(rand_range(cfg.mistype_delay_correct))
 
        # 处理大写字母
        if ch.isupper() and ch.isalpha():
            _type_shifted_char(page, raw, ch, cfg)
        # 处理 Shift 符号
        elif ch in SHIFT_SYMBOLS:
            _type_shift_symbol(page, raw, ch, cfg, cdp_session)
        else:
            _type_normal_char(raw, ch, cfg)
 
        if i < len(text) - 1:
            _inter_char_delay(cfg)

字符间延迟

def _inter_char_delay(cfg):
    """模拟打字节奏的变化。"""
    base = cfg.typing_delay
    spread = cfg.typing_delay_spread
    delay = base + random.gauss(0, spread)  # 正态分布延迟
    sleep_ms(max(10, delay))
 
    # 思考暂停:有 10% 概率在打字中途"思考"
    if random.random() < cfg.typing_pause_chance:
        sleep_ms(rand_range(cfg.typing_pause_range))

打字节奏使用高斯分布(正态分布)来产生自然变化,而非固定延迟。这是区分人类和机器的关键行为特征。

错字与自修正

_neighbors = {
    'a': 'sqwz', 'b': 'vghn', 'c': 'xdfv', 'd': 'sfecx',
    # ... 更多键盘邻近映射
}
 
def _get_nearby_key(ch: str) -> str:
    """返回相邻按键上的随机字符(模拟打字错误)。"""
    lower = ch.lower()
    if lower in NEARBY_KEYS:
        neighbors = NEARBY_KEYS[lower]
        wrong = random.choice(neighbors)
        return wrong.upper() if ch.isupper() else wrong
    return ch

错字模拟使用 QWERTY 键盘的邻近映射表,选择目标字符旁边的按键——这模拟了真实的打字错误模式。

CDP 隐身路径

对于 Shift 符号(@, #, $, % 等),CloakBrowser 提供了两条路径:

  1. 隐身路径(有 CDP 会话时):使用 Input.dispatchKeyEvent CDP 命令,产生 isTrusted=true 事件,没有 evaluate 堆栈痕迹
  2. 回退路径(无 CDP 会话时):使用 insertText + page.evaluate,可能被检测到 isTrusted=false

隐身路径是 CloakBrowser 区别于简单 JS 注入方案的关键特性之一。

滚动物理模拟

滚动 humanize 实现位于 scroll.pyhuman_scroll_into_view() 函数中。

三段式滚动模型

CloakBrowser 的滚动模拟采用真实物理模型:

加速 → 巡航 → 减速 → 过冲 → 修正 → 稳定
# 加速阶段
if i < accel_steps:
    delta = rand(80, 100)
    pause = rand_range(cfg.scroll_pause_slow)
# 减速阶段
elif i >= total_clicks - decel_steps:
    delta = rand(60, 90)
    pause = rand_range(cfg.scroll_pause_slow)
# 巡航阶段
else:
    delta = rand_range(cfg.scroll_delta_base)
    pause = rand_range(cfg.scroll_pause_fast)
  • 加速(2-3 步):起始滚动较慢,增量较小
  • 巡航:全速滚动,增量较大
  • 减速(2-3 步):接近目标时减速

平滑滚轮事件

def _smooth_wheel(raw: RawMouse, delta: int, cfg: HumanConfig) -> None:
    """将一个逻辑滚动分解为一系列小滚轮事件(模拟真实惯性)。"""
    abs_d = abs(delta)
    sign = 1 if delta > 0 else -1
    sent = 0
    while sent < abs_d:
        step_size = rand(20, 40)
        chunk = min(step_size, abs_d - sent)
        raw.wheel(0, round(chunk) * sign)
        sent += chunk
        sleep_ms(rand(8, 20))

每次逻辑滚动被分解为多个小的滚轮事件(20-40px 每步),步间暂停 8-20ms——模拟鼠标滚轮的物理惯性。

过冲与修正

# 过冲:有 10% 概率滚过目标然后拉回
if random.random() < cfg.scroll_overshoot_chance:
    overshoot_px = round(rand_range(cfg.scroll_overshoot_px)) * direction
    _smooth_wheel(raw, overshoot_px, cfg)
    sleep_ms(rand_range(cfg.scroll_settle_delay))
    corrections = rand_int_range((1, 2))
    for _ in range(corrections):
        corr_delta = round(rand(40, 80)) * -direction
        _smooth_wheel(raw, corr_delta, cfg)
        sleep_ms(rand(100, 250))

滚动过冲模拟了人类滚轮操作中常见的"滚过了再回来"的现象。

动作性预检查

actionability.py 中,CloakBrowser 实现了 Playwright 风格的动作性检查,确保在 humanize 操作之前元素是可交互的:

  • 可见:元素在视口中且未被隐藏
  • 稳定:元素位置不再变化(无动画)
  • 启用:元素未被禁用
  • 指针事件通过:元素上方没有其他元素拦截点击

这些检查使用 CDP 隔离执行环境(_SyncIsolatedWorld / _AsyncIsolatedWorld)执行 DOM 查询,避免在页面主执行环境中留下评估痕迹。

# 使用 CDP 隔离世界执行 DOM 查询
class _SyncIsolatedWorld:
    def evaluate(self, expression: str) -> Any:
        # 通过 CDP 的 Page.createIsolatedWorld 创建隔离上下文
        # 评估结果不会在 Error.stack 中留下 "eval at evaluate :302:" 痕迹
        ...

自定义配置示例

from cloakbrowser import launch_async
 
browser = await launch_async(
    headless=False,
    humanize=True,
    human_config={
        "mistype_chance": 0.05,          # 5% 错字率
        "typing_delay": 120,             # 更慢打字(120ms/字符)
        "typing_pause_chance": 0.15,     # 更多思考暂停
        "idle_between_actions": True,    # 动作间空闲微移动
        "idle_between_duration": [0.5, 1.5],  # 空闲时长(秒)
        "mouse_overshoot_chance": 0.20,  # 更多过冲
        "scroll_overshoot_chance": 0.15, # 更多滚动过冲
    }
)

每个方法调用还可以临时覆盖配置:

page.fill("input#email", "user@example.com",
          human_config={"typing_delay": 200, "mistype_chance": 0.1})

行为检测测试效果

根据 CloakBrowser README,启用 humanize=True 后,deviceandbrowserinfo.com 的行为检测测试通过了全部 24/24 信号,标记为 "You are human!"。

检测项无 humanize有 humanize
deviceandbrowserinfo.com 行为检测部分通过24/24 全部通过
DataDome 行为分析可能拦截通常通过
鼠标移动模式检测直线瞬移(明显机器人)贝塞尔曲线
键盘输入模式检测无间隔瞬发逐字符 + 可变延迟

性能考量

启用 humanize=True 会增加操作延迟,因为每次交互都增加了:

  • 贝塞尔曲线路径计算(约 0.1-0.5ms)
  • 多点鼠标移动(根据距离 25-80 步)
  • 字符间延迟(每字符 70-150ms)
  • 动作性检查(1-3 次 DOM 查询)

对于不需要行为模拟的批量数据提取操作,可以通过访问原始方法绕过:

# 原始方法(无 humanize)用于快速操作
page._original.fill("input#quantity", "100")
page._original.click("button#submit")

结论

CloakBrowser 的 humanize 功能是一个精心设计的行为模拟层,通过贝塞尔曲线鼠标路径、逐字符键盘时序、三段式滚动物理模型和动作性预检查,在浏览器自动化中实现了高度逼真的人类操作模式。其源码实现(位于 cloakbrowser/human/ 目录)展现了对手部运动学、打字动力学和滚动物理学的深入理解。

与简单的随机延迟方案不同,CloakBrowser 的 humanize 模块在每个层级都设计了统计分布、随机变化和物理模型,使得自动化行为在统计上与真实用户不可区分。对于需要绕过行为检测系统的反爬虫生产环境,humanize=True 是不可或缺的配置项。

本文基于 CloakBrowser v0.4.10(2026 年 7 月)版本源码撰写。相关源码位于 cloakbrowser/human/ 目录。

需要企业代理方案?

我们可根据目标站点、并发规模与稳定性目标提供定制方案。