Tutoriales

Resolver CAPTCHA en la automatización de navegador móvil

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_KEY de los ejemplos).
  • Python 3.10+ con httpx y playwright (playwright install chromium) para la primera ruta.
  • Node.js 18+ con selenium-webdriver y axios para 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:

  1. Abre la página en un contexto de navegador con señales móviles.
  2. Localiza el elemento .g-recaptcha y extrae su atributo data-sitekey.
  3. Envía el sitekey y la URL a CaptchaAI con el método userrecaptcha y sondea el resultado.
  4. Inyecta el token recibido en el textarea g-recaptcha-response y dispara el callback si existe.
  5. 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

  1. solve_recaptcha envía la tarea a in.php con tu clave y el sitekey.
  2. Sondea res.php cada cinco segundos hasta que el estado devuelve el token.
  3. main lanza Chromium con el perfil iPhone 13 y abre el formulario.
  4. Extrae el sitekey, obtiene el token e inyecta la respuesta en el DOM.
  5. 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

  1. mobileEmulation declara el tamaño de pantalla, el pixelRatio y el User-Agent de iPhone.
  2. solveCaptcha replica el ciclo de envío a in.php y sondeo de res.php.
  3. El driver abre el formulario y extrae el data-sitekey del widget.
  4. Inyecta el token en el textarea g-recaptcha-response con executeScript.
  5. 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 13 listo; 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 usas async/await sobre 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

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:

Los comentarios están deshabilitados para este artículo.