Use Cases

Récupération de données financières avec la gestion des CAPTCHA

Un pipeline de collecte de données financières s'arrête net dès qu'un filtre d'actions ou un portail de dépôts affiche un CAPTCHA. La solution tient en une boucle : détectez le défi, envoyez son sitekey à l'API CaptchaAI, récupérez le token puis renvoyez-le avec votre requête. CaptchaAI prend en charge reCAPTCHA v2, reCAPTCHA v3 et Cloudflare Turnstile, soit l'essentiel de ce que déclenchent les sites boursiers face au scraping.

Concrètement, pour un analyste quantitatif qui alimente chaque matin son modèle avec les cotations européennes, les graphiques TradingView et les derniers dépôts SEC, ce mécanisme transforme un job qui plante à 6 h du matin en une collecte qui tourne seule. Ce guide montre comment brancher la résolution CAPTCHA sur un scraper Python, du filtre d'actions au dépôt SEC EDGAR en passant par les données protégées par Turnstile.


Où les CAPTCHA bloquent la collecte de données financières

Avant d'écrire une ligne de code, identifiez où le défi apparaît et quel type est servi : c'est ce qui détermine la méthode d'API à appeler.

Source Type de CAPTCHA Déclencheur Valeur des données
SEC EDGAR reCAPTCHA v2 Requêtes à fort volume Dépôts d'entreprises
Yahoo Finance reCAPTCHA v2 Détection de scraping Cotations, historiques
Bloomberg Cloudflare Turnstile Tout accès automatisé Données de marché
Finviz reCAPTCHA v2 Accès au filtre d'actions Résultats de filtrage
TradingView Cloudflare Challenge Limitation de débit Graphiques, indicateurs
Morningstar reCAPTCHA v3 Pages d'export de données Analyses de fonds

Régler le débit avant de résoudre les CAPTCHA

Les sites financiers sont particulièrement stricts sur l'accès automatisé. Bien réglé, le débit réduit la fréquence des défis et évite le blocage d'IP, ce qui compte autant que la résolution elle-même.

Pratique Recommandation
Délai entre requêtes 2 à 5 secondes entre les pages
Connexions simultanées 3 à 5 maximum par domaine
Type de proxy Résidentiel ou FAI (ISP)
Durée de session Sessions persistantes de 5 à 10 min
User-Agent Réaliste et constant sur toute la session
SEC EDGAR Adresse e-mail de contact obligatoire dans le User-Agent
Heures de marché Scraper en heures creuses quand c'est possible

Côté capacité, CaptchaAI facture au thread (un CAPTCHA en cours), pas à la résolution : le plan BASIC ($15/mois, 5 threads) suffit à une collecte quotidienne séquentielle, tandis qu'un ADVANCE ($90/mois, 50 threads) absorbe une collecte parallèle sur des dizaines de tickers.


Périmètre et conformité RGPD

Les données financières publiques (dépôts SEC, cotations, indices) sont généralement collectables, mais restez dans un périmètre propre : respectez les conditions d'utilisation, les fichiers robots.txt et les limites de débit de chaque source. Côté européen, dès que vous croisez ces données avec des informations personnelles (identité d'un dirigeant, portefeuille d'un client), le RGPD s'applique : minimisez les données personnelles conservées et documentez votre base légale. CaptchaAI ne fait que résoudre le défi CAPTCHA ; la conformité de votre collecte reste votre responsabilité.


Scraper un filtre d'actions (stock screener)

Le scraper détecte l'attribut data-sitekey, envoie le défi à CaptchaAI, puis renvoie le token dans le champ g-recaptcha-response pour rejouer la requête et parser les résultats.

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']})")

Le token n'est valide que quelques minutes : résolvez à la demande, au moment où le défi apparaît, plutôt que de constituer une réserve de tokens à l'avance.


Extraire les dépôts SEC EDGAR

SEC EDGAR applique une limitation de débit et sert un CAPTCHA sur les accès à fort volume. Point crucial : l'API exige un User-Agent identifiant avec une adresse e-mail de contact, sans quoi vous récoltez des 403 avant même de voir un défi.

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")

Données de marché protégées par Cloudflare Turnstile

Bloomberg et d'autres portails servent un Cloudflare Turnstile sur tout accès automatisé. La méthode change (turnstile) et le token part cette fois dans le champ 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

Planifier une collecte quotidienne

Une fois les briques en place, orchestrez un instantané journalier : le script parcourt une liste de tickers, gère automatiquement les CAPTCHA rencontrés et écrit un CSV daté, prêt à alimenter votre modèle.

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)

Planifiez ce job en heures creuses (avant l'ouverture des marchés, par exemple) via cron ou une fonction serverless sur une région européenne comme eu-west-3 (Paris) pour rester proche des sources et réduire la latence.


Dépannage

Problème Cause Correctif
403 sur SEC EDGAR User-Agent sans e-mail de contact Ajoutez l'en-tête CompanyName email@domain
CAPTCHA à chaque requête Limite de débit dépassée Ajoutez des délais de 3 à 5 s entre les requêtes
Prix obsolètes Réponse servie depuis le cache Ajoutez un paramètre anti-cache à l'URL
Erreur de parsing JSON Page CAPTCHA renvoyée à la place Vérifiez la présence d'un CAPTCHA avant de parser
IP bloquée Trop de requêtes depuis la même IP Passez à un proxy résidentiel rotatif

FAQ

Quel plan CaptchaAI choisir pour un scraping financier quotidien ?

Pour un instantané journalier séquentiel, le plan BASIC ($15/mois, 5 threads) suffit largement. Si vous collectez en parallèle sur des centaines de tickers ou plusieurs sources simultanément, passez à ADVANCE ($90/mois, 50 threads). La facturation se fait au thread, avec des résolutions illimitées.

Quels types de CAPTCHA CaptchaAI gère-t-il sur les sites financiers ?

Les plus courants : reCAPTCHA v2 (SEC EDGAR, Finviz, Yahoo Finance), reCAPTCHA v3 (pages d'export type Morningstar) et Cloudflare Turnstile (Bloomberg). Il suffit d'adapter la méthode d'API et le champ de token renvoyé.

Quel type de proxy utiliser pour les portails boursiers ?

Privilégiez les proxys résidentiels ou FAI (ISP), qui déclenchent moins de défis que les IP de datacenter. Combinez-les à des sessions persistantes de 5 à 10 minutes et à un User-Agent constant pour rester crédible.

Le scraping de données financières est-il conforme au RGPD ?

Les données de marché publiques ne relèvent pas du RGPD en tant que telles, mais dès qu'elles sont associées à des personnes identifiables, minimisez ce que vous conservez et vérifiez votre base légale. CaptchaAI résout uniquement le défi ; la conformité de la collecte reste de votre côté.


Guides connexes


Collectez des données de marché sans blocage CAPTCHA — récupérez votre clé CaptchaAI et automatisez votre veille financière.

Les commentaires sont désactivés pour cet article.