Use Cases

Collecte de données sur les sites de vente au détail avec gestion CAPTCHA

Un crawl de catalogue e-commerce s'arrête rarement sur une erreur HTTP propre : il s'arrête sur une image de texte déformé et un champ de saisie. Trois gestes suffisent à le relancer — détecter la page de défi, envoyer l'image à l'API CaptchaAI avec method=base64, renvoyer le formulaire avec la chaîne obtenue — et ce guide les écrit en Python. Le CAPTCHA image (OCR) fait partie des types pris en charge en disponibilité générale par CaptchaAI, comme reCAPTCHA v2 et v3, Cloudflare Turnstile ou GeeTest v3.

Pourquoi les sites marchands déclenchent un défi CAPTCHA

Un site de vente au détail n'affiche pas une image à résoudre au hasard : il réagit à un faisceau de signaux accumulés sur votre session.

Signal Ce que voit le site
Volume de requêtes Trop d'appels depuis une seule adresse IP sur une fenêtre courte
Cookies absents Aucun cookie de session, donc aucune continuité entre les pages
En-têtes incohérents User-Agent automatisé, Accept-Language manquant
Réputation IP Plages datacenter ou proxys déjà signalés

Une fois le seuil franchi, la réponse HTTP reste souvent un code 200 : c'est le corps de la page qui change. Tant que le défi n'est pas validé, la session reste sur le même écran.

Scénario : surveiller un catalogue e-commerce depuis Paris

Une équipe qui alimente un comparateur de prix suit 4 000 références par jour sur plusieurs enseignes, depuis des workers Python hébergés chez OVHcloud ou sur AWS eu-west-3 (Paris). Le pipeline tourne trois semaines sans incident, puis le taux de pages exploitables tombe à 60 %. Aucune erreur dans les logs : simplement des sélecteurs de prix qui renvoient None, parce que le HTML reçu est en réalité un défi CAPTCHA.

Deux corrections, dans cet ordre : détecter le défi explicitement au lieu de laisser le parseur échouer en silence, puis brancher la résolution OCR pour que le worker reprenne sa page.

Côté conformité, une veille de prix publics ne collecte pas de données personnelles : journalisez les URL et les horodatages, pas des avis clients nominatifs. C'est la lecture RGPD la plus simple à tenir.

Prérequis pour la gestion des CAPTCHA en scraping

Prérequis Détails
Clé API CaptchaAI Depuis captchaai.com
Python 3.7+ Avec requests et beautifulsoup4
Proxys résidentiels Recommandés pour un crawl soutenu
Un plan adapté au parallélisme Les threads plafonnent les résolutions simultanées, pas le total mensuel

Résoudre le CAPTCHA image d'un site de vente au détail

Étape 1 : détecter la page de défi

Cherchez la signature du défi dans le corps de la réponse avant de parser quoi que ce soit : une fonction de détection dédiée rend le phénomène mesurable. Les extraits ci-dessous visent une page produit publique d'Amazon ; la mécanique se transpose aux autres enseignes, seuls les sélecteurs changent.

import requests
from bs4 import BeautifulSoup

session = requests.Session()
session.headers.update({
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
    "Accept-Language": "en-US,en;q=0.9",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
})

def is_captcha_page(html):
    return "Type the characters you see in this image" in html or \
           "captcha" in html.lower()

url = "https://www.amazon.com/dp/B0EXAMPLE"
resp = session.get(url)

if is_captcha_page(resp.text):
    print("CAPTCHA detected!")
else:
    print("Page loaded successfully")

Étape 2 : extraire l'image et la résoudre

L'image est téléchargée avec la même session — donc les mêmes cookies — puis encodée en base64 et envoyée à in.php. Le résultat s'obtient en interrogeant res.php : tant que la réponse vaut CAPCHA_NOT_READY, la résolution est en cours.

import base64

API_KEY = "YOUR_API_KEY"

def solve_amazon_captcha(session, captcha_page_html, captcha_page_url):
    soup = BeautifulSoup(captcha_page_html, "html.parser")

    # Find the CAPTCHA image
    img_tag = soup.find("img", src=lambda s: s and "captcha" in s.lower())
    if not img_tag:
        raise Exception("CAPTCHA image not found")

    img_url = img_tag["src"]

    # Download the image
    img_resp = session.get(img_url)
    img_base64 = base64.b64encode(img_resp.content).decode()

    # Submit to CaptchaAI
    submit_resp = requests.get("https://ocr.captchaai.com/in.php", params={
        "key": API_KEY,
        "method": "base64",
        "body": img_base64
    })
    task_id = submit_resp.text.split("|")[1]

    # Poll for result
    import time
    for _ in range(30):
        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(f"Solve error: {result.text}")

    raise TimeoutError("Solve timed out")

Étape 3 : renvoyer le formulaire

C'est l'étape la plus souvent ratée. Reprenez tous les champs cachés du formulaire d'origine, ajoutez-y la réponse, puis postez vers l'action convertie en URL absolue.

def submit_captcha_solution(session, captcha_page_html, solution, captcha_page_url):
    soup = BeautifulSoup(captcha_page_html, "html.parser")
    form = soup.find("form")

    # Build form data
    form_data = {}
    for inp in form.find_all("input"):
        name = inp.get("name")
        if name:
            form_data[name] = inp.get("value", "")

    # Set the CAPTCHA answer
    form_data["field-keywords"] = solution

    # Submit
    action = form.get("action", captcha_page_url)
    if action.startswith("/"):
        from urllib.parse import urljoin
        action = urljoin(captcha_page_url, action)

    resp = session.post(action, data=form_data)
    return resp

Script complet : collecte produit avec gestion CAPTCHA

Assemblé, le parcours tient dans une fonction : requête, détection, résolution, renvoi du formulaire, extraction des champs produit.

import requests
import base64
import time
from bs4 import BeautifulSoup

API_KEY = "YOUR_API_KEY"

def scrape_amazon_product(url):
    session = requests.Session()
    session.headers.update({
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
        "Accept-Language": "en-US,en;q=0.9"
    })

    resp = session.get(url)

    # Handle CAPTCHA if present
    if "captcha" in resp.text.lower():
        soup = BeautifulSoup(resp.text, "html.parser")
        img = soup.find("img", src=lambda s: s and "captcha" in s.lower())

        if img:
            # Download and solve
            img_data = session.get(img["src"]).content
            img_b64 = base64.b64encode(img_data).decode()

            submit = requests.get("https://ocr.captchaai.com/in.php", params={
                "key": API_KEY, "method": "base64", "body": img_b64
            })
            task_id = submit.text.split("|")[1]

            for _ in range(30):
                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|"):
                    solution = result.text.split("|")[1]
                    break

            # Submit solution
            form = soup.find("form")
            form_data = {inp.get("name"): inp.get("value", "")
                        for inp in form.find_all("input") if inp.get("name")}
            form_data["field-keywords"] = solution

            action = form.get("action", url)
            resp = session.post(action, data=form_data)

    # Parse product data
    soup = BeautifulSoup(resp.text, "html.parser")
    title = soup.find("span", {"id": "productTitle"})
    price = soup.find("span", class_="a-price-whole")

    return {
        "title": title.text.strip() if title else None,
        "price": price.text.strip() if price else None
    }

product = scrape_amazon_product("https://www.amazon.com/dp/B0EXAMPLE")
print(product)

Réduire la fréquence des défis CAPTCHA

Le défi le moins coûteux reste celui qui n'apparaît pas. Cinq réglages font l'essentiel :

  1. Proxys résidentiels – les plages datacenter sont repérées en premier.
  2. Rotation des User-Agent – un pool de chaînes réalistes, cohérentes avec vos en-têtes.
  3. Sessions persistantes – gardez les cookies d'un même worker au lieu d'ouvrir une session par page.
  4. Délais variables – 3 à 10 s entre deux requêtes, avec un peu d'aléa.
  5. Accept-Language explicite – une locale absente est un signal facile à repérer.

Sixième règle, non négociable : restez sur les pages produit publiques. Rien qui exige une connexion n'entre dans le périmètre de ce guide.

Coût : combien de threads pour un crawl e-commerce

La facturation CaptchaAI se fait au thread simultané, avec un nombre de résolutions illimité par thread sur le mois. Ce qui compte n'est pas votre volume mensuel, mais le nombre de défis à résoudre en même temps au pic.

Profil de crawl Plan indicatif
Un ou deux workers, quelques centaines de pages/jour BASIC ($15/mois, 5 threads)
Plusieurs enseignes en parallèle STANDARD ($30/mois, 15 threads)
Veille tarifaire à grande échelle ADVANCE ($90/mois, 50 threads)

Au-dessus, PREMIUM ($170/mois, 100 threads), CORPORATE ($240/mois, 150 threads), ENTERPRISE ($300/mois, 200 threads), VIP-1 ($1,500/mois), VIP-2 ($4,500/mois) et VIP-3 ($7,500/mois) suivent la même logique : plus de threads, jamais de facturation à la résolution, toujours en dollars US.

Dépannage

Problème Cause probable Correctif
Un défi à chaque requête Réputation IP ou cadence trop élevée Proxys résidentiels, et un peu d'aléa entre les requêtes
Chaîne OCR refusée Image téléchargée hors session, champs cachés perdus Téléchargez l'image avec la session courante, reconstruisez les input
Boucles de redirection Cookies non conservés Un seul objet Session, allow_redirects=True
Données produit vides, sans défi visible Variante de mise en page servie par le site Vérifiez vos sélecteurs sur le HTML réellement reçu
CAPCHA_NOT_READY jusqu'au timeout Polling trop rapproché ou image illisible Espacez l'interrogation, vérifiez le base64 envoyé

FAQ

Combien de threads faut-il pour un crawl de catalogue quotidien ?

Comptez en simultané, pas en volume mensuel. Un pipeline séquentiel de quelques centaines de pages tient sur BASIC ($15/mois, 5 threads) : seul le parallélisme au pic justifie de monter en gamme.

Pourquoi le formulaire refuse-t-il ma chaîne résolue ?

Presque toujours parce que le contexte a été perdu : image téléchargée hors session, champs cachés non repris, ou action postée en chemin relatif. Rejouez l'étape 3 avant de mettre en cause la résolution.

CaptchaAI prend-il en charge hCaptcha si un site marchand en affiche un ?

Non — hCaptcha n'est pas pris en charge, pas plus que FunCaptcha (Arkose Labs) ; GeeTest v4 est annoncé comme à venir. Sont couverts : CAPTCHA image/OCR, grilles d'images, reCAPTCHA v2 et v3, Cloudflare Turnstile, Cloudflare Challenge et GeeTest v3, plus CaptchaFox (bêta), Friendly Captcha (bêta) et Lemin (bêta).

Que faut-il vérifier côté RGPD sur une veille tarifaire ?

Restez sur des données produit publiques, sans contenus nominatifs (avis signés compris). Journalisez les URL et les horodatages, pas les identités, et validez vos obligations avec votre référent conformité.

Guides connexes

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