浏览器自动化防封指南:四层防御体系——Clawbrowser 文章封面插图

浏览器自动化防封指南:四层防御体系

Clawbrowser Team浏览器自动化反检测指纹指南

你的自动化脚本在本地跑得好好的,部署上线几个小时后就开始吃 403、弹验证码,或者每个页面都返回一模一样的空壳 HTML。代码没动过,目标站也没改版——变的是浏览器运行环境,而反爬系统会同时扫描这个环境的四个维度。

结论先行: 浏览器自动化被封,是因为反爬系统同时检查四层信号:浏览器指纹、网络信号(IP 和 TLS)、行为模式(鼠标和时序)、会话管理(Cookie 和状态)。只修一层,其他三层照样暴露。正确做法是自底向上:先用一个指纹一致的浏览器搞定引擎层的代理路由,再在代码里处理行为和会话。


自动化为什么会被封

Cloudflare、Akamai、PerimeterX、DataDome 这些反爬系统不会只查某一项指标。它们会同时对你的会话打多维度的分——一个干净的 IP 配上泄漏的 navigator.webdriver 标志,照样被拦;一个完美的指纹从数据中心 IP 发出来,也照样被拦。检测是分层的,防御也必须分层。

四层防御,从最基础到最上层:

层级 检测内容 修复位置
1. 浏览器指纹 指纹信号、自动化标志、引擎特征 浏览器二进制层
2. 网络层 IP 信誉、TLS 指纹、DNS、WebRTC 代理配置
3. 行为层 鼠标轨迹、操作时序、滚动模式 应用代码
4. 会话层 Cookie、登录态、请求频率 应用代码

第 1、2 层是基础设施问题,第 3、4 层是代码问题。大多数开发者从第 3 层开始改(加个 random sleep),而第 1、2 层完全裸奔——所以补丁永远治不了根。

第一层:浏览器指纹

大多数自动化就是栽在这一层,也是大多数开发者压根不会去看的地方。

原生 Chromium 被 Playwright 或 Puppeteer 驱动时,会在 20 多个指纹面上暴露自动化特征:navigator.webdriver 为 true,chrome.runtime 对象缺失,WebGL renderer 字符串暴露无头 GPU 驱动,Canvas readback 产出确定性的哈希值,AudioContext 输出和真实硬件对不上。反爬脚本会在页面加载后 200ms 内把这些信号全部采集完——你的代码还没开始跑。

什么方法不管用

Stealth 插件,比如 puppeteer-extra-plugin-stealth 或 playwright-stealth,在页面层面修补部分信号,用 JavaScript 覆写 navigator.webdriver。问题是:反爬系统读的是浏览器引擎本身的信号,不只是页面上的。用 JavaScript 覆写 navigator.webdriver 是可以被检测出来的——属性描述符和真实浏览器不一样。Canvas 和 WebGL readback 来自 GPU 管线,JavaScript 根本碰不到。页面层面的补丁打不到引擎层。

随机 User-Agent 改的是 20 多个信号中的一个。如果 User-Agent 写着 macOS,但 WebGL renderer 报的是 Linux GPU 驱动、字体列表全是 Windows 独有的、时区偏移和 locale 也对不上——这种自相矛盾比不伪装还糟糕。

无头模式 (--headless) 产生的指纹和有头 Chrome 完全不同。屏幕尺寸、GPU 渲染路径、字体渲染、插件列表全都不一样。有些站直接封杀无头模式。

有效的做法

修复点在浏览器二进制层。反检测浏览器在 Chromium 引擎内部修改指纹信号,确保 20 多个指纹面完全一致:Canvas、WebGL、AudioContext、字体、navigator 属性、client rects、媒体设备、屏幕尺寸、时区、语言、WebRTC——所有信号指向同一个真实设备配置文件。

关键词是一致性。单独伪造某个信号会制造矛盾。受管理的指纹确保每个信号讲的是同一个故事。

完整的 20 多种指纹信号详解,参见浏览器指纹详解:反爬系统检测自动化的 20+ 信号。


第二层:网络层

浏览器指纹再干净,IP 被标记了照样被封。反爬系统独立检查网络层。

IP 信誉

数据中心 IP(AWS、GCP、Azure、DigitalOcean)早就被编入名册。反爬厂商维护着云基础设施 IP 段的数据库,来自这些地址段的请求会被加强审查甚至直接拦截,跟浏览器指纹无关。

真实 ISP 的住宅 IP 封锁率低得多。差距是可以量化的:同一套自动化脚本、同一个浏览器指纹,从数据中心 IP 出去可能有 90% 的封锁率,换成住宅代理可以降到 5% 以下。

TLS 指纹

每个 HTTPS 连接都从 TLS 握手开始。ClientHello 消息中密码套件的排列顺序、扩展和支持的曲线会形成一个 TLS 指纹(JA3/JA4)。原生 Chromium、无头 Chrome 和 Playwright 各自产生不同的 JA3 哈希。TLS 指纹说的是"自动化工具",User-Agent 说的是"Chrome 126 on macOS",这个会话就被标记了。

解决方式:使用能产生真实 Chrome TLS 指纹的浏览器二进制。页面层的代理和 HTTP 库修不了 TLS 指纹——握手在 JavaScript 执行之前就完成了。

地理位置对齐

浏览器指纹报的是美国英语 locale 加美国时区,但 IP 定位到法兰克福的数据中心——反爬系统会立刻标记这种不匹配。代理路由必须做地理对齐:IP 所在地要和指纹的时区、语言、locale 对得上。

WebRTC 泄漏

即使走了代理,WebRTC 也可能暴露你的真实 IP。STUN 请求会绕过 HTTP 代理直连,泄漏源 IP。这个问题必须在浏览器层处理,应用代码管不了。


第三层:行为模式

第 1、2 层在基础设施层面搞定之后,第 3 层才是你的应用代码要发力的地方。

反爬系统会跟踪会话与页面的交互方式。真实用户不会像脚本那样操作:不会在页面加载后 5ms 就点击元素,不会以恒定的 500px/s 速度滚动,不会在 60 秒内访问 200 个页面且中间零空闲。

需要实现的点

操作间的随机延迟。 不是固定的 sleep(2000),而是按操作类型变化的随机区间。

import random
import asyncio

async def human_delay(min_ms=800, max_ms=2500):
    delay = random.uniform(min_ms, max_ms) / 1000

拟真鼠标移动。 点击前先把光标移到元素上,不要瞬移。部分反爬系统会跟踪鼠标事件序列,标记没有前置 mousemove 的点击。

async def move_and_click(page, selector):
    element = await page.query_selector(selector)
    box = await element.bounding_box()
    # Move to element with some randomness in the endpoint
    x = box["x"] + box["width"] * random.uniform(0.3, 0.7)

滚动行为。 分段滚动,不要直接跳到元素位置。真实用户会滚过目标再滚回来。

请求节奏。 如果持续请求同一个域名,拉开请求间隔,别让会话看起来像爬虫。大多数站点 2-5 秒的页面导航间隔是合理的基线。


第四层:会话管理

最后一层是状态。反爬系统会持续跟踪会话,标记真实用户不可能产生的模式。

真实浏览器会在多次访问中积累 Cookie。一个会话带着零 Cookie 到达、全部接受,五分钟后又以零 Cookie 出现——这明显是自动化。使用隔离的浏览器配置文件在多次运行间保持 Cookie 状态。

登录态维护

如果自动化需要登录账号,保持登录态。反复登录、执行一个操作、登出,还换不同的指纹——账号会被标记。

会话隔离

跑多个并发会话时(抓取不同板块、管理多个账号),每个会话需要独立的上下文:独立 Cookie、独立 localStorage、独立指纹。会话之间的交叉污染——A 会话的 Cookie 泄漏到 B 会话——是并发自动化被封的常见原因。

from playwright.async_api import async_playwright

async def isolated_session(profile_name, target_url):
    async with async_playwright() as p:
        # Connect to Clawbrowser with an isolated profile

自限速

监控你对单个目标的请求模式。如果每小时往同一个域名发 1000 个请求,再好的指纹管理也救不了你。给每个域名设合理的上限,接近阈值时轮换会话。具体做法是把请求负载分散到多个账号,每个账号用独立的浏览器配置文件和代理。


组合起来

第 1、2 层是基础设施问题,不应该在应用代码里解决。反检测浏览器通过引擎级的指纹管理和内置代理路由搞定这两层,你的代码只需要处理第 3、4 层。

Clawbrowser 是一个 Chromium 分支,专门处理基础设施层:引擎级修改覆盖 20 多个指纹面、内置代理路由并做地理对齐、WebRTC 泄漏防护、原生 CDP 支持。你的 Playwright 或 Puppeteer 代码连接它,和连接普通 Chrome 一样。

from playwright.async_api import async_playwright
import random, asyncio

async def scrape_with_clawbrowser():
    async with async_playwright() as p:

从 clawbrowser.ai 复制安装 prompt,粘贴到你的 AI agent 中——两分钟内完成配置。

四层是独立的。被封了先定位是哪一层的问题。如果指纹已经确认干净、IP 没被标记,问题在行为代码或会话管理。如果行为已经够拟真但还是被封,问题在浏览器指纹或网络层。自底向上排查。

验证码频繁弹出时的逐步诊断流程,参见自动化总是触发验证码?7 个信号诊断指南。


哪些方法行不通(以及为什么)

方法 为什么无效
--headless 标志 产生的指纹和有头 Chrome 完全不同。很多站直接封杀无头。
Stealth 插件 在页面层面修补信号,引擎层信号依然暴露。每次发布后几个月内就会被识别。
随机 User-Agent 轮换 20 多个信号中只改了 1 个,和其他指纹信号产生矛盾。
只轮换 IP 修了第 2 层,第 1 层完全裸奔。干净 IP 配漏洞百出的浏览器指纹照样被封。
到处加 sleep(5) 修了第 3 层的一个方面,第 1、2 层和第 3 层的其他方面都没管。

共同问题:每种方法只修了一个层面的一个信号。反爬系统同时检查四层,局部修复在生产环境中撑不住。


常见问题

Cloudflare 能检测到 Playwright 吗?

能。原生 Playwright 配标准 Chromium,Cloudflare 的 Turnstile 和 Bot Management 几秒内就能识别。检测主要发生在第 1 层(浏览器指纹):navigator.webdriver、CDP 痕迹、WebGL 渲染差异和 TLS 指纹。详细分析参见为什么你的 Playwright 脚本总被 Cloudflare 拦截。

需要住宅代理吗?

对于反爬较强的站点,需要。数据中心 IP 已经被反爬厂商编目,受到的审查更严。真实 ISP 的住宅代理可以显著降低封锁率。但住宅 IP 配一个指纹泄漏的浏览器照样被封。代理修的是第 2 层,不是第 1 层。

无头模式一定会被检测到吗?

新版无头 Chrome(--headless=new)比老版本更接近有头模式,但在 GPU 渲染、屏幕指标、字体可用性上仍有差异。Cloudflare、Akamai、DataDome 这类高级检测系统依然能区分无头会话。对于防护较强的目标站,有头模式加反检测浏览器才是靠谱的选择。

采集速度能到多快?

没有统一标准,取决于目标站的限速策略和检测灵敏度。合理起点:同一域名的页面导航间隔 2-5 秒,在这个区间内随机。监控封锁率,升高就降速。有些站容忍每秒 1 个请求,有些站单会话超过 10 秒 1 个请求就会标记。

AI Agent 做浏览器自动化也有同样的检测问题吗?

有。Claude Code、Codex、Cursor、Hermes Agent 这类 AI Agent 框架通过 CDP 控制浏览器。反爬系统检查的是浏览器指纹,不关心指令来自人还是 Agent。AI Agent 连接普通 Chrome 实例被封的原因和 Playwright 脚本一模一样。Agent 场景的专项配置参见 CDP 浏览器与 AI Agent:开发者指南。


从底层开始

浏览器自动化被封,是因为反爬系统检查四层而大多数自动化只处理了一两层。自底向上修复:先搞定浏览器指纹,再搞网络层,然后是行为层,最后是会话层。前两层是基础设施问题,应该由浏览器解决,而不是塞进你的代码里。

从 clawbrowser.ai 复制安装 prompt,粘贴到你的 AI agent 中——两分钟内完成配置。

然后把精力放在第 3、4 层的代码上:拟真时序、鼠标移动、会话隔离、频率控制。这才是在生产环境中站得住的方案。

继续阅读

查看全部 →