Explicaciones Técnicas

Cómo manejar reCAPTCHA v2 en flujos de trabajo de Web Scraping

Resolver un reCAPTCHA v2 dentro de un scraper se reduce a un intercambio de tres pasos con una API de resolución como CaptchaAI:

  1. Extraes el sitekey que el sitio incrusta en el HTML de la página.
  2. Se lo envías a la API junto con la URL de la página y esperas a que te devuelva un token válido.
  3. Vuelves a inyectar ese token en la página para que el backend del sitio deje pasar la solicitud.

Mientras ese token siga siendo válido, tu extracción continúa sin que ningún humano tenga que marcar la casilla. Esta guía recorre el flujo completo con código listo para usar en Python (Selenium) y Node.js (Puppeteer), más una ruta solo HTTP sin navegador.

Qué necesita tu scraper para resolver reCAPTCHA v2

Todo widget de reCAPTCHA v2 expone dos datos que tu scraper tiene que capturar antes de delegar la resolución:

Parámetro Qué es
googlekey El sitekey público incrustado en el HTML de la página
pageurl La URL donde aparece el CAPTCHA

Tu scraper envía ambos a la API de CaptchaAI, espera a que se complete la resolución y recibe un token. Ese token se inyecta en el campo g-recaptcha-response de la página (o se pasa a la función callback), y el backend del sitio de destino lo verifica contra Google antes de permitir que la solicitud siga adelante.


Resolver reCAPTCHA v2 con Selenium en Python

import requests
import time
from selenium import webdriver
from selenium.webdriver.common.by import By

# Step 1: Open the page with Selenium
driver = webdriver.Chrome()
driver.get("https://example.com/protected-page")

# Step 2: Extract the sitekey
sitekey = driver.find_element(By.CSS_SELECTOR, ".g-recaptcha").get_attribute("data-sitekey")
page_url = driver.current_url

# Step 3: Submit to CaptchaAI
response = requests.get("https://ocr.captchaai.com/in.php", params={
    "key": "YOUR_API_KEY",
    "method": "userrecaptcha",
    "googlekey": sitekey,
    "pageurl": page_url,
    "json": 1
}).json()

task_id = response["request"]

# Step 4: Poll for result
token = None
for _ in range(40):
    time.sleep(5)
    result = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": "YOUR_API_KEY",
        "action": "get",
        "id": task_id,
        "json": 1
    }).json()

    if result.get("status") == 1:
        token = result["request"]
        break
    if result.get("request") != "CAPCHA_NOT_READY":
        raise RuntimeError(f"Solve failed: {result['request']}")

# Step 5: Inject the token and submit
driver.execute_script(
    f'document.getElementById("g-recaptcha-response").innerHTML = "{token}";'
)

# Check for callback
callback = driver.execute_script(
    'var el = document.querySelector(".g-recaptcha"); '
    'return el ? el.getAttribute("data-callback") : null;'
)
if callback:
    driver.execute_script(f'{callback}("{token}");')
else:
    driver.find_element(By.CSS_SELECTOR, "form").submit()

# Step 6: Scrape the data
print(driver.page_source[:500])
driver.quit()

Resolver reCAPTCHA v2 con Puppeteer en Node.js

const puppeteer = require("puppeteer");

async function scrapeWithCaptcha(url) {
  const browser = await puppeteer.launch({ headless: "new" });
  const page = await browser.newPage();
  await page.goto(url, { waitUntil: "networkidle2" });

  // Extract sitekey
  const sitekey = await page.$eval(".g-recaptcha", (el) => el.dataset.sitekey);

  // Submit to CaptchaAI
  const submitRes = await fetch(
    `https://ocr.captchaai.com/in.php?${new URLSearchParams({
      key: "YOUR_API_KEY",
      method: "userrecaptcha",
      googlekey: sitekey,
      pageurl: url,
      json: 1,
    })}`
  );
  const { request: taskId } = await submitRes.json();

  // Poll for result
  let token;
  for (let i = 0; i < 40; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    const res = await fetch(
      `https://ocr.captchaai.com/res.php?${new URLSearchParams({
        key: "YOUR_API_KEY",
        action: "get",
        id: taskId,
        json: 1,
      })}`
    );
    const data = await res.json();
    if (data.status === 1) {
      token = data.request;
      break;
    }
    if (data.request !== "CAPCHA_NOT_READY")
      throw new Error(`Solve failed: ${data.request}`);
  }

  // Inject token
  await page.evaluate((t) => {
    document.getElementById("g-recaptcha-response").innerHTML = t;
    const cb = document.querySelector(".g-recaptcha")?.dataset.callback;
    if (cb && window[cb]) window[cb](t);
  }, token);

  // Wait for navigation after form submit
  await page.waitForNavigation({ waitUntil: "networkidle2" });
  const content = await page.content();
  await browser.close();
  return content;
}

scrapeWithCaptcha("https://example.com/protected-page").then(console.log);

No todos los flujos necesitan un navegador completo. Antes de escribir código, decide la ruta según cómo valide el token el sitio de destino:

Situación Ruta recomendada
El sitio valida el token con un callback de JavaScript Navegador (Selenium o Puppeteer)
El sitio acepta g-recaptcha-response como campo POST Solo HTTP, sin navegador
El sitio bloquea navegadores headless antes de mostrar el CAPTCHA Navegador con configuración estándar

Resolver reCAPTCHA v2 sin navegador (solo HTTP)

Si el sitio de destino entrega el CAPTCHA dentro de un envío de formulario, puedes saltarte el navegador por completo y trabajar solo con solicitudes HTTP. Es más rápido y consume muchos menos recursos:

import requests
import time

session = requests.Session()
session.headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0"

# Load the page to get cookies
session.get("https://example.com/protected-page")

# Solve the CAPTCHA
sitekey = "6Le-wvkSAAAAAN..."  # extracted from page HTML
solve_resp = requests.get("https://ocr.captchaai.com/in.php", params={
    "key": "YOUR_API_KEY", "method": "userrecaptcha",
    "googlekey": sitekey, "pageurl": "https://example.com/protected-page",
    "json": 1
}).json()

task_id = solve_resp["request"]
time.sleep(15)

# Poll
for _ in range(30):
    result = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": "YOUR_API_KEY", "action": "get", "id": task_id, "json": 1
    }).json()
    if result.get("status") == 1:
        token = result["request"]
        break
    time.sleep(5)

# Submit with token
resp = session.post("https://example.com/protected-page", data={
    "g-recaptcha-response": token,
    "other_field": "value"
})
print(resp.text[:500])

Cuándo tu navegador headless queda bloqueado

Algunos sitios detectan navegadores headless y los bloquean antes incluso de mostrar el CAPTCHA. Si te frenan antes de ver el reCAPTCHA, prueba estos ajustes:

  • Usa el modo headless: "new" de Puppeteer, más cercano al comportamiento de un navegador real
  • Define un User-Agent real en lugar del predeterminado de automatización
  • Mantén una configuración estándar del navegador: idioma, viewport y cabeceras coherentes
  • Enruta las solicitudes de extracción posteriores a través de una salida de red autorizada

Un escenario habitual: monitoreo de portales y marketplaces

Piensa en un equipo que hace seguimiento de precios en un marketplace regional (MercadoLibre, Amazon.es) o que valida un portal de trámites públicos —una cita previa, el SAT, AFIP— donde un reCAPTCHA v2 corta la navegación automatizada. El patrón es siempre el mismo: detectas el widget, delegas la resolución en la API y reanudas la extracción con el token inyectado.

Para que ese trabajo sea sostenible, respeta los términos de servicio y la normativa de protección de datos aplicable (RGPD y LOPDGDD en España, LFPDPPP en México, entre otras): extrae solo datos públicos y automatiza únicamente flujos que tengas autorización para consultar. Y trata el coste como un gasto mensual predecible en USD, porque CaptchaAI factura por thread concurrente y no por resolución:

Plan Precio Threads Ideal para
BASIC $15/mes 5 threads Volúmenes bajos y pruebas iniciales
ADVANCE $90/mes 50 threads Extracción de gran volumen en paralelo

Para agencias y freelancers que facturan en monedas locales volátiles, un coste fijo en USD es más fácil de presupuestar que el pago por cada CAPTCHA resuelto.

Preguntas frecuentes

¿Sirve este flujo para reCAPTCHA v2 invisible?

Sí. El reCAPTCHA v2 invisible usa el mismo sitekey y devuelve un g-recaptcha-response; envías los mismos parámetros a la API y, en vez de marcar una casilla, disparas el callback de la página con el token. El código de esta guía ya cubre ese caso.

¿Qué plan de CaptchaAI conviene para scraping de gran volumen?

Depende de cuántas resoluciones necesites en paralelo. Como CaptchaAI factura por thread concurrente con resoluciones ilimitadas, esta es la referencia rápida:

  • BASIC ($15/mes, 5 threads) para volúmenes bajos o pruebas iniciales
  • ADVANCE ($90/mes, 50 threads) cuando lanzas muchas extracciones a la vez

No hay tarifa por CAPTCHA resuelto, así que subes de threads solo cuando tu concurrencia lo pide.

¿Necesito ejecutar un navegador para cada resolución?

No siempre. Si el sitio acepta g-recaptcha-response como campo POST, la ruta solo HTTP evita el navegador y es mucho más ligera. Si la validación depende de un callback de JavaScript, necesitas un navegador como Selenium o Puppeteer.

Depende del sitio y de tu jurisdicción. Resolver el CAPTCHA es una operación técnica; la responsabilidad legal está en qué datos extraes y con qué permiso. Ten presentes estos límites:

  • Limítate a datos públicos y respeta los términos de servicio del sitio
  • Cumple la normativa de protección de datos aplicable a tu operación
  • No automatices flujos autenticados que no te pertenezcan

¿Y si el sitio usa reCAPTCHA Enterprise?

Añade enterprise=1 a tu solicitud a CaptchaAI y el resto del flujo no cambia. Tienes el detalle en cómo resolver reCAPTCHA v2 Enterprise con la API.


Empieza a resolver reCAPTCHA v2 en tu pipeline

  1. Consigue tu API key en captchaai.com/api.php
  2. Extrae el sitekey de la página objetivo
  3. Usa los ejemplos de arriba para resolver e inyectar el token
  4. Escala con resoluciones simultáneas (más threads) para flujos de gran volumen

Guías relacionadas

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