
invisible_playwright v0.20.0
Playwright que los anti-bots no pueden ver, así que sin captchas: misma API, Firefox headless sigiloso anti-detección, huella indetectable, elude la detección de bots, web scraping y automatización de navegador en Python.
Playwright es detectado por anti-bots y captchas.
Este se ejecuta sobre un Firefox anti-detect con una huella indetectable, compatible con tu código Playwright existente.

Cómo funciona
Los anti-bots hacen dos preguntas, y reCAPTCHA, hCaptcha y Cloudflare Turnstile puntúan las respuestas. invisible_playwright responde sí a ambas.
1. ¿Es un navegador real? Sí. Es Firefox, parcheado a nivel del código fuente en C++.
- La huella del navegador se establece dentro del motor, no se inyecta en la página: navigator, screen, GPU/WebGL, canvas, fuentes, audio, WebRTC, zona horaria, red. Headless o headed, los mismos valores en ambos casos.
- Sin shim de JS, sin override, sin costura que leer.
2. ¿Lo está usando una persona real? Sí. Las acciones están humanizadas en el driver.
- Cada clic, hover y arrastre sigue una trayectoria natural del ratón con tiempos humanos, sin cursor que se teletransporta.
- Cada entrada es idéntica byte a byte a la de un ratón real: fuente de entrada real, presión, eventos trusted.
Instalación
pip install invisible-playwright
python -m invisible_playwright fetch # descarga única, verificada por sha256: 240 MB (Windows) / 262 MB (Linux x86_64) / 253 MB (Linux arm64), unos 550 MB descomprimido
Requiere Python 3.11 o superior.
Plataformas soportadas: Windows x86_64, Linux x86_64 / arm64.
Uso
Huella aleatoria por sesión
La API de Playwright, sync y async, sin cambios en el código. Los mismos objetos, las mismas llamadas, los mismos valores de retorno. Algunas superficies quedan fuera de alcance - tracing, HAR, CDP, el contexto de peticiones de la API - y cada una rechaza con una frase que explica por qué en lugar de comportarse mal en silencio. Si ya usas Playwright, cambiar son dos líneas:
- from playwright.sync_api import sync_playwright
- with sync_playwright() as p:
- browser = p.firefox.launch()
+ from invisible_playwright import InvisiblePlaywright
+ with InvisiblePlaywright() as browser:
Cada sesión obtiene una huella distinta (GPU, audio, fuentes, pantalla, ~200 campos) y movimiento del ratón con curvas de Bezier.
Sync
from invisible_playwright import InvisiblePlaywright
with InvisiblePlaywright(proxy={"server": "socks5://...", "username": "u", "password": "p"}) as browser:
page = browser.new_page()
page.goto("https://example.com")
page.click("#submit") # el ratón traza un arco hacia el botón en una curva de Bezier
Async
from invisible_playwright.async_api import InvisiblePlaywright
async with InvisiblePlaywright(proxy={"server": "socks5://...", "username": "u", "password": "p"}) as browser:
page = await browser.new_page()
await page.goto("https://example.com")
await page.click("#submit")
El objeto browser es un playwright.sync_api.Browser / playwright.async_api.Browser - todos los métodos de Playwright funcionan tal cual.
Registra la seed para reproducir una ejecución:
sf = InvisiblePlaywright()
with sf as browser:
print("seed =", sf.seed)
# ...
Huella reproducible
with InvisiblePlaywright(seed=42) as browser:
... # misma GPU, mismo hash de canvas, mismo contexto de audio, en cada ejecución
Proxies
proxy = {
"server": "socks5://gate.example.com:1080",
"username": "user",
"password": "pass",
}
with InvisiblePlaywright(proxy=proxy) as browser:
...
Esquemas soportados: socks5, socks4, http, https. El DNS se enruta a través del proxy por defecto, sin fuga local.
Zona horaria
La zona horaria del navegador sigue a timezone=:
# por defecto: la zona horaria se deriva automáticamente de la IP de salida (la salida
# del proxy si hay un proxy configurado, si no la propia IP pública del host)
with InvisiblePlaywright(proxy=proxy) as browser:
...
# una zona IANA explícita siempre gana, la única forma de forzar una zona concreta
with InvisiblePlaywright(proxy=proxy, timezone="America/New_York") as browser:
...
Fijar campos específicos de la huella
Por defecto todo proviene de seed. Para forzar valores concretos mientras el resto sigue derivándose de la seed:
with InvisiblePlaywright(
seed=42,
pin={
"gpu.renderer": "ANGLE (AMD, Radeon R9 200 Series Direct3D11 vs_5_0 ps_5_0, D3D11)",
"gpu.vendor": "Google Inc. (AMD)",
"screen.width": 2560,
"screen.height": 1440,
"hardware.concurrency": 16,
},
) as browser:
...
Un pin de GPU elige una de las personas validadas en lugar de establecer una cadena libre,
porque los ~81 valores de getParameter y la lista de extensiones viajan con el
nombre del renderer y un detector los contrasta entre sí. Un nombre que el pool no
contiene lanza un error, y el error lista los que sí contiene.
La lista completa de claves fijables, cómo interactúa el pinning con el muestreador bayesiano y los patrones comunes están en docs/pinning.md.
CLI
El comando instalado es invisible-playwright, con guion. python -m invisible_playwright funciona de forma idéntica y no necesita nada en el PATH.
invisible-playwright fetch # descarga el motor si falta, comprueba cada copia
# en caché contra el sello, imprime la ruta
invisible-playwright version # versiones del wrapper, el core y el motor, y dónde
# está cacheado el motor
Documentación, guías y comparativas
Si prefieres promptear en lugar de programar. Esta página es el motor, como
librería de Python. Dos formas de usarlo sin escribir código, ambas en una línea: desde un cliente
MCP que ya tengas (Claude Code, Claude Desktop, Cursor),
claude mcp add stealth -- uvx aihawk, consulta
la página del servidor MCP; o con una
interfaz y un modelo incluidos, necesitando solo una clave de OpenRouter,
uvx aihawk ui --openrouter-key sk-or-..., consulta
AIHawk. El mismo motor por debajo, y el segundo
es un cliente del primero.
Todo se lee mejor, y es buscable, en la wiki, organizada en cuatro secciones en lugar de una lista plana: