API Tutorials

Types CAPTCHA personnalisés : soumission de défis inhabituels à CaptchaAI

Un curseur à faire glisser, une image à pivoter, un puzzle, un widget JavaScript maison : ces défis n'ont aucune méthode dédiée dans une API de résolution. Une seule technique les couvre pourtant presque tous — capturer le défi sous forme d'image et l'envoyer à l'endpoint image/OCR de CaptchaAI avec des instructions en texte clair. Le solveur renvoie une valeur brute (position en pixels, angle, ordre de clics, transcription) que votre script n'a plus qu'à traduire en action dans le navigateur.

Périmètre : ces techniques servent à automatiser vos propres tests QA et vos environnements autorisés. Cadrez la capture sur le seul widget concerné et vérifiez vos obligations RGPD sur la minimisation des données.


La méthode qui couvre presque tout

Le principe tient en un appel : envoyez l'image encodée en base64 à in.php avec un champ textinstructions qui décrit précisément la réponse attendue, puis interrogez res.php jusqu'à obtenir le résultat.

import requests
import base64
import time
import os

API_KEY = os.environ["CAPTCHAAI_API_KEY"]


def solve_custom_captcha(image_b64, instructions):
    """Solve any visual CAPTCHA using image + text instructions."""
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY,
        "method": "base64",
        "body": image_b64,
        "textinstructions": instructions,
        "json": 1,
    }, timeout=30)

    result = resp.json()
    if result.get("status") != 1:
        raise RuntimeError(result.get("request"))

    task_id = result["request"]

    time.sleep(10)
    for _ in range(30):
        resp = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get",
            "id": task_id, "json": 1,
        }, timeout=15)
        data = resp.json()
        if data.get("status") == 1:
            return data["request"]
        if data["request"] != "CAPCHA_NOT_READY":
            raise RuntimeError(data["request"])
        time.sleep(5)

    raise TimeoutError("Solve timeout")

Trois étapes suffisent à comprendre le flux :

  1. La première requête renvoie un task_id.
  2. La boucle interroge le résultat toutes les cinq secondes.
  3. La valeur CAPCHA_NOT_READY signifie « pas encore prêt » ; toute autre valeur est une erreur à remonter.

Cette fonction sert de socle à tous les cas particuliers ci-dessous : seul le texte des instructions change.


Reconnaître le format en un coup d'œil

Avant d'écrire du code, identifiez la famille du défi. Le tableau relie chaque format inhabituel à la méthode la plus adaptée.

Type de défi Ce que voit l'utilisateur Méthode côté API
CAPTCHA à curseur Faire glisser jusqu'à une position Capture d'écran + instructions textuelles
Puzzle (pièce à emboîter) Glisser la pièce au bon endroit Peut relever d'une résolution de type GeeTest v3
CAPTCHA audio Écouter puis saisir Envoyer le fichier audio encodé
Rotation d'image Pivoter jusqu'à la bonne orientation Capture d'écran + instructions
Ordre de sélection Cliquer les éléments dans l'ordre Approche grille d'images
Équation mathématique Résoudre un calcul Paramètre calc=1
Widget interactif sur mesure Composant JS propre au site Capture d'écran + instructions textuelles

Écrire des instructions exploitables

La qualité de la réponse dépend presque entièrement du texte des instructions. Quatre règles suffisent :

  1. Imposez le format de sortie : « Renvoyez uniquement le nombre ».
  2. Donnez le repère spatial : « de gauche à droite, de haut en bas ».
  3. Posez une seule question par appel ; découpez si besoin.
  4. Cadrez la capture au plus près du défi, sans le reste de la page.

CAPTCHA à curseur

Un curseur demande de faire glisser une poignée jusqu'à une position précise : capturez le composant, demandez l'offset horizontal en pixels, puis rejouez le déplacement avec ActionChains.

# slider_captcha.py
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains


def solve_slider_captcha(driver, captcha_selector):
    """Screenshot slider CAPTCHA and solve via CaptchaAI."""
    captcha = driver.find_element(By.CSS_SELECTOR, captcha_selector)
    image_b64 = captcha.screenshot_as_base64

    result = solve_custom_captcha(
        image_b64,
        "What pixel position should the slider be dragged to? "
        "Return only the X offset number."
    )

    try:
        offset = int(result)
    except ValueError:
        return False

    # Drag slider to position
    slider = driver.find_element(By.CSS_SELECTOR, ".slider-handle")
    ActionChains(driver).click_and_hold(slider).move_by_offset(offset, 0).release().perform()

    return True

Rotation d'image

# rotation_captcha.py


def solve_rotation_captcha(driver, captcha_selector):
    """Solve rotation CAPTCHA."""
    captcha = driver.find_element(By.CSS_SELECTOR, captcha_selector)
    image_b64 = captcha.screenshot_as_base64

    result = solve_custom_captcha(
        image_b64,
        "How many degrees should this image be rotated clockwise "
        "to be in the correct upright orientation? Return only the number."
    )

    try:
        degrees = int(result)
    except ValueError:
        return False

    # Click rotation button the correct number of times
    rotate_btn = driver.find_element(By.CSS_SELECTOR, ".rotate-button")
    clicks = degrees // 90  # Each click rotates 90 degrees

    for _ in range(clicks):
        rotate_btn.click()
        time.sleep(0.3)

    return True

Demandez l'angle en degrés dans le sens horaire, puis cliquez le bon nombre de fois. Adaptez le diviseur // 90 si le widget tourne par pas de 45 degrés.


Ordre de sélection

Demandez au solveur une liste de positions numérotées à partir de 1, puis cliquez les éléments dans cet ordre.

# order_captcha.py


def solve_order_captcha(driver, captcha_selector, item_selector):
    """Solve click-in-order CAPTCHA."""
    captcha = driver.find_element(By.CSS_SELECTOR, captcha_selector)
    image_b64 = captcha.screenshot_as_base64

    result = solve_custom_captcha(
        image_b64,
        "What is the correct order? Return as comma-separated "
        "numbers (1-indexed) representing positions left-to-right, top-to-bottom."
    )

    # Parse order
    try:
        order = [int(x.strip()) for x in result.split(",")]
    except ValueError:
        return False

    # Click items in order
    items = driver.find_elements(By.CSS_SELECTOR, item_selector)
    for idx in order:
        if 1 <= idx <= len(items):
            items[idx - 1].click()
            time.sleep(0.5)

    return True

Variante audio

# audio_captcha.py
import requests


def solve_audio_captcha(audio_url):
    """Download and solve an audio CAPTCHA."""
    # Download audio
    resp = requests.get(audio_url, timeout=30)
    audio_b64 = base64.b64encode(resp.content).decode("ascii")

    # Submit as image with instructions
    # CaptchaAI may support audio via the base64 method
    result = solve_custom_captcha(
        audio_b64,
        "This is an audio CAPTCHA. Transcribe the spoken characters."
    )
    return result

Beaucoup de formulaires exposent une alternative audio pour l'accessibilité. Téléchargez le fichier, encodez-le en base64 et demandez une transcription. Vérifiez la réponse plutôt que de la supposer valide.


Widget entièrement inconnu

Face à un composant maison, restez générique : capturez tout le widget, reprenez les consignes affichées et injectez la réponse dans le premier champ trouvé.

# custom_widget.py
from selenium import webdriver
from selenium.webdriver.common.by import By


def handle_custom_widget(driver, widget_selector):
    """Handle an unknown custom CAPTCHA widget."""

    # Step 1: Screenshot the entire widget
    widget = driver.find_element(By.CSS_SELECTOR, widget_selector)
    image_b64 = widget.screenshot_as_base64

    # Step 2: Get any visible instructions
    try:
        instructions_el = widget.find_element(By.CSS_SELECTOR, ".instructions, .prompt, p")
        visible_instructions = instructions_el.text
    except Exception:
        visible_instructions = "Solve this CAPTCHA"

    # Step 3: Submit with descriptive instructions
    result = solve_custom_captcha(
        image_b64,
        f"CAPTCHA instructions: {visible_instructions}. "
        f"Return the answer text."
    )

    # Step 4: Try to submit result
    try:
        input_el = widget.find_element(By.CSS_SELECTOR, "input")
        input_el.clear()
        input_el.send_keys(result)
    except Exception:
        # No input — try clicking based on result
        driver.execute_script("""
            var input = document.querySelector('input[name*="captcha"]');
            if (input) input.value = arguments[0];
        """, result)

    return result

Détection automatique du type

# detector.py
import re


def detect_captcha_type(page_html):
    """Detect which CAPTCHA type is on a page."""
    checks = {
        "recaptcha_v2": r'data-sitekey.*g-recaptcha',
        "recaptcha_v3": r'recaptcha/api\.js\?render=',
        "turnstile": r'cf-turnstile|challenges\.cloudflare\.com/turnstile',
        "geetest": r'gt\b.*challenge|geetest',
        "bls": r'method.*bls|bls-captcha',
        "image_text": r'captcha.*\.(png|jpg|gif|jpeg)',
        "slider": r'slider.*captcha|slide.*verify',
        "audio": r'audio.*captcha|captcha.*audio',
    }

    detected = []
    for captcha_type, pattern in checks.items():
        if re.search(pattern, page_html, re.IGNORECASE):
            detected.append(captcha_type)

    return detected if detected else ["unknown"]

Quand vos cibles varient, aiguillez chaque page vers la bonne fonction plutôt que de coder un format en dur. Une fois le type identifié, redirigez vers la méthode dédiée (reCAPTCHA, Turnstile, GeeTest v3, BLS) ou, à défaut, vers solve_custom_captcha.


Coût et débit

Les CAPTCHA personnalisés passent par la même méthode image/OCR que les images classiques : ils sont facturés au thread concurrent, jamais à la résolution. Chaque défi en cours occupe un thread ; dès qu'il se termine, le thread reprend le suivant. Le plan d'entrée BASIC ($15/mois, 5 threads) traite donc cinq défis en parallèle sans surcoût par résolution ; montez en gamme selon le débit voulu. La facturation reste en dollars US, jamais convertie en euros.


Dépannage

Problème Cause probable Correctif
ERROR_CAPTCHA_UNSOLVABLE Image floue ou instructions vagues Améliorez la netteté de la capture et précisez les instructions
Format de réponse inattendu Le solveur a renvoyé une description au lieu d'une valeur Contraignez la sortie : « Renvoyez uniquement le nombre »
Widget non capturé Élément hors de la zone visible Faites défiler jusqu'à l'élément avant la capture d'écran
L'interaction échoue Coordonnées de clic erronées Reliez soigneusement la réponse aux éléments réels de l'interface

FAQ

Quels formats de CAPTCHA cette méthode couvre-t-elle ?

Les défis visuels sans méthode dédiée : curseur, rotation, ordre de clics, widgets maison. Pour les familles prises en charge nativement — reCAPTCHA v2/v3, Cloudflare Turnstile et Challenge, GeeTest v3, image/OCR, grilles d'images, BLS — utilisez plutôt leur méthode attitrée.

Comment obtenir une réponse directement exploitable ?

Contraignez le format dans le champ textinstructions (« Renvoyez uniquement le nombre »), posez une seule question par appel et donnez le repère spatial. Un try/except autour de la conversion protège le script si la réponse reste inattendue.

Un curseur relève-t-il de cette méthode ou de GeeTest v3 ?

Cela dépend de son origine. Si le curseur est un défi GeeTest v3, passez par la méthode geetest dédiée, prise en charge nativement. S'il s'agit d'un composant maison sans équivalent, la capture d'écran assortie d'instructions reste la bonne approche.

Capturer un CAPTCHA pose-t-il un problème RGPD ?

La capture elle-même est anodine, mais elle peut inclure des données affichées autour du widget. Cadrez au plus près du défi, ne conservez pas les images plus longtemps que nécessaire et vérifiez vos obligations de minimisation.


Guides connexes


Résolvez vos CAPTCHA les plus atypiques — démarrez avec CaptchaAI.

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