Playwright & Puppeteer
O Clearcote é só Chromium, então funciona como navegador drop-in para as ferramentas de automação que você já usa.
Pelo SDK (recomendado)
O pacote clearcote (npm, PyPI e NuGet) transforma as opções de identidade em argumentos nomeados e devolve objetos comuns do Playwright. No primeiro uso, ele baixa o navegador e verifica o SHA-256 — o build aberto sem chave de licença, o build licenciado mais recente com uma (veja Instalação) — e aplica os padrões descritos mais abaixo.
# pip install clearcote
from clearcote import launch
browser = launch(fingerprint="seed-123", platform="windows", brand="Chrome")
page = browser.new_page()
page.goto("https://example.com")
browser.close()SDK atual: 0.31.1. Desde a 0.23, o launch() em Python e Node roda sobre um diretório de perfil real e descartável (apagado ao fechar), em vez do modo anônimo, para que a superfície que depende do perfil corresponda à de um Chrome real e widevine: true consiga carregar o módulo de DRM. Ele devolve um handle que se comporta como um navegador: newPage() funciona normalmente, mas newContext() devolve esse mesmo contexto de perfil, e não um contexto isolado. Para ter cookie jars separados, inicie navegadores separados; passe ephemeralProfile: false / ephemeral_profile=False para o antigo Browser em modo anônimo, ou userDataDir / user_data_dir para manter o perfil.
A API assíncrona (clearcote.async_api) aceita as mesmas opções de persona e proxy dentro de um loop asyncio e devolve objetos assíncronos do Playwright; o launch() dela usa o modo anônimo, então use launch_persistent_context() para ter um perfil (e para widevine=True). O SDK .NET cobre LaunchEphemeralProfileAsync (recomendado — o LaunchAsync do .NET usa o modo anônimo e não consegue ajustar uma janela headless), LaunchPersistentContextAsync, ServeAsync, download verificado, licenciamento, Geoip e entrada humana (chamadas explícitas a HumanClickAsync / HumanTypeAsync / HumanSelectOptionAsync, e não uma flag de inicialização). Perfis salvos, profile: "auto", verificações de coerência de renderização, Widevine e os helpers de agente são, por enquanto, exclusivos de Python & Node. Para fluxos completos prontos para copiar e colar, veja Exemplos.
Puppeteer e outros clientes CDP (serve)
O SDK não tem um launcher para o Puppeteer. Em vez disso, o serve() inicia o Clearcote com as configurações de inicialização do SDK (persona, proxy, padrões) e um endpoint CDP no loopback, e qualquer cliente CDP se conecta a ele — Puppeteer, o connectOverCDP do Playwright, browser-use, Crawl4AI, Stagehand. Funciona com o build licenciado, e nada adiciona --enable-automation. O humanize roda do lado do Playwright, então não se aplica a um cliente conectado dessa forma.
import { serve } from "clearcote";
import puppeteer from "puppeteer-core";
const srv = await serve({ fingerprint: "seed-123", platform: "windows" });
const browser = await puppeteer.connect({ browserURL: srv.cdpUrl, defaultViewport: null });
const page = await browser.newPage();
await page.goto("https://example.com");
await browser.disconnect();
await srv.close();No modo headless, o serve() dá ao navegador um display de tamanho real e ajusta a janela à área útil da tela antes que qualquer cliente se conecte (0.31+); passe windowSize / window_size para uma janela menor. No shell, o clearcote serve faz o mesmo como um serviço permanente — e pode dar a cada conexão o próprio navegador, com identidade, proxy, fuso horário e idioma tirados da URL de conexão. Veja Deploy.
Controlando o binário diretamente (build aberto)
Você também pode apontar o seu próprio launcher para o build aberto com executablePath (Node) ou executable_path (Python) e passar as opções de identidade como args. O build licenciado não inicia dessa forma — ele precisa do token de licença que o SDK obtém, então use launch() ou serve(), descritos acima. Remova --enable-automation, como o SDK faz (o Puppeteer e versões mais antigas do Playwright adicionam essa flag, que coloca o Chromium no modo de automação). Nenhum dos padrões do SDK (idioma, política de WebRTC, geometria da janela) se aplica nesse caminho.
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(
executable_path=r"C:\clearcote\chrome.exe",
headless=False,
ignore_default_args=["--enable-automation"],
args=[
"--fingerprint=seed-123",
"--fingerprint-platform=windows",
"--timezone=America/New_York",
],
)
page = browser.new_page()
page.goto("https://abrahamjuliot.github.io/creepjs/")
browser.close()Resolva ou pré-baixe o binário verificado
O SDK resolve o navegador nesta ordem: um executablePath / executable_path explícito, depois CLEARCOTE_BINARY, depois uma version que você pediu (ou CLEARCOTE_BROWSER_VERSION), depois o build licenciado, se encontrar uma chave de licença (a opção licenseKey, CLEARCOTE_LICENSE_KEY ou ~/.clearcote/license.key) e, por fim, o build aberto fixado nesta versão do SDK. Um caminho que você informar é verificado antes da inicialização, em busca de arquivos ausentes ou truncados. Chame download / executable_path quando quiser aquecer o cache sem iniciar o navegador, usar um diretório de cache personalizado ou optar, em tempo de execução, pelo build aberto mais recente no GitHub.
from clearcote import download, launch
chrome = download(cache_dir=r"C:\clearcote-cache", auto_update=True)
browser = launch(executable_path=chrome, fingerprint="seed-123")O modo fixado confere os valores SHA-256 embutidos no SDK. autoUpdate / auto_update é opcional (opt-in) e confere o manifesto de checksums da versão; quando o gpg está disponível, também confere o manifesto assinado contra o fingerprint fixado da chave de assinatura do Clearcote. Isso vale só para o build aberto: com uma chave de licença, o download() baixa o build licenciado atual (escolha um com version ou releaseChannel — veja como escolher um build). No .NET, o DownloadAsync baixa apenas o build aberto; para pré-baixar o licenciado, use ExecutablePathAsync(new LaunchOptions { LicenseKey = … }).
Importe um perfil de Chrome real
Em vez da persona sintética, derivada do seed, você pode fazer o Clearcote reportar os valores capturados de uma máquina real. Capture um perfil num Chrome doador com o coletor em tools/fingerprint-collect (abra o collect.html, clique em Capture e ele baixa um perfil JSON), ou parta do dataset open source de 10 mil registros chrome-fingerprints usando o conversor incluído convert_dataset.py. A captura cobre navigator, geometria da tela, vendor/renderer do WebGL + limites de getParameter, Web Audio, vozes de síntese de fala, fontes, codecs e características CSS @media.
Passe o perfil ao SDK como caminho de arquivo, objeto ou string JSON — ele cuida do empacotamento gzip + base64 para você. Os campos presentes no perfil substituem o que o navegador reportaria; os campos ausentes ficam com os padrões do navegador. O SDK também deriva o Accept-Language do navigator.languages do perfil quando você não define um explicitamente. Os dois builds leem perfis importados; no build licenciado, eles se aplicam por completo a partir do 151 r19.
Não passe um seed de fingerprint junto com um perfil. O seed ativa a camada de farbling, que uma pontuação rigorosa interpreta como adulteração do canvas, e não traz nada que o perfil já não forneça. Use um ou outro. Um perfil substitui apenas os valores reportados — ele nunca muda o que renderiza os seus pixels, então duas contas com dois perfis diferentes na mesma máquina ainda produzem um canvas idêntico. Se você precisa de renderização separada por conta, use um seed por conta e nenhum perfil.
from clearcote import launch
browser = launch(fingerprint_profile="profile.json", disable_gpu_fingerprint=True, fingerprint_noise=False)
page = browser.new_page()
page.goto("https://example.com")
browser.close()Coerente por padrão (sem flags extras)
Seja qual for a forma como você inicia o navegador, o motor mantém as superfícies secundárias de acordo com a persona escolhida — numa persona Windows, com o que um desktop real com Chrome no Windows reporta. A plataforma da persona é, por padrão, o sistema operacional do host; então, num host Linux, essas superfícies seguem uma persona Linux, a menos que você passe platform. Os limites de getParameter do WebGL (WebGL1 + WebGL2) reportam os valores da persona, limitados ao que a GPU desta máquina consegue de fato entregar, e UNMASKED_RENDERER / UNMASKED_VENDOR são constantes durante a sessão (a mesma GPU em todos os sites, acompanhando a persona). Numa persona Windows, navigator.getBattery() reporta um desktop ligado na tomada, navigator.connection, uma conexão residencial, e AudioContext, a taxa de amostragem e a latência correspondentes do WASAPI do Windows. getScreenDetails() reporta um único monitor, e @media (pointer: fine) / (hover: hover) correspondem a um desktop com mouse.
Verificado no 153 r26, em modo headed no Windows (setembro de 2026): o BrowserScan não reporta bot e o CreepJS mostra 0% de stealth. Atrás de um proxy, adicione geoip para que o fuso horário e o WebRTC correspondam à saída.
Eventos de console e de erro de página
O motor não repassa eventos de console nem de erro de página para clientes de automação, então page.on("console") e page.on("pageerror") não recebem nada. Isso é intencional — repassá-los é exatamente o que uma sonda de presença de automação mede. Os handlers window.onerror e unhandledrejection dentro da página disparam normalmente; então, para capturar a saída do console, colete-a na própria página e leia de volta com page.evaluate(). Os SDKs de Python e Node exibem um aviso sobre isso, uma única vez, na inicialização.
Ajuste automático à região do proxy (geoip)
Passe geoip junto com um proxy e o SDK resolve o IP de saída do proxy — consultado no banco de dados offline geoip-all-in-one — e define fuso horário + idioma principal do navigator + Accept-Language + IP do WebRTC coerentes com essa região. Chega de ajustar o fuso horário à mão para cada proxy:
from clearcote import launch
browser = launch(
fingerprint="user-7423",
proxy={"server": "http://host:8080", "username": "u", "password": "p"},
geoip=True, # timezone + language auto-matched to the proxy's region
)Ele também define a geolocalização e a lista completa de navigator.languages da região, funciona com proxies HTTP e SOCKS5 (inclusive com credenciais) e está nos três SDKs (Geoip = true no .NET). A primeira execução baixa o banco de dados (cerca de 50 MB). Se a região não puder ser resolvida, a inicialização para com um GeoipError, em vez de subir silenciosamente com o relógio e o idioma desta máquina; defina timezone e acceptLanguage para iniciar mesmo assim. A consulta tem 20 segundos (CLEARCOTE_GEOIP_TIMEOUT_SECONDS); no .NET, o erro é GeoipException. Sem geoip nem um timezone explícito, o fuso horário segue o idioma — en-US significa Nova York, mesmo atrás de um proxy alemão.
Prefere definir você mesmo? Use acceptLanguage (Node) / accept_language (Python), por exemplo "en-US,en" — ele define o header Accept-Language, o array completo navigator.languages e navigator.language — e Intl / toLocaleString também o seguem.
Entrada humanizada (humanize & showCursor)
Passe humanize e toda a entrada — mover, clicar, arrastar, rolar e digitar — segue um único padrão humanizado, tanto no nível da página (page.click / hover / type / fill / mouse.* / keyboard.type) quanto no nível do locator (locator.click / type / fill / pressSequentially / dragTo / …). Os movimentos seguem uma trajetória bézier cúbica levemente arqueada, construída a partir da última posição do cursor e percorrida como uma soma de submovimentos min-jerk (um movimento balístico primário + um corretivo — a velocidade com vários picos de um alcance real, e não um único sino simétrico), e tudo é disparado como eventos reais e confiáveis (isTrusted === true, e navigator.webdriver continua false). No Pro, cliques por coordenada (mouse.click(x, y)) seguem movimentos gravados de pessoas reais; todo o resto, e todo clique no build aberto e no Grátis com GitHub, usa trajetórias geradas. Adicione showCursor para desenhar um ponto que acompanha o movimento, para você ver o que acontece.
Como os movimentos usam entrada nativa, um botão pressionado com mouse.down() continua pressionado durante o movimento — então down → move → up é um arraste real com o botão pressionado (controles de arrastar até uma posição, como sliders, recebem um arraste de fato pressionado), e locator.dragTo também é humanizado. A digitação é feita tecla por tecla, com intervalos aleatórios entre teclas + pausas entre palavras e, de vez em quando, a correção de um erro de digitação; a rolagem usa inércia ease-out, com uma pausa de leitura ocasional. O fill foca o campo e digita o valor (valores com mais de ~200 caracteres continuam atômicos, para que preenchimentos grandes não se arrastem).
from clearcote import launch
browser = launch(fingerprint="seed-123", humanize=True, show_cursor=True)
page = browser.new_page()
page.goto("https://example.com")
page.click("text=Sign in") # eased curve, then a trusted click
page.fill("#email", "you@example.com") # focus + key-by-key human typing
page.locator("#password").type("s3cr3t") # locators are humanized too
# held-button drag (e.g. a slider): the press stays held across the move
x0, y0, x1 = 100, 300, 400 # the handle's start, and where to release it
page.mouse.move(x0, y0); page.mouse.down()
page.mouse.move(x1, y0); page.mouse.up()
browser.close()Verificação de coerência do backend de renderização (checkRenderCoherence)
Uma persona pode declarar uma GPU, mas, se a página for de fato pintada por um rasterizador de software (SwiftShader / llvmpipe — comum em headless sem GPU), um detector rigoroso consegue perceber. Verifique uma página carregada: a função lê o vendor/renderer do WebGL (sem máscara) que a página realmente vê, sinaliza um fallback para rasterizador de software (um indício fatal de headless — ative a canvas bridge ou rode em modo headed numa GPU real) ou um par vendor/renderer incoerente, e devolve um veredito estruturado. Passe a GPU declarada para conferir também a família renderizada. Disponível em versão síncrona, assíncrona e em Node.
from clearcote import launch, check_render_coherence
browser = launch(fingerprint="seed-123")
page = browser.new_page(); page.goto("about:blank")
verdict = check_render_coherence(page) # {'renderer', 'software_suspected', 'coherent', 'warnings'}
if not verdict["coherent"]:
print(verdict["warnings"]) # e.g. software rasterizer / incoherent GPU family
browser.close()Perfis & persistência
Mantenha uma identidade estável entre execuções reutilizando o mesmo seed de fingerprint e persista cookies/storage com um diretório de dados do usuário (user-data dir):
from clearcote import launch_persistent_context
ctx = launch_persistent_context(r"C:\clearcote\profiles\acme", fingerprint="acme-tenant-7", headless=False)
page = ctx.pages[0] if ctx.pages else ctx.new_page()Os cookies de um diretório de perfil são criptografados com uma chave ligada à máquina que o criou. Para copiar um perfil para outra máquina com os cookies intactos, passe portableProfile: true / portable_profile=True (a chave vai junto com o perfil) ou encryptionKey / encryption_key (a chave é derivada do seu segredo, e nada sensível é gravado em disco). Build licenciado, Python & Node.
O SDK também tem personas salvas: um Profile guarda opções de fingerprint, configurações de proxy, configurações da canvas bridge e outras opções de inicialização como JSON em ~/.clearcote/profiles (para mudar o local, use CLEARCOTE_PROFILE_DIR).
from clearcote import Profile, launch, launch_persistent_context
Profile("acct-1", {
"fingerprint": "acct-1",
"gpu_vendor": "Google Inc. (Intel)",
"gpu_renderer": "ANGLE (Intel, Intel(R) UHD Graphics ... D3D11)",
"canvas_bridge": {"url": "ws://127.0.0.1:8443", "auth": "user:secret"},
}).save()
ctx = launch_persistent_context(r"C:\clearcote\profiles\acct-1", profile="acct-1")
browser = launch(profile="acct-1", headless=False)Os perfis salvos ficam em texto puro e podem conter credenciais, como canvasBridge.auth. Trate os arquivos de perfil como entrada confiável e não faça commit deles nem os compartilhe.Mais opções de inicialização
extensions— uma lista de caminhos de diretórios de extensões descompactadas (gera--load-extension+--disable-extensions-except).disablePrivacySandbox/disable_privacy_sandbox— defina comotruepara desligar as APIs do Privacy Sandbox (Topics, FLEDGE / Protected Audience, Shared Storage, Private Aggregation, Fenced Frames). Desativada por padrão desde a 0.23: a persona padrão se apresenta como Google Chrome, que traz todas elas. Ative-a só quando a persona for um Chromium sem serviços do Google. O WebUSB não é afetado.agentTyping/agent_typing— o ritmo de digitação do agente (human, o padrão /fast/instant). Veja Agente.tlsProfile— mantém o TLS ClientHello coerente com a versão do Chrome que a persona declara, para que a camada de rede acompanhe o UA (e não o TLS nativo do build). O padrão,"match-persona", seguebrandVersion;"native"não mexe nele;"chrome-<major>"fixa uma versão major. Veja Flags de fingerprint.platform: "android"— uma persona mobile no modo best-effort (toque, ponteiro coarse, tela/DPR de celular, WebGL Mali/Adreno, viewport de celular). Num motor de desktop, a renderização da GPU continua sendo de desktop — combine com a canvas bridge para ter coerência de renderização.storageQuota,fingerprintProfile,canvasBridge,webrtcIp,acceptLanguage,disableGpuFingerprint,fingerprintNoise— veja Flags de fingerprint.
Opções mais novas (build licenciado)
Estas exigem o build licenciado (Grátis com GitHub ou Pro). A revisão do motor que cada uma exige aparece entre colchetes; num motor mais antigo, o SDK pula as de 152 r22 com um aviso, e o motor ignora as demais.
allowThirdPartyCookies/allow_third_party_cookies— permite cookies de terceiros, como o Chrome padrão faz. A base sem serviços do Google os bloqueia por padrão, o que quebra frames incorporados de login, pagamento e desafio que dependem deles. [152 r22]transparentProxy/transparent_proxy— esconde o proxy dos headers das requisições e do timing da conexão (requisições HTTP simples não levam header de proxy; conexões via proxy reportam timing como o de uma conexão reutilizada). Exige um proxy. [152 r22]fingerprintVoices: false/fingerprint_voices=False— mantém as vozes de síntese de fala desta máquina, em vez da lista da persona. [152 r22]fingerprint: "off"— inicia sem nenhuma persona, para diagnosticar problemas. [152 r22]socks5Udp/socks5_udp— leva o UDP do WebRTC por um proxysocks5://, para que voz, vídeo e conexões peer funcionem e continuem saindo pelo endereço do proxy. O proxy precisa permitir isso; muitos pools residenciais não permitem. [151 r17]portableProfile/encryptionKey— perfis que você pode copiar entre máquinas (veja acima). [151 r14; Python & Node]personaSchema: 2/persona_schema=2— um modelo de identidade opcional em que a tela e o chip gráfico combinam com o processador e a memória que a persona declara. Desativado por padrão, para que todo seed existente mantenha a sua identidade. AdicionerealGpuHost/real_gpu_hostsó numa máquina com placa de vídeo real. [151 r19; Python & Node]shaderDialect: "hlsl"— veja Dialeto de shader. [151 r15]profile: "auto"— inicia com um fingerprint real capturado, escolhido para esta máquina, em vez de um seed; ajuste a escolha comprofileSelect/profile_select. Python & Node. No Node, funciona com olaunch()padrão, comlaunchPersistentContext()e comserve()a partir do SDK 0.31.1; a 0.31.0 e as anteriores exigiamephemeralProfile: falsee falhavam comserve().
Sem vínculo com um build específico do motor:
version/releaseChannel— escolhe um build; veja como escolher um build.licenseKey/license_key/LicenseKey— a chave de licença, caso ela não esteja emCLEARCOTE_LICENSE_KEYnem em~/.clearcote/license.key.licenseThroughProxy/license_through_proxy(ouCLEARCOTE_LICENSE_THROUGH_PROXY=1) — envia as chamadas de licença pelo proxy da inicialização, em vez de saírem direto desta máquina.ephemeralProfile/userDataDirnolaunch()— veja a nota em Pelo SDK, mais acima.widevine: true— reprodução com DRM (nos dois builds); veja Widevine & DRM.quiet— silencia os avisos de inicialização e a saída de progresso do SDK.
Padrões coerentes (substituíveis)
O SDK aplica alguns padrões corretos do ponto de vista de stealth, para que os indícios óbvios não vazem:
- Inicializações em modo headed não emulam viewport por padrão (
viewport: null/no_viewport=True), para quewindow.innerWidthacompanhe a janela real do sistema operacional — um viewport emulado de 1280×720 dentro de uma janela real é indício de uma janela impossível. Passe umviewportexplícito para substituir esse padrão. - O WebRTC usa
disable_non_proxied_udppor padrão, então nenhum UDP sai por fora do proxy e o endereço da sua própria máquina continua privado. Só o seu próprio--webrtc-ip-handling-policyemargssubstitui esse padrão — e, no build aberto,webrtcIp/geoip, que voltam a deixar o UDP do WebRTC sair pela sua própria conexão (o build licenciado bloqueia o UDP do WebRTC de qualquer forma). Com essa política e semwebrtcIp, a página não recebe nenhum candidato ICE — atrás de um proxy, passegeoip(ouwebrtcIp) para que o WebRTC reporte o endereço do proxy, ousocks5Udppara levar UDP real por um proxy SOCKS5. - Atrás de um proxy, QUIC / HTTP-3 fica desligado, como acontece com um Chrome real atrás de um proxy — então nenhum UDP é tentado por fora dele.
- A plataforma da persona é, por padrão, o sistema operacional do host, e a marca, Google Chrome. Sem
timezonee semgeoip, o fuso horário segue o idioma (en-US→ Nova York). - As credenciais do proxy vão para o navegador, e não para o Playwright, onde o motor oferece suporte: SOCKS5 sempre (o Playwright simplesmente não consegue se autenticar em SOCKS5), HTTP(S) no 151 r19+, para que o cache de páginas continue ativo (Python & Node). Veja abaixo.
- O
humanizefaz uma checagem prévia de actionability antes de cada clique confiável (visível / habilitado / estável + uma checagem de sobreposição comelementFromPoint) e, se preciso, recorre ao clique nativo, para que um clique confiável nunca dispare sob um overlay ou no meio de uma animação.
SOCKS5 com credenciais
O Chromium padrão simplesmente não consegue se autenticar num proxy SOCKS5 — ele não implementa a subnegociação de usuário/senha —, então a solução usual é um relay local que guarda as credenciais. O build licenciado implementa isso no motor (RFC 1929), então não é preciso relay. Passe o usuário e a senha como campos separados ou dentro do endereço (socks5://user:pass@host:port); de um jeito ou de outro, o SDK os entrega ao motor. Credenciais dentro do endereço exigem o SDK 0.31.1 ou mais recente: SDKs mais antigos deixavam o Playwright descartá-las, e o proxy não recebia login nenhum.
from clearcote import launch_persistent_context
ctx = launch_persistent_context(
"./profile",
proxy={"server": "socks5://proxy.example.net:1080", "username": "user", "password": "pass"},
)
page = ctx.pages[0] if ctx.pages else ctx.new_page()
page.goto("https://api.ipify.org?format=json") # confirm the exit IP is the proxy'sExige o build licenciado (Grátis com GitHub ou Pro, motor 151 r14 ou mais recente); o build aberto não consegue se autenticar num proxy SOCKS5, então use com ele um relay local ou um proxy HTTP.
Sempre verifique o endereço de saída antes de confiar numa sessão. Um proxy que falha em modo aberto sem avisar envia o tráfego pelo seu próprio IP, e todas as outras precauções perdem o sentido. Confira uma vez, na inicialização, com um serviço que devolve o seu endereço IP, em vez de presumir.
Dica: derive o seed do id da sua própria conta/tenant, para que cada identidade seja reproduzível — mesmo seed, mesmo fingerprint do navegador, sempre. Veja a lista completa de switches em Flags de fingerprint.