No necesitas un teléfono físico para resolver el CAPTCHA que aparece en la versión móvil de un sitio. Con la emulación de dispositivos de Playwright o de Selenium y la API de CaptchaAI, envías el reCAPTCHA v2 a resolver y recibes el token desde el mismo script que controla el navegador móvil. En esta guía cubres dos rutas equivalentes: Playwright con Python y Selenium con Chrome DevTools en Node.js.
Piensa en un caso concreto: validas el flujo de registro de una tienda optimizada para móvil —un marketplace regional tipo MercadoLibre o una página de producto en Amazon.es— que solo muestra reCAPTCHA v2 cuando detecta un User-Agent móvil. Reproduces ese contexto, resuelves el CAPTCHA y envías el formulario desde el teléfono simulado. Es QA de tu propio flujo: respeta siempre los términos de servicio del sitio y la normativa de protección de datos aplicable.
Requisitos previos
- Una cuenta de CaptchaAI y tu clave API (el valor
YOUR_API_KEYde los ejemplos). - Python 3.10+ con
httpxyplaywright(playwright install chromium) para la primera ruta. - Node.js 18+ con
selenium-webdriveryaxiospara la segunda ruta. - Chrome instalado, ya que ambas rutas lo controlan en modo emulado.
Diferencias entre móvil y escritorio que afectan al CAPTCHA
La automatización móvil enfrenta los mismos desafíos que el escritorio, pero cambia el contexto del navegador. Estas son las variables que más influyen en cómo se presenta el desafío:
| Factor | Escritorio | Móvil | Impacto en el CAPTCHA |
|---|---|---|---|
| User-Agent | Chrome/Firefox de escritorio | Safari/Chrome móvil | Algunos sitios sirven configuraciones de CAPTCHA distintas según el dispositivo |
| Ventana gráfica | 1920×1080+ | 390×844 | El widget del CAPTCHA puede renderizarse de otra forma |
| Interacción | Eventos de ratón | Eventos táctiles | Algunos CAPTCHA validan el tipo de interacción |
| Red | Banda ancha | 4G/5G | Puede que necesites tiempos de espera más largos |
El patrón común: detectar, resolver e inyectar
Aunque cambian la herramienta y el lenguaje, las dos rutas siguen el mismo flujo:
- Abre la página en un contexto de navegador con señales móviles.
- Localiza el elemento
.g-recaptchay extrae su atributodata-sitekey. - Envía el sitekey y la URL a CaptchaAI con el método
userrecaptchay sondea el resultado. - Inyecta el token recibido en el textarea
g-recaptcha-responsey dispara el callback si existe. - Envía el formulario y comprueba que el servidor lo acepta.
Método 1: emulación móvil con Playwright (Python)
La emulación de dispositivos de Playwright es la vía más directa: no hace falta ningún dispositivo real. El perfil iPhone 13 fija de una vez la ventana gráfica, el User-Agent y el pixelRatio.
# playwright_mobile_captcha.py
import asyncio
import httpx
from playwright.async_api import async_playwright
API_KEY = "YOUR_API_KEY"
async def solve_recaptcha(sitekey: str, pageurl: str) -> str:
"""Submit reCAPTCHA v2 to CaptchaAI and poll for result."""
async with httpx.AsyncClient(timeout=180) as client:
# Submit task
resp = await client.get(
"https://ocr.captchaai.com/in.php",
params={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"json": "1",
},
)
result = resp.json()
if result["status"] != 1:
raise Exception(f"Submit failed: {result['request']}")
task_id = result["request"]
# Poll for result
for _ in range(30):
await asyncio.sleep(5)
poll = await client.get(
"https://ocr.captchaai.com/res.php",
params={
"key": API_KEY,
"action": "get",
"id": task_id,
"json": "1",
},
)
poll_result = poll.json()
if poll_result["status"] == 1:
return poll_result["request"]
if poll_result["request"] != "CAPCHA_NOT_READY":
raise Exception(f"Solve failed: {poll_result['request']}")
raise Exception("Polling timeout")
async def main():
async with async_playwright() as p:
# Launch with iPhone 13 emulation
iphone = p.devices["iPhone 13"]
browser = await p.chromium.launch(headless=False)
context = await browser.new_context(**iphone)
page = await context.new_page()
await page.goto("https://example.com/mobile-form")
await page.wait_for_selector(".g-recaptcha", timeout=10000)
# Extract sitekey
sitekey = await page.get_attribute(".g-recaptcha", "data-sitekey")
pageurl = page.url
print(f"Found sitekey: {sitekey}")
# Solve via CaptchaAI
token = await solve_recaptcha(sitekey, pageurl)
print(f"Token received: {token[:50]}...")
# Inject token
await page.evaluate(f"""
document.getElementById('g-recaptcha-response').value = '{token}';
document.getElementById('g-recaptcha-response').style.display = '';
""")
# Trigger callback if it exists
await page.evaluate(f"""
try {{
const clients = ___grecaptcha_cfg.clients;
Object.keys(clients).forEach(k => {{
Object.keys(clients[k]).forEach(j => {{
if (clients[k][j] && clients[k][j].callback) {{
clients[k][j].callback('{token}');
}}
}});
}});
}} catch(e) {{}}
""")
# Submit form
await page.click('button[type="submit"]')
await page.wait_for_load_state("networkidle")
print("Form submitted successfully")
await browser.close()
asyncio.run(main())
Qué hace el script, paso a paso
solve_recaptchaenvía la tarea ain.phpcon tu clave y el sitekey.- Sondea
res.phpcada cinco segundos hasta que el estado devuelve el token. mainlanza Chromium con el perfiliPhone 13y abre el formulario.- Extrae el sitekey, obtiene el token e inyecta la respuesta en el DOM.
- Dispara el callback de reCAPTCHA y envía el formulario.
Método 2: emulación móvil con Selenium y Chrome DevTools (JavaScript)
Si tu pila vive en Node.js, Selenium ofrece la emulación móvil a través del Chrome DevTools Protocol. En lugar de un perfil predefinido, defines tú mismo las métricas del dispositivo y el User-Agent.
// selenium_mobile_captcha.js
const { Builder, By, until } = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
const axios = require('axios');
const API_KEY = 'YOUR_API_KEY';
async function solveCaptcha(sitekey, pageurl) {
// Submit task
const submitResp = await axios.get('https://ocr.captchaai.com/in.php', {
params: {
key: API_KEY,
method: 'userrecaptcha',
googlekey: sitekey,
pageurl: pageurl,
json: '1',
},
});
if (submitResp.data.status !== 1) {
throw new Error(`Submit failed: ${submitResp.data.request}`);
}
const taskId = submitResp.data.request;
// Poll for result
for (let i = 0; i < 30; i++) {
await new Promise((r) => setTimeout(r, 5000));
const pollResp = await axios.get('https://ocr.captchaai.com/res.php', {
params: { key: API_KEY, action: 'get', id: taskId, json: '1' },
});
if (pollResp.data.status === 1) return pollResp.data.request;
if (pollResp.data.request !== 'CAPCHA_NOT_READY') {
throw new Error(`Solve failed: ${pollResp.data.request}`);
}
}
throw new Error('Polling timeout');
}
async function main() {
// Configure mobile emulation
const mobileEmulation = {
deviceMetrics: { width: 390, height: 844, pixelRatio: 3.0 },
userAgent:
'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) ' +
'AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1',
};
const options = new chrome.Options();
options.setMobileEmulation(mobileEmulation);
const driver = await new Builder()
.forBrowser('chrome')
.setChromeOptions(options)
.build();
try {
await driver.get('https://example.com/mobile-form');
await driver.wait(until.elementLocated(By.css('.g-recaptcha')), 10000);
// Extract sitekey
const captchaEl = await driver.findElement(By.css('.g-recaptcha'));
const sitekey = await captchaEl.getAttribute('data-sitekey');
const pageurl = await driver.getCurrentUrl();
console.log(`Sitekey: ${sitekey}`);
// Solve CAPTCHA
const token = await solveCaptcha(sitekey, pageurl);
console.log(`Token: ${token.substring(0, 50)}...`);
// Inject token
await driver.executeScript(`
document.getElementById('g-recaptcha-response').value = arguments[0];
`, token);
// Submit form
await driver.findElement(By.css('button[type="submit"]')).click();
console.log('Form submitted');
} finally {
await driver.quit();
}
}
main().catch(console.error);
Qué hace el script, paso a paso
mobileEmulationdeclara el tamaño de pantalla, elpixelRatioy el User-Agent de iPhone.solveCaptchareplica el ciclo de envío ain.phpy sondeo deres.php.- El driver abre el formulario y extrae el
data-sitekeydel widget. - Inyecta el token en el textarea
g-recaptcha-responseconexecuteScript. - Envía el formulario; usa el mismo User-Agent para resolver y para enviar.
Cuándo elegir cada método
Ninguna ruta es "mejor" en abstracto: depende de tu pila y del control que necesites sobre cada señal.
- Configuración: Playwright trae el perfil
iPhone 13listo; Selenium requiere definir las métricas y el User-Agent a mano. - Lenguaje: Playwright encaja en proyectos Python; Selenium, en Node.js.
- Control de señales: Selenium da control total campo a campo; Playwright parte de valores por defecto sensatos.
- Asincronía: Playwright es async nativo con
asyncio; en Selenium usasasync/awaitsobre el driver.
Solución de problemas frecuentes
| Problema | Causa | Solución |
|---|---|---|
| El CAPTCHA no aparece en la emulación móvil | El sitio detecta la emulación por navigator.platform |
Aplica una anulación de plataforma en el DevTools Protocol |
No se encuentra el textarea g-recaptcha-response |
El diseño móvil usa un render de CAPTCHA distinto | Busca el textarea por el atributo name en lugar del id |
| El token se inyecta pero el formulario sigue fallando | El servidor valida la coherencia del User-Agent | Usa el mismo User-Agent para resolver y para enviar el formulario |
| Cargas de página lentas en la emulación | Páginas con muchos recursos | Usa la bandera --disable-images de Chrome para acelerar la carga |
Preguntas frecuentes
¿Qué plan de CaptchaAI necesito para automatización móvil?
Depende de cuántos CAPTCHA resuelvas en paralelo, porque la facturación es por thread con resoluciones ilimitadas dentro del plan:
- BASIC ($15/mes, 5 threads): pruebas y volúmenes pequeños.
- STANDARD ($30/mes, 15 threads): varios navegadores móviles a la vez.
- ADVANCE ($90/mes, 50 threads): automatización móvil a mayor escala.
¿Qué señales conviene mantener coherentes en la emulación móvil?
Para que el widget se cargue y el servidor acepte el envío, cuida que estas señales sean consistentes entre resolver y enviar:
- El User-Agent, idéntico al resolver y al enviar el formulario.
- La ventana gráfica, con un tamaño realista de teléfono.
navigator.platform, con una anulación en el DevTools Protocol si el sitio lo inspecciona.- El tipo de interacción: algunos CAPTCHA distinguen eventos táctiles de eventos de ratón.
¿Sirve el mismo sitekey en la versión móvil y en la de escritorio?
Normalmente sí. El sitekey de reCAPTCHA v2 se asigna al dominio, no al dispositivo, así que el valor que extraes en móvil suele coincidir con el de escritorio. Lo que cambia es el diseño del widget, no la clave.
¿CaptchaAI necesita saber que el CAPTCHA es móvil?
No. CaptchaAI resuelve a partir del sitekey y la URL de la página; la distinción entre móvil y escritorio no afecta al proceso de resolución.
Artículos relacionados
- Resolver el callback de reCAPTCHA v2 con la API
- Automatización sin código con Zapier y CaptchaAI
- Manejar reCAPTCHA v2 y Turnstile en el mismo sitio
Próximos pasos
Ya tienes las dos rutas para resolver reCAPTCHA v2 desde un navegador móvil emulado. Obtén tu clave API de CaptchaAI e intégrala en tu pila de automatización móvil. Estas guías te ayudan a dar el siguiente paso:
- Manejo de CAPTCHA en apps móviles con Appium
- Resolver CAPTCHA en el WebView de React Native
- Extraer parámetros de reCAPTCHA desde el código de la página