Casos de Uso

Extracción de datos financieros con manejo de CAPTCHA

Si intentas recopilar cotizaciones, informes de la SEC o resultados de un screener a escala, el CAPTCHA es el muro con el que chocas antes de ver el primer dato. La salida no es evitar el sitio: es resolver el desafío por API y seguir con tu scraper como si nada. Con CaptchaAI envías el CAPTCHA a resolver, recuperas el token y lo reinyectas en la misma solicitud, de modo que la parte visual deja de frenar tu recopilación.

Es un patrón habitual para agencias de datos y equipos de fintech —de Madrid a Ciudad de México o Buenos Aires— que necesitan un monitor de mercado predecible y facturado en USD. A lo largo de la guía verás dónde aparecen estos desafíos y cómo integrarlos en un flujo de extracción reutilizable con Python.


Dónde aparecen los CAPTCHA en los sitios financieros

Antes de escribir código conviene saber qué tipo de CAPTCHA protege cada fuente: no es lo mismo un reCAPTCHA v2 de un screener que el Cloudflare Turnstile de un terminal de mercado. Esta tabla resume los casos más frecuentes.

Fuente Tipo de CAPTCHA Activador Valor de los datos
SEC EDGAR reCAPTCHA v2 Solicitudes de gran volumen Informes corporativos
Yahoo Finance reCAPTCHA v2 Detección de scraping Cotizaciones e histórico
Bloomberg Cloudflare Turnstile Todo acceso automatizado Datos de mercado
Finviz reCAPTCHA v2 Acceso al screener Resultados del filtrado
TradingView Cloudflare Challenge Límite de solicitudes Gráficos e indicadores
Morningstar reCAPTCHA v3 Páginas de exportación Analítica de fondos

CaptchaAI resuelve las cinco primeras familias de esta tabla —reCAPTCHA v2 y v3, Cloudflare Turnstile y Cloudflare Challenge— de forma directa por API, que es justo el conjunto que domina en las plataformas financieras.


Extracción de screeners de acciones

El screener es el caso de uso más común: filtras cientos de valores por sector, capitalización o rendimiento y quieres el resultado en tabla. El flujo detecta el data-sitekey en la respuesta, resuelve el reCAPTCHA v2 con el método userrecaptcha y reenvía la solicitud con el token g-recaptcha-response.

import requests
import time
from bs4 import BeautifulSoup
import re

CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"


def solve_captcha(method, sitekey, pageurl, **kwargs):
    data = {
        "key": CAPTCHAAI_KEY,
        "method": method,
        "googlekey": sitekey,
        "pageurl": pageurl,
        "json": 1,
    }
    data.update(kwargs)

    resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data=data)
    task_id = resp.json()["request"]

    for _ in range(60):
        time.sleep(5)
        result = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
            "key": CAPTCHAAI_KEY, "action": "get",
            "id": task_id, "json": 1,
        })
        r = result.json()
        if r["request"] != "CAPCHA_NOT_READY":
            return r["request"]

    raise TimeoutError("Solve timeout")


class FinancialScraper:
    def __init__(self, proxy=None):
        self.session = requests.Session()
        if proxy:
            self.session.proxies = {"http": proxy, "https": proxy}
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36",
            "Accept-Language": "en-US,en;q=0.9",
        })

    def scrape_screener(self, url):
        """Scrape stock screener, handling CAPTCHA if triggered."""
        resp = self.session.get(url, timeout=30)

        # Check for CAPTCHA
        sitekey_match = re.search(r'data-sitekey="([^"]+)"', resp.text)
        if sitekey_match:
            sitekey = sitekey_match.group(1)
            token = solve_captcha("userrecaptcha", sitekey, url)

            # Resubmit with token
            resp = self.session.post(url, data={
                "g-recaptcha-response": token,
            })

        return self._parse_stocks(resp.text)

    def _parse_stocks(self, html):
        soup = BeautifulSoup(html, "html.parser")
        stocks = []
        for row in soup.select("table.screener-table tr")[1:]:
            cols = row.select("td")
            if len(cols) >= 8:
                stocks.append({
                    "ticker": cols[1].get_text(strip=True),
                    "company": cols[2].get_text(strip=True),
                    "sector": cols[3].get_text(strip=True),
                    "price": cols[6].get_text(strip=True),
                    "change": cols[7].get_text(strip=True),
                })
        return stocks


# Usage
scraper = FinancialScraper(
    proxy="http://user:pass@residential.proxy.com:5000"
)
stocks = scraper.scrape_screener("https://screener.example.com/screener.ashx?v=111")
for stock in stocks[:5]:
    print(f"{stock['ticker']}: {stock['price']} ({stock['change']})")

El CAPTCHA solo se resuelve cuando aparece: si la respuesta no trae data-sitekey, el scraper pasa directo al parseo y no gastas ningún thread de más.


Descarga de informes de SEC EDGAR

SEC EDGAR aplica límite de solicitudes y CAPTCHA cuando detecta acceso de gran volumen, y además exige un User-Agent identificable con correo de contacto. El siguiente scraper cumple ese requisito y solo resuelve el CAPTCHA si el servidor lo devuelve o responde con un 403.

import json


class SECFilingScraper:
    BASE_URL = "https://efts.sec.gov/LATEST"

    def __init__(self, user_agent_email, proxy=None):
        self.session = requests.Session()
        if proxy:
            self.session.proxies = {"http": proxy, "https": proxy}
        # SEC requires identifying User-Agent
        self.session.headers.update({
            "User-Agent": f"CompanyName admin@{user_agent_email}",
            "Accept": "application/json",
        })

    def search_filings(self, company, filing_type="10-K"):
        """Search EDGAR for specific filing types."""
        url = f"{self.BASE_URL}/search-index"
        params = {
            "q": company,
            "dateRange": "custom",
            "forms": filing_type,
        }

        resp = self.session.get(url, params=params, timeout=30)

        # Handle CAPTCHA if triggered
        if "captcha" in resp.text.lower() or resp.status_code == 403:
            sitekey = self._extract_sitekey(resp.text)
            if sitekey:
                token = solve_captcha("userrecaptcha", sitekey, url)
                resp = self.session.post(url, data={
                    **params,
                    "g-recaptcha-response": token,
                })

        return resp.json() if resp.status_code == 200 else {}

    def download_filing(self, filing_url):
        """Download individual filing document."""
        resp = self.session.get(filing_url, timeout=60)
        if resp.status_code == 200:
            return resp.text
        return None

    def _extract_sitekey(self, html):
        match = re.search(r'data-sitekey="([^"]+)"', html)
        return match.group(1) if match else None


# Usage
sec = SECFilingScraper(
    user_agent_email="example.com",
    proxy="http://user:pass@proxy.example.com:5000",
)
filings = sec.search_filings("Apple Inc", "10-K")

Los informes de la SEC son datos públicos y su descarga con fines de investigación está expresamente prevista, pero el correo en el User-Agent no es opcional: sin él, el 403 llega antes que cualquier CAPTCHA.


Datos de mercado protegidos con Cloudflare Turnstile

Los terminales y las páginas de datos en tiempo real cada vez usan más Cloudflare Turnstile en lugar del reCAPTCHA clásico. El patrón es el mismo, pero cambian el método (turnstile) y el nombre del token (cf-turnstile-response).

def scrape_turnstile_market_data(url, sitekey):
    """Handle Cloudflare Turnstile on financial data sites."""
    token = solve_captcha("turnstile", sitekey, url)

    session = requests.Session()
    session.headers.update({
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36",
    })

    resp = session.post(url, data={
        "cf-turnstile-response": token,
    }, timeout=30)

    return resp.json() if resp.status_code == 200 else None

Recopilación de datos programada

En producción rara vez extraes una vez: montas una foto diaria del mercado para varios tickers y la guardas en CSV. Aquí es donde importa la concurrencia. Cada CAPTCHA en vuelo ocupa un thread de tu plan, así que si vigilas cientos de valores en paralelo el número de threads es lo que marca el ritmo. El plan STANDARD ($30/mes, 15 threads) cubre un monitor de mercado modesto, mientras que ADVANCE ($90/mes, 50 threads) da margen para carteras grandes o barridos frecuentes; en todos los planes las resoluciones por thread son ilimitadas.

import csv
from datetime import datetime


def daily_market_snapshot(tickers, output_dir="data"):
    """Collect daily stock data, handling CAPTCHAs automatically."""
    scraper = FinancialScraper(
        proxy="http://user:pass@residential.proxy.com:5000"
    )

    date_str = datetime.now().strftime("%Y-%m-%d")
    results = []

    for ticker in tickers:
        url = f"https://screener.example.com/quote.ashx?t={ticker}"
        try:
            data = scraper.scrape_screener(url)
            if data:
                results.extend(data)
            time.sleep(2)  # Rate limit
        except Exception as e:
            print(f"Error on {ticker}: {e}")

    # Save to CSV
    filepath = f"{output_dir}/market_{date_str}.csv"
    with open(filepath, "w", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=["ticker", "company", "sector", "price", "change"])
        writer.writeheader()
        writer.writerows(results)

    print(f"Saved {len(results)} records to {filepath}")
    return results


# Run daily
tickers = ["AAPL", "GOOGL", "MSFT", "AMZN", "TSLA"]
daily_market_snapshot(tickers)

Buenas prácticas para no saturar el sitio

Los portales financieros son más estrictos que la media con el acceso automatizado, así que la disciplina de solicitudes pesa tanto como resolver el CAPTCHA. Estas pautas reducen los bloqueos antes de que aparezcan.

Práctica Recomendación
Retraso entre solicitudes 2-5 segundos entre páginas
Conexiones simultáneas Máximo 3-5 por dominio
Tipo de proxy Residencial o ISP
Duración de la sesión Sesiones sticky de 5 a 10 minutos
User-Agent Realista y consistente por sesión
SEC EDGAR Incluir email de contacto en el UA (obligatorio)
Horario de mercado Extrae en horas de menor actividad cuando puedas

Ten presente también el marco legal: respeta siempre los términos de servicio de cada fuente y la normativa de protección de datos aplicable —GDPR y LOPDGDD en España, LFPDPPP en México— cuando el flujo toque datos personales. Esta guía no es asesoramiento legal.


Solución de problemas comunes

Cuando un scraper financiero deja de devolver datos, el fallo casi siempre es uno de estos cinco. La tabla te da la causa probable y el arreglo directo.

Problema Causa Solución
403 en SEC EDGAR Falta el User-Agent con email Añade el encabezado CompanyName email@domain
CAPTCHA en cada solicitud Límite de solicitudes superado Añade retrasos de 3-5 s entre solicitudes
Precios desactualizados Respuesta en caché Añade un parámetro de cache-busting a la consulta
Error al parsear JSON Se devolvió la página del CAPTCHA Comprueba el CAPTCHA antes de parsear
IP bloqueada Demasiadas solicitudes desde la misma IP Cambia a una red residencial autorizada rotativa

Preguntas frecuentes

¿Qué plan de CaptchaAI necesito para monitorizar muchos tickers a la vez?

Depende de cuántos CAPTCHA quieras resolver en paralelo, no de cuántos resuelvas al día. Como cada thread atiende un CAPTCHA en vuelo y las resoluciones por thread son ilimitadas, el plan STANDARD ($30/mes, 15 threads) basta para un monitor pequeño y ADVANCE ($90/mes, 50 threads) para barridos amplios.

¿CaptchaAI resuelve el Cloudflare Turnstile y el reCAPTCHA de los sitios financieros?

Sí. Resuelve reCAPTCHA v2 y v3, Cloudflare Turnstile y Cloudflare Challenge por API, que son las familias que protegen la mayoría de screeners, portales de informes y terminales de mercado.

¿Cómo evito que bloqueen mi IP al extraer datos de mercado?

Combina retrasos de 2-5 segundos entre páginas, un máximo de 3-5 conexiones por dominio y salida de red residencial o ISP rotativa. Extraer en horas de menor actividad y mantener un User-Agent estable por sesión reduce todavía más los bloqueos.

Los datos públicos —informes ante la SEC, cotizaciones— suelen estar permitidos, y EDGAR habilita el acceso con fines de investigación. Aun así, respeta los términos de servicio, los límites de cada sitio y la normativa de protección de datos aplicable.


Guías relacionadas


Recopila datos de mercado sin que el CAPTCHA te interrumpa: consigue tu clave de CaptchaAI y automatiza tu investigación financiera.

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