跳到正文

部署——Docker & CDP 端点

把 Clearcote 作为常驻的 CDP 端点运行,再让任意现有框架连接它——Playwright、Puppeteer、browser-use、Crawl4AI、Stagehand——无需改代码。它直接启动二进制文件(不带 --enable-automation),因此 navigator.webdriver 保持为 false:隐蔽性是由启动方式本身决定的。

官方 Docker 镜像

拉取镜像即可开始使用。任何 CDP 客户端都可以通过暴露的端口接入。

bash
docker run -d --rm -p 127.0.0.1:9222:9222 -e CC_FINGERPRINT=user-7423 teamflatearth/clearcote   # CDP on http://localhost:9222

这样运行时 Chrome 不带沙箱,sdk-0.41.0 之前的所有镜像都是如此。加上镜像自带的 seccomp 配置文件,Chrome 就会带着沙箱运行;参见下文的Chrome 的沙箱。

python
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp("http://localhost:9222")   # your code, unchanged
    page = browser.contexts[0].new_page()                            # the container's own profile
    page.goto("https://example.com")
    print(page.title())

镜像内置了经过 SHA-256 校验的 Linux 版开源版构建、与 Windows 字体度量兼容的替代字体(让拉丁文字的测量结果与身份画像的字体一致),以及从 sdk-0.41.0 起,Windows 机器开箱即可绘制的其他书写系统所需的字体(参见字体),并默认使用一致的原生 Linux 身份画像(persona)。浏览器在虚拟显示器上以有头模式运行(CC_HEADLESS=1 为纯无头模式)。容器中没有 GPU 时,WebGL 以软件方式渲染,所用后端的限制值与身份画像所声明的 GPU 相符:Linux 身份画像(无论有头还是无头)使用 Mesa,Windows 身份画像使用 SwiftShader。所有配置都通过环境变量完成:

bash
docker run -d -p 127.0.0.1:9222:9222 -e CC_PLATFORM=linux -e CC_FINGERPRINT=user-7423 -e CC_ACCEPT_LANGUAGE=en-US -e CC_TIMEZONE=America/New_York teamflatearth/clearcote
变量含义
CC_FINGERPRINT种子(seed)→ 稳定的身份。不设置时,每个容器都会获得自己的随机种子,该种子在启动时打印出来,并保存在容器的 profile 中,重启后依然沿用(sdk-0.39.0 之前的镜像会让所有容器使用同一个种子)。设置它即可指定或复用某个身份。
CC_PLATFORMlinux(默认)| windows | macos | android。Windows 身份画像还会开启 Widevine,并在授权版构建(151 r15+)上启用 HLSL 着色器方言(可用 CC_WIDEVINE / CC_SHADER_DIALECT 覆盖)。
CC_BRAND, CC_BRAND_VERSIONChrome(默认)| Edge | Opera | Vivaldi,以及它所声称的版本。
CC_ACCEPT_LANGUAGE, CC_TIMEZONE语言列表和 IANA 时区。
CC_TLS_PROFILE保持不设置即可:TLS 会跟随身份画像声称的 Chrome 版本。只有在同时设置了匹配的 CC_BRAND_VERSION 时,才固定为 chrome-<major>。
CC_HARDWARE_CONCURRENCY, CC_GPU_VENDOR, CC_GPU_RENDERER, CC_STORAGE_QUOTA单项身份画像值。
CC_HEADLESS, CC_SCREEN1 表示纯无头模式;虚拟屏幕尺寸(默认 1920x1080x24)。
CC_EXTRA_ARGS额外的浏览器开关,以空格分隔。在这里传入 --disable-runtime-suppression 就相当于 SDK 的 stock_runtime(仅在带有该开关的引擎上保留);--no-sandbox 会关闭 Chrome 的沙箱。
CLEARCOTE_LICENSE_KEY, CC_VERSION运行授权版构建——见下文。
CC_IDLE_EXIT_SECONDS连续这么多秒都没有 CDP 客户端连接时停止运行,从浏览器首次响应时开始计时(默认:不会停止)。与 --rm 搭配使用,被弃置的容器也会随之消失。自 sdk-0.40.0 起的镜像可用。
CC_SECRETS_FILE容器内的一个 JSON 文件,存放 CLEARCOTE_LICENSE_KEY 和/或 CC_PROXY,启动时读取一次后即被删除——参见避免机密信息出现在 docker inspect 中。自 sdk-0.40.0 起的镜像可用。
CLEARCOTE_FONT_DIRS你自己的字体,挂载到容器中(例如一台 Windows 机器上 Fonts 文件夹的副本)——参见字体。自 sdk-0.41.0 起的镜像可用。
CLEARCOTE_FALLBACK_FONT_DIRS只用于其他字体都不覆盖的字符的字体。镜像会把它设置为自带的各书写系统字体(/usr/local/share/clearcote/fonts);除非你要替换这些字体,否则保持不变即可。
CLEARCOTE_PERSONA_ENV0 会把身份画像保留在浏览器的命令行上。默认情况下,在支持这种方式的引擎上(r32 起的授权版构建),身份画像通过环境变量传给容器内的浏览器,因此不会出现在宿主机的进程列表中;serve-state 行会报告 persona_env。自 sdk-0.41.0 起的镜像可用。
让身份画像声明容器实际运行的操作系统。页面能读取到的部分信息来自浏览器之下的宿主操作系统,任何身份画像设置都触及不到那一层。在这个 Linux 镜像中,无论 CC_PLATFORM 设为什么,文字尺寸都由 Linux 的 FreeType 缩放器计算,字体都来自 fontconfig。在此镜像上以 CC_PLATFORM=windows 实测:字号每次调整 0.01 px,只有 66% 的调整会改变文本宽度(真实的 Windows Chrome 为 99%),而且 Segoe UI 和 Georgia 在测量上显示为已安装,却无法按名称加载。指纹检测会把这两点都标记出来。默认的 Linux 身份画像则能通过。在 r32 起的授权版构建上,用 CLEARCOTE_FONT_DIRS 挂载真正的 Windows 字体可以修正字体名称的问题(字体),但修正不了缩放器,任何设置都触及不到它。如需 Windows 身份,请运行 Windows 构建,或使用运行在 Windows 机器上的托管浏览器。在 Linux 服务器上使用 serve() 和 launch() 也是同样的道理。详情参见:用户代理之下的字体栈。

在 Docker 中运行授权版构建——传入密钥,挂载缓存

镜像内置的是开源版构建。设置 CLEARCOTE_LICENSE_KEY 后,容器会改为获取授权版构建——默认是最新的那个;如果是 Pro 套餐,也可以用 CC_VERSION 指定某个构建。免费密钥始终运行最新构建,指定版本会被拒绝。

bash
docker run -d -p 127.0.0.1:9222:9222 -v clearcote-cache:/opt/xdg-cache -e CLEARCOTE_LICENSE_KEY=cc_lic_... teamflatearth/clearcote

# Pro only: pin a major, an exact build, or a revision
#   -e CC_VERSION=154        -e CC_VERSION=154.0.8037.57        -e CC_VERSION=r36

务必挂载缓存卷。授权版容器会在首次启动时下载引擎;如果没有持久化的卷,每个容器都会重新下载一遍。挂载卷之后,后续的容器会直接从缓存的构建启动。

启动日志会显示你拿到的是哪个引擎,所以密钥配置有误时能立刻发现,而不是等到第一个请求被拦截才察觉:

text
[clearcote] engine: /opt/xdg-cache/clearcote/pro-154.0.8037.57-r36/browser/chrome (licensed)
[clearcote] licence lease acquired
镜像需要基于 SDK 0.26.1 或更新版本构建;使用来自 GitHub 的免费密钥时,则需要 0.30.0 或更新版本——授权版浏览器要求免费容器在运行期间持续续期许可证,并会拒绝较旧的镜像。较旧的已发布镜像会忽略密钥,悄悄提供开源版构建——运行 docker pull teamflatearth/clearcote 即可更新。授权版运行还会在启动时申领一个并发租约,因此容器需要能出站访问许可证 API;如果租约申领失败,容器会带着失败原因退出,而不是启动一个无法运行的引擎。一个免费密钥在你所有的容器中同一时间只能运行一个浏览器——参见授权版浏览器的计数方式。

从 sdk-0.40.0 镜像起,多个授权版容器在同一个缓存卷上同时启动时,引擎只会下载一次:第一个容器负责获取,其余容器等待它完成。

避免机密信息出现在 docker inspect 中

每个 -e 变量都是容器配置的一部分,因此任何能对容器运行 docker inspect 的人(或能读取 docker compose config 以及大多数容器管理面板的人)都能以明文看到 CLEARCOTE_LICENSE_KEY 和 CC_PROXY URL 中的密码。从 sdk-0.40.0 镜像起,你可以改为通过文件传入它们:容器内的一个 JSON 文件,由 CC_SECRETS_FILE 指定,容器在启动时读取一次后将其删除。该文件必须属于镜像的用户(uid 10001)。

bash
printf '{"CLEARCOTE_LICENSE_KEY": "%s"}' "$CLEARCOTE_LICENSE_KEY" > clearcote-secrets.json
tar --owner=10001 --group=10001 --mode=600 -cf clearcote-secrets.tar clearcote-secrets.json
id=$(docker create -p 127.0.0.1:9222:9222 -e CC_SECRETS_FILE=/tmp/clearcote-secrets.json   -v clearcote-cache:/opt/xdg-cache teamflatearth/clearcote)
docker cp - "$id:/tmp" < clearcote-secrets.tar    # copy the file in before the container starts
docker start "$id"
rm clearcote-secrets.json clearcote-secrets.tar

各 SDK 的 macOS 启动方式就是这样做的。能访问 Docker 守护进程的人仍然可以读取运行中容器的文件和内存,因此请把守护进程的访问权限等同于密钥的访问权限来对待。

自 sdk-0.40.0 起,镜像以 tini 作为 init 进程运行,因此在容器内退出的浏览器辅助进程(例如来自你通过 docker exec 运行、会自行启动浏览器的脚本)会被及时清理,而不会越积越多。docker stop 仍会传达到浏览器,并释放许可证席位。

字体

浏览器只能看到 SDK 指给它的字体:先是该版本自带的 Windows 替代字体,然后是下面这些目录。/usr/share/fonts 下的其他字体都不会到达浏览器,因此镜像中安装的字体不可能改变 Windows 身份画像报告的字宽。自 sdk-0.41.0 镜像起:

  • 内置各书写系统字体。彩色 emoji、中文、日文和韩文(WenQuanYi Zen Hei、IPA Gothic),以及阿拉伯文、亚美尼亚文、孟加拉文、切罗基文、天城文、埃塞俄比亚文、格鲁吉亚文、古吉拉特文、古木基文、卡纳达文、高棉文、老挝文、马拉雅拉姆文、缅甸文、僧伽罗文、泰米尔文、泰卢固文、泰文、藏文等文字的 Noto 字体,位于 /usr/local/share/clearcote/fonts,由 CLEARCOTE_FALLBACK_FONT_DIRS 列在自带字体之后。它们只用于自带字体无法绘制的字符,让这些字符正常渲染,而不是显示为空白方框;开源版构建和授权版构建都是如此。不会添加任何拉丁字体族。
  • 你自己的字体。挂载一个字体目录(通常是一台 Windows 机器上 C:\Windows\Fonts 的副本),并在 CLEARCOTE_FONT_DIRS 中指定它。它排在自带字体之前。在 r32 起的授权版构建上,它提供的每个字体族都会以它本身、以它自己的名称渲染,Helvetica、Times、Courier 和 CSS 通用字体族也随之对应;较旧的引擎仍使用替代字体,只把该目录用于其他字体都不覆盖的字符。只使用你已获得授权的字体。在 Docker 之外,对应的设置是 SDK 的 fontDirs。
bash
docker run -d -p 127.0.0.1:9222:9222 -v /srv/windows-fonts:/fonts:ro -e CLEARCOTE_FONT_DIRS=/fonts \
  -e CC_PLATFORM=windows -v clearcote-cache:/opt/xdg-cache -e CLEARCOTE_LICENSE_KEY=cc_lic_... teamflatearth/clearcote
docker exec <container> clearcote info --quick   # which writing systems render, which families are genuine
安全提示:CDP 端点意味着对浏览器的完全控制。只应将其发布到受信任的网络——-p 127.0.0.1:9222:9222 可将其限制在本机。docker/ Dockerfile 可供审计——你可以自己重新构建并验证。

Chrome 的沙箱

Chrome 的 Linux 沙箱会让每个渲染器(负责解析和运行页面的进程)在各自独立的命名空间中、在 seccomp 过滤器之下运行,因此即使某个页面攻破了它的渲染器,也仍被限制在该渲染器之内。使用 --no-sandbox 时,该渲染器拥有容器用户拥有的一切:它的文件、它的网络以及它的其他进程。Docker 默认的 seccomp 配置文件会拒绝创建这些命名空间的系统调用,因此 sdk-0.41.0 之前的镜像总是以 --no-sandbox 运行 Chrome。

自 sdk-0.41.0 镜像起,容器会先做检查:它发起与 Chrome 沙箱相同的系统调用,如果这些调用可行,就带着沙箱运行 Chrome。请使用镜像自带的 seccomp 配置文件启动容器,它就是 Docker 的默认配置文件,只是加上了 Chrome 所需的两个命名空间调用:

bash
# copy the profile out of the image (--tmpfs keeps docker from first copying the engine into a volume)
docker run --rm --tmpfs /opt/xdg-cache --entrypoint cat teamflatearth/clearcote /etc/clearcote/seccomp.json > clearcote-seccomp.json
docker run -d -p 127.0.0.1:9222:9222 --security-opt seccomp=clearcote-seccomp.json teamflatearth/clearcote
yaml
# docker compose
services:
  clearcote:
    image: teamflatearth/clearcote
    ports: ["127.0.0.1:9222:9222"]
    security_opt: ["seccomp=./clearcote-seccomp.json"]
  • 日志会说明结果:[clearcote] sandbox: on (…),或者 [clearcote] sandbox: OFF, Chrome runs with --no-sandbox: …,并附上原因以及开启方法。serve-state 行带有 "sandbox": true 或 false。不使用该配置文件时,容器的运行方式与之前完全相同。
  • 它还需要:使用镜像自己的用户(而不是 --user 0)、CAP_SYS_CHROOT(默认授予;如果用了 --cap-drop ALL,请再用 --cap-add SYS_CHROOT 加回来)、允许非特权用户命名空间的宿主机,以及一台 amd64 机器。缺少其中任何一项时,容器会回退到 --no-sandbox 并说明原因。
  • 代价:使用该配置文件后,容器中的任何进程(不只是 Chrome)都可以创建带有自己网络命名空间的用户命名空间,从而触及 Docker 默认配置文件原本挡在外面的内核网络代码。运行不受信任页面的 Chrome 渲染器仍处于 Chrome 自己的过滤器之下。没有沙箱时,被攻破的渲染器拥有容器用户拥有的一切,因此使用该配置文件仍是更好的选择。不要改用 --privileged、--cap-add SYS_ADMIN 或 seccomp=unconfined:它们关闭的容器保护要多得多。
  • CC_EXTRA_ARGS=--no-sandbox 用于有意关闭沙箱。--no-zygote、--disable-namespace-sandbox、--renderer-cmd-prefix= 和 canvas bridge(--canvas-bridge-url=)也会关闭沙箱,因为在这里使用它们时 Chrome 无法运行沙箱。
  • 各 SDK 的 macOS 启动方式会自行传入该配置文件,前提是 Docker 运行在 x86_64 CPU 上。在 Apple silicon 上镜像以模拟方式运行,目前沙箱在那里保持关闭。

通过 SDK 提供常驻 CDP 端点——serve()

自己启动二进制文件,并获得一个任何客户端都能接入的 cdp_url。与镜像相同的隐蔽直接启动方式,只是由你自己的进程来驱动。

python
from clearcote import serve

srv = serve(fingerprint="seed-123", platform="windows")   # -> srv.cdp_url
# attach ANY CDP client:
#   playwright:  p.chromium.connect_over_cdp(srv.cdp_url)
#   puppeteer:   puppeteer.connect({ browserURL: srv.cdp_url, defaultViewport: null })
#   browser-use / Crawl4AI / Stagehand: point them at srv.cdp_url
srv.close()
javascript
import { serve } from "clearcote";
const srv = await serve({ fingerprint: "seed-123", platform: "windows" });
// srv.cdpUrl -> connectOverCDP / puppeteer.connect({ browserURL, defaultViewport: null })
await srv.close();

在无头模式下,被服务的浏览器会获得一个真实尺寸的显示器——采用身份画像的尺寸,或从真实桌面数据中抽取的尺寸——并且在任何客户端接入之前,窗口就已适配到工作区,因此每个页面、标签页和弹出窗口上报的窗口都与其屏幕相符(SDK 0.31+)。如需更小的窗口,请传入 windowSize: { width, height }(Python 中为 window_size,.NET 中为 WindowSize);也可以在 args 中传入你自己的 --window-size,由你完全接管。

一个端点,多个身份——clearcote serve

在命令行中(Python 和 Node 包,0.29+),clearcote serve 会运行一个常驻端点,为连接所请求的每个身份分别启动一个独立的浏览器——其种子、代理、时区和语言都取自连接 URL。相同的种子会复用已经在运行的浏览器。

bash
clearcote serve --port 9222 --idle-timeout 300 --max-browsers 16

# then, from any Playwright client:
#   chromium.connect_over_cdp("http://127.0.0.1:9222?fingerprint=acct-1&platform=windows")
#   chromium.connect_over_cdp("http://127.0.0.1:9222?fingerprint=acct-2&proxy=socks5://user:pass@host:1080&geoip=true")
#   (a password-protected proxy needs the licensed build)

它默认绑定到 localhost,并会拒绝网页可能发起的请求。空闲的身份会在 --idle-timeout 秒后关闭,--max-browsers 限制同时运行的数量(超出的身份会收到 HTTP 429),--data-dir 会在重启之间保留每个身份的 profile,--allow-host / --allow-origin 则让它可以运行在反向代理之后。打开 http://127.0.0.1:9222/ 可以查看当前在运行什么。使用 GitHub 免费版密钥时,同一时间只能运行一个浏览器。旧版的单身份脚本 clearcote-serve --port 9222 --fingerprint seed-123 仍然保留在 Python 包中。

直接启动——开源版构建,不用 SDK

Releases 页面上的开源版构建是一个普通的 Chromium 二进制文件,你可以通过 executable_path 和启动开关自行启动。授权版构建则需要 SDK,因为许可证令牌由 SDK 持有。下面的额外参数是 SDK 在 Windows 上为这个种子添加的主要默认值(它还会合并功能开关,并在使用代理时关闭 QUIC):

python
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(
        executable_path=r"C:\clearcote\chrome.exe",
        ignore_default_args=["--enable-automation", "--enable-unsafe-swiftshader"],
        args=[
            "--fingerprint=seed-123",
            "--fingerprint-platform=windows",
            "--fingerprint-brand=chrome",
            "--accept-lang=en-US,en",
            "--lang=en-US",
            "--timezone=America/New_York",
            "--webrtc-ip-handling-policy=disable_non_proxied_udp",
            "--ignore-gpu-blocklist",
        ],
    )
    browser.new_page().goto("https://example.com")

构建你自己的镜像(由 SDK 驱动)

Clearcote 提供 Linux x64 二进制文件,因此可以在容器中以无头模式运行。镜像需要浏览器运行时库、fontconfig 和 SDK。浏览器使用其版本自带的字体包,而不是系统字体,因此你用 apt 安装的字体在被列出之前对它都是不可见的:可以像官方镜像那样,把各书写系统字体链接到同一个目录,并在 CLEARCOTE_FALLBACK_FONT_DIRS 中指定它(SDK 0.41.0+);也可以把你自己的字体列在 CLEARCOTE_FONT_DIRS 中(参见字体)。切勿列出整个 /usr/share/fonts:其中的拉丁字体族会改变 Windows 身份画像的字宽。在 Linux 上,身份画像默认是一致的原生 Linux 身份。WebRTC 防泄漏默认开启;Privacy Sandbox API 保持开启,与 Google Chrome 一致。安装 libegl1 和 libgl1-mesa-dri 后,SDK 会通过基于 EGL 的 Mesa 渲染 Linux 身份画像的 WebGL,其限制值和渲染器形式与真实 Linux 机器报告的一致:无头模式自 SDK 0.39.0 起如此,有头模式也自 0.42.0 起如此,因此两种模式呈现的是同一个 WebGL 表面。缺少这两个库时则回退到 SwiftShader。在没有窗口管理器的 Xvfb 下以有头模式启动时,窗口会在第一个页面打开之前适配到屏幕(SDK 0.42.0)。

dockerfile
FROM node:22-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
      xz-utils libnss3 libnspr4 libgbm1 libasound2 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 \
      libxkbcommon0 libxcomposite1 libxdamage1 libxrandr2 libxfixes3 libxext6 libxrender1 \
      libpango-1.0-0 libcairo2 libx11-6 libxcb1 libexpat1 libdbus-1-3 ca-certificates libegl1 libgl1-mesa-dri \
      fontconfig fonts-noto-color-emoji fonts-ipafont-gothic fonts-wqy-zenhei \
 && F=/usr/local/share/clearcote/fonts && mkdir -p $F && cd /usr/share/fonts \
 && cp -l truetype/noto/NotoColorEmoji.ttf truetype/wqy/wqy-zenhei.ttc opentype/ipafont-gothic/ipag.ttf $F/ \
 && rm -rf /var/lib/apt/lists/*
# emoji + CJK, used only where the bundle has no glyph
ENV CLEARCOTE_FALLBACK_FONT_DIRS=/usr/local/share/clearcote/fonts
WORKDIR /app
RUN npm i clearcote
RUN node --input-type=module -e "import { download } from 'clearcote'; await download();"   # bake the binary in
COPY run.mjs .
CMD ["node", "run.mjs"]
run.mjs
import { launchPersistentContext } from "clearcote";
const ctx = await launchPersistentContext("/tmp/prof", {
  headless: true,
  fingerprint: "user-1",
  proxy: { server: "http://gateway:8080", username: "u", password: "p" },
  geoip: true,      // timezone + languages + WebRTC IP matched to the proxy exit
  humanize: true,   // trusted bezier input; navigator.webdriver stays false
  args: ["--no-sandbox"],
});
const page = ctx.pages()[0] ?? (await ctx.newPage());
await page.goto("https://example.com");
await ctx.close();
运行容器时加上 --shm-size=1g,避免在重型页面上出现 /dev/shm 相关的崩溃。Python 的用法完全相同(from clearcote import launch_persistent_context,选项使用 snake_case 命名)。

哪些客户端可以通过 CDP 接入

客户端接入方式
Playwrightchromium.connect_over_cdp(url) / connectOverCDP(url)
Puppeteerpuppeteer.connect({ browserURL: url, defaultViewport: null })
browser-use · Crawl4AI · Stagehand把它们的 CDP/端点设置指向该 URL
任何支持 CDP 的工具直接使用该端口上的原始 DevTools Protocol

想让 AI 智能体来驱动它?参见 MCP 服务器。好奇为什么直接启动更隐蔽?参见检测的工作原理。