CloakBrowser Humanize 行为层深度解析
深入分析 CloakBrowser 的 humanize 功能源码,揭示贝塞尔鼠标曲线、逐字符按键时序、滚动物理模拟以及动作性预检查的实现原理。
概述
浏览器自动化面临的挑战不仅来自静态指纹检测(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 的修补流程如下:
- 浏览器级别:在
__init__.py的patch_page()或patch_page_async()函数中,将页面对象的click、fill、type、hover、check等方法替换为 humanize 版本 - 保留原始方法:原始方法保存在
page._original中,供需要原始速度的场景使用 - Locator 类修补:通过
_patch_locator_class_sync()和_patch_locator_class_async()在类级别修补 Playwright 的Locator类 - 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_delay | 70ms | 每字符基础延迟 |
typing_delay_spread | 40ms | 延迟随机偏差范围 |
typing_pause_chance | 0.1 (10%) | 打字中途"思考暂停"的概率 |
typing_pause_range | (400, 1000)ms | 思考暂停持续时间 |
mistype_chance | 0.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_divisor | 8 | 控制移动步数的除数 |
mouse_min_steps | 25 | 最小移动步数 |
mouse_max_steps | 80 | 最大移动步数 |
mouse_wobble_max | 1.5px | 移动路径抖动幅度 |
mouse_overshoot_chance | 0.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_variance | 0.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_chance | 0.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.py 的 human_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 提供了两条路径:
- 隐身路径(有 CDP 会话时):使用
Input.dispatchKeyEventCDP 命令,产生isTrusted=true事件,没有evaluate堆栈痕迹 - 回退路径(无 CDP 会话时):使用
insertText+page.evaluate,可能被检测到isTrusted=false
隐身路径是 CloakBrowser 区别于简单 JS 注入方案的关键特性之一。
滚动物理模拟
滚动 humanize 实现位于 scroll.py 的 human_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/ 目录。
需要企业代理方案?
我们可根据目标站点、并发规模与稳定性目标提供定制方案。