Solver de captcha con Selenium en Python: ejemplo práctico (2026)
Selenium controla un navegador real, así que la página se renderiza y sus scripts se ejecutan como lo harían para una persona. El único paso que no puede dar es resolver el widget de PopularCaptcha. Esta guía añade ese paso: leer la sitekey de la página renderizada, obtener un token con una sola llamada a la API, escribir el token en el formulario y enviarlo.
Cubre el widget de PopularCaptcha, que Scarnox resuelve hoy sin proxy. Empieza con crédito de prueba gratis, sin tarjeta y prueba el script en un formulario de prueba primero.
Lo que Selenium tiene que hacer
El flujo tiene cuatro partes:
- Cargar la página y esperar a que el widget se renderice.
- Leer el atributo
data-sitekeydel widget. - Obtener un token de la API y ponerlo en el formulario.
- Enviar el formulario antes de que caduque el token.
La tercera parte es la única que toca el captcha. Todo lo demás es trabajo normal de Selenium.
Instala los paquetes
pip install selenium requests
Selenium 4.6 y posteriores encuentran por sí mismos un ChromeDriver compatible, así que no necesitas descargar el driver a mano.
El script completo
import requests
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
API_KEY = "scarnox_YOURAPIKEY"
API_URL = "https://api.scarnox.com/api/tasks/create"
PAGE_URL = "https://example.com/signup"
def solve(sitekey: str, page_url: str, invisible: bool = False) -> str:
body = {
"captcha_type": "PopularCaptchaTaskProxyless",
"site_key": sitekey,
"site_url": page_url,
}
if invisible:
body["is_invisible"] = True
r = requests.post(
API_URL,
headers={"Authorization": f"Bearer {API_KEY}"},
json=body,
timeout=185, # se bloquea hasta que el token está listo
)
data = r.json()
if data.get("errorId") != 0:
raise RuntimeError(f"{data.get('errorCode')}: {data.get('errorDescription')}")
return data["solution"]["token"]
INJECT_TOKEN_JS = """
const token = arguments[0];
let field = document.querySelector('[name="h-captcha-response"]');
if (!field) {
field = document.createElement("textarea");
field.name = "h-captcha-response";
field.style.display = "none";
document.querySelector("form").appendChild(field);
}
field.value = token;
let extra = document.querySelector('[name="g-recaptcha-response"]');
if (!extra) {
extra = document.createElement("textarea");
extra.name = "g-recaptcha-response";
extra.style.display = "none";
document.querySelector("form").appendChild(extra);
}
extra.value = token;
"""
def main() -> None:
driver = webdriver.Chrome()
try:
driver.get(PAGE_URL)
widget = WebDriverWait(driver, 15).until(
EC.presence_of_element_located((By.CSS_SELECTOR, "[data-sitekey]"))
)
sitekey = widget.get_attribute("data-sitekey")
invisible = widget.get_attribute("data-size") == "invisible"
token = solve(sitekey, driver.current_url, invisible=invisible)
# Rellena primero los demás campos y luego inyecta el token justo antes de enviar,
# porque el token caduca en unos dos minutos.
driver.find_element(By.NAME, "email").send_keys("you@example.com")
driver.execute_script(INJECT_TOKEN_JS, token)
driver.find_element(By.CSS_SELECTOR, "form button[type=submit]").click()
WebDriverWait(driver, 15).until(EC.url_changes(PAGE_URL))
print("Submitted. Now at:", driver.current_url)
finally:
driver.quit()
if __name__ == "__main__":
main()
El script no necesita proxy para una resolución de PopularCaptcha sin proxy. Omite el campo de proxy y deja que el sitio vea tu conexión normal.
El orden de las operaciones es la parte que más veces sale mal. Rellena el resto del formulario primero, luego resuelve, después inyecta y por último haz clic. Si resuelves antes de escribir en los demás campos, una página o un formulario lentos pueden hacer que el envío supere la vida útil del token. Mantén lo más corto posible el intervalo entre la resolución y el clic, y revisa la página de resultados en busca de errores antes de dar por hecho que el captcha se superó.
Por qué el token se inyecta con JavaScript
La mayoría de los widgets de PopularCaptcha escriben el token en un textarea oculto llamado h-captcha-response cuando una persona lo resuelve. Como Selenium no ve a ninguna persona, el script escribe ese mismo campo por sí mismo. El fragmento INJECT_TOKEN_JS hace dos cosas:
- Reutiliza el textarea si el widget ya lo creó.
- Lo crea si la página no lo hizo, y lo añade al formulario.
También rellena g-recaptcha-response, porque muchos sitios leen ese nombre por compatibilidad.
Widgets invisibles
Un widget invisible no tiene casilla. Se ejecuta en segundo plano y normalmente se dispara al enviar el formulario. El script comprueba data-size="invisible" y envía is_invisible: true cuando lo detecta. Algunos sitios además asocian una función de callback de JavaScript al widget. Si el formulario se queda donde está tras inyectar el token, busca un atributo data-callback y llama a esa función con el token. Nuestra guía sobre captcha invisible cubre este caso.
Uso en pruebas
Selenium es también la herramienta habitual para las pruebas end-to-end. Si tu propio sitio de staging muestra un captcha, el mismo código permite que una prueba lo supere. Mantén el entorno de pruebas separado de producción, y no ejecutes pruebas contra sitios que no sean tuyos. La configuración de las suites de pruebas está en captcha en pruebas automatizadas.
Problemas comunes
- El widget nunca aparece. Aumenta la espera, o comprueba si el widget está dentro de un iframe. Si lo está, cambia primero al frame con
driver.switch_to.frame(...). - El formulario se envía pero el sitio rechaza el token. Comprueba que enviaste el token a la página donde se mostró el widget.
driver.current_urldebe coincidir con la URL real de la página, incluida cualquier cadena de consulta. - El token caducó. Acerca la llamada a
solveal paso de envío. El token dura unos dos minutos. - Un error de timeout de la API. Usa un timeout de unos 185 segundos, como en el script. La lógica de reintentos se explica en gestión de errores del solver de captcha.
Precios
Pagas por resolución exitosa. Las resoluciones fallidas no se cobran. Consulta las tarifas actuales para ver los precios, y empieza con crédito de prueba gratis, sin tarjeta.