Para hacer scraping de un sitio protegido por CAPTCHA solo necesitas tres pasos: detectar qué tipo de desafío aparece, resolverlo con la API de CaptchaAI y reinyectar el token en la misma sesión antes de seguir extrayendo datos. El resto es ingeniería de scraping normal: sesiones, cabeceras y control de la frecuencia de solicitudes. Esta guía te muestra el patrón completo con ejemplos en Python, desde la detección bajo demanda hasta los sitios detrás de Cloudflare.
Trabaja siempre sobre sitios que tengas autorización para extraer y respeta los términos de servicio y la normativa de protección de datos aplicable (por ejemplo, RGPD y LOPDGDD en España o la LFPDPPP en México).
Qué CAPTCHA vas a encontrar al hacer scraping
Antes de escribir una sola línea, conviene saber a qué te enfrentas. La mayoría de los sitios de alto valor colocan uno de estos desafíos en formularios de inicio de sesión, buscadores o páginas de listado. Cada tipo se resuelve con un método distinto de la API:
| CAPTCHA | Dónde aparece | Método CaptchaAI |
|---|---|---|
| reCAPTCHA v2 | Formularios de inicio de sesión, páginas de búsqueda | method=userrecaptcha |
| reCAPTCHA v3 | Puntuación en segundo plano en cualquier página | method=userrecaptcha&version=v3 |
| Cloudflare Turnstile | Sitios detrás de Cloudflare | method=turnstile |
| Cloudflare Challenge | Bloqueo Cloudflare de página completa | method=cloudflare_challenge |
| Imagen/OCR | Sitios heredados y catálogos antiguos | method=base64 |
| GeeTest v3 | Portales y tiendas de la región asiática | method=geetest |
CaptchaAI resuelve estos tipos junto con las variantes reCAPTCHA v2 Invisible, v2 Enterprise y v3 Enterprise, además de grid-image y BLS. No resuelve hCaptcha ni FunCaptcha (Arkose Labs); si tu objetivo depende de esos, esta ruta no aplica.
Estrategia 1: detectar y resolver bajo demanda
Es el enfoque más fiable para la mayoría de proyectos: extrae con normalidad y llama al solver solo cuando aparece un CAPTCHA. Así no gastas threads en páginas que no lo necesitan y tu scraper se comporta como un cliente normal el resto del tiempo.
import requests
import time
from bs4 import BeautifulSoup
API_KEY = "YOUR_API_KEY"
class ProtectedScraper:
def __init__(self):
self.session = requests.Session()
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
})
def scrape(self, url):
resp = self.session.get(url)
# Check for CAPTCHA
if self._has_captcha(resp.text):
resp = self._handle_captcha(resp.text, url)
return resp.text
def _has_captcha(self, html):
indicators = ["g-recaptcha", "cf-turnstile", "h-captcha", "captcha"]
return any(ind in html.lower() for ind in indicators)
def _handle_captcha(self, html, url):
soup = BeautifulSoup(html, "html.parser")
# reCAPTCHA v2
rc = soup.find("div", class_="g-recaptcha")
if rc:
token = self._solve_recaptcha(rc["data-sitekey"], url)
return self.session.post(url, data={"g-recaptcha-response": token})
# Cloudflare Turnstile
ts = soup.find("div", class_="cf-turnstile")
if ts:
token = self._solve_turnstile(ts["data-sitekey"], url)
return self.session.post(url, data={"cf-turnstile-response": token})
raise Exception("Unknown CAPTCHA type")
def _solve_recaptcha(self, site_key, page_url):
resp = requests.get("https://ocr.captchaai.com/in.php", params={
"key": API_KEY, "method": "userrecaptcha",
"googlekey": site_key, "pageurl": page_url
})
return self._poll(resp.text.split("|")[1])
def _solve_turnstile(self, site_key, page_url):
resp = requests.get("https://ocr.captchaai.com/in.php", params={
"key": API_KEY, "method": "turnstile",
"sitekey": site_key, "pageurl": page_url
})
return self._poll(resp.text.split("|")[1])
def _poll(self, task_id):
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get", "id": task_id
})
if result.text == "CAPCHA_NOT_READY": continue
if result.text.startswith("OK|"): return result.text.split("|")[1]
raise Exception(result.text)
raise TimeoutError()
# Usage
scraper = ProtectedScraper()
html = scraper.scrape("https://example.com/data")
La clave está en el sondeo: envías la tarea al endpoint in.php, esperas unos segundos y consultas el resultado en res.php hasta que devuelve OK|token. Ese token es el que reinyectas en el POST de la página protegida.
Estrategia 2: resolución anticipada en páginas conocidas
Si ya sabes que una URL concreta siempre pide CAPTCHA (por ejemplo, un buscador tras un formulario), no pierdas tiempo cargándola dos veces. Resuelve primero y envía la solicitud con el token ya incluido:
def scrape_known_captcha_page(url, site_key):
# Solve before even loading the page
token = solve_recaptcha(site_key, url)
# Submit directly with token
resp = requests.post(url, data={
"g-recaptcha-response": token,
"query": "search term"
})
return resp.text
Este patrón reduce una petición por página y es ideal cuando el sitekey es estable y no cambia entre visitas.
Estrategia 3: sitios detrás de Cloudflare
Cloudflare Challenge no devuelve un simple token: entrega una cookie de validación y un User-Agent que debes reutilizar en todas las solicitudes posteriores. Manda la tarea con tu proxy de salida autorizado y espera a que la respuesta traiga la cookie:
def get_cloudflare_clearance(url, proxy):
resp = requests.get("https://ocr.captchaai.com/in.php", params={
"key": API_KEY,
"method": "cloudflare_challenge",
"pageurl": url,
"proxy": proxy,
"proxytype": "HTTP"
})
task_id = resp.text.split("|")[1]
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get", "id": task_id
})
if result.text == "CAPCHA_NOT_READY": continue
if "qa_validation_cookie" in result.text:
# Parse qa_validation_cookie and user_agent from response
return result.text
raise TimeoutError()
El detalle que suele romper esta integración es mezclar salidas de red: si resuelves con una IP y luego navegas con otra, Cloudflare invalida la cookie. Usa el mismo proxy y el mismo User-Agent de principio a fin.
Scraping de varias páginas
Una vez tienes la resolución resuelta, el recorrido por listados paginados es rutina. Añade una pausa aleatoria entre páginas para no disparar el CAPTCHA en cada solicitud:
def scrape_multiple_pages(base_url, site_key, pages):
scraper = ProtectedScraper()
results = []
for page in pages:
url = f"{base_url}?page={page}"
try:
html = scraper.scrape(url)
soup = BeautifulSoup(html, "html.parser")
items = soup.find_all("div", class_="item")
results.extend([item.text.strip() for item in items])
print(f"Page {page}: {len(items)} items")
except Exception as e:
print(f"Page {page} failed: {e}")
time.sleep(random.uniform(2, 5))
return results
Cuánto cuesta a escala
En scraping, el coste no lo marca cada CAPTCHA suelto sino cuántos resuelves en paralelo. CaptchaAI factura por thread (una resolución en curso), con resoluciones ilimitadas por thread durante el mes. El plan BASIC cuesta $15/mes e incluye 5 threads; STANDARD ($30/mes, 15 threads) y ADVANCE ($90/mes, 50 threads) cubren cargas más grandes. Para una agencia que monitoriza precios en marketplaces regionales (por ejemplo, una plataforma tipo MercadoLibre o Amazon.es), un coste mensual fijo en USD es más predecible que pagar por resolución cuando facturas en una moneda local volátil.
Este mismo patrón sirve para hacer QA de portales públicos protegidos por CAPTCHA —trámites de cita previa, SAT o AFIP, o los centros de visado BLS— siempre sobre flujos que tengas autorización para probar. Empieza con pocos threads y escala cuando el volumen lo pida.
Solución de problemas
| Problema | Solución |
|---|---|
| El CAPTCHA aparece en cada página | Usa proxies y baja la frecuencia de solicitudes |
| El token es rechazado tras resolverlo | Puede haber caducado; úsalo dentro de los 120 s |
| Cloudflare bloquea pese a la validación | Usa el mismo proxy y User-Agent en todas las solicitudes |
| El sitio devuelve otra página tras resolver | Revisa redirecciones o cookies adicionales |
Preguntas frecuentes
¿Necesito proxies para hacer scraping de sitios con CAPTCHA?
Depende. Para volúmenes bajos suele bastar con controlar la frecuencia de solicitudes. En cuanto escalas, una salida de red autorizada y estable evita que el sitio te muestre un CAPTCHA en cada página; para Cloudflare Challenge, además, la validación queda ligada a esa IP.
¿Cuánto tarda en resolverse un CAPTCHA durante el scraping?
Varía según el tipo. El patrón de sondeo espera 5 segundos entre consultas a res.php hasta recibir OK|token. Turnstile y reCAPTCHA suelen completarse en pocos segundos; diseña tu bucle con un tiempo de espera holgado para no abandonar tareas válidas.
¿El token sirve para siempre?
No. Los tokens caducan rápido —por eso conviene usarlos dentro de unos 120 segundos y reinyectarlos en la misma sesión de inmediato. Si tu pipeline los almacena, valida la caducidad antes de reutilizarlos dentro de su ventana de vida.
¿Cómo manejo las páginas renderizadas con JavaScript?
Usa Selenium, Puppeteer o Playwright para renderizar el JavaScript, extrae los parámetros del CAPTCHA (como el sitekey) y resuélvelo con CaptchaAI. Consulta el manejo de CAPTCHA con Selenium.
¿Puedo hacer scraping de sitios que requieren inicio de sesión?
Sí. Inicia sesión primero —resolviendo cualquier CAPTCHA de acceso—, conserva las cookies de sesión y luego extrae las páginas autenticadas. CaptchaAI resuelve el CAPTCHA en cualquier etapa del flujo.