API Tutorials

Résoudre automatiquement un CAPTCHA en grille d'images

Pour résoudre une grille d'images par programmation, vous envoyez l'image complète à l'API CaptchaAI avec method=post et le paramètre recaptcha=1, puis vous rejouez les cellules renvoyées — par index ou par coordonnées. C'est la voie à suivre pour les grilles personnalisées que l'on croise sur les formulaires de connexion, les tunnels d'inscription ou les portails métier, et qui ne dépendent pas du système de Google.

Une grille d'images affiche une grande image découpée en cases (souvent 3 × 3 ou 4 × 4) et vous demande de cliquer sur celles qui correspondent à une consigne. reCAPTCHA emploie ce format, mais de nombreux sites déploient leur propre grille maison. Ce guide couvre précisément ces défis non-reCAPTCHA, de la capture de l'image jusqu'au clic automatique.


Grille personnalisée ou reCAPTCHA : comment les distinguer

Avant de coder, identifiez à quel type de grille vous avez affaire, car la méthode d'API n'est pas la même :

  • Grille personnalisée (image statique) — une seule image, aucune iframe google.com/recaptcha, les cases ne se rechargent pas après un clic. C'est le cas traité ici : method=post avec recaptcha=1.
  • Grille reCAPTCHA — chargée depuis une iframe Google, avec un sitekey et parfois des vignettes qui se remplacent après sélection. Passez alors par la résolution par token (method=userrecaptcha) plutôt que par l'endpoint image décrit ici.

En cas de doute, inspectez le DOM : la présence d'un attribut data-sitekey ou d'une iframe reCAPTCHA tranche immédiatement.


Prérequis

Élément Valeur
Clé API CaptchaAI Depuis votre tableau de bord CaptchaAI
Image de la grille Capture d'écran ou base64 de la grille complète
Environnement Python 3.7+ ou Node.js 14+

CaptchaAI facture au thread, jamais au défi résolu : le plan BASIC ($15/mois, 5 threads) suffit pour démarrer, avec un nombre de résolutions illimité sur la durée du mois.


Étape 1 : capturer l'image de la grille

Deux approches, selon la façon dont le site sert l'image. Prenez une capture nette et en pleine résolution : la qualité de l'image conditionne directement le résultat.

Méthode A : capture d'écran de l'élément captcha

Ciblez le conteneur du CAPTCHA et prenez-en une capture isolée, sans le reste de la page.

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://example.com/protected-form")

# Screenshot just the captcha container
captcha_element = driver.find_element(By.CSS_SELECTOR, "#captcha-container")
captcha_element.screenshot("captcha_grid.png")

Méthode B : extraire l'image depuis l'attribut src

Quand l'image est déjà présente dans le DOM, récupérez-la directement — en base64 si elle est encodée en data:image, sinon par un téléchargement HTTP.

import base64
import requests

captcha_img = driver.find_element(By.CSS_SELECTOR, ".grid-captcha img")
src = captcha_img.get_attribute("src")

if src.startswith("data:image"):
    image_b64 = src.split(",")[1]
else:
    image_data = requests.get(src).content
    image_b64 = base64.b64encode(image_data).decode()

Étape 2 : envoyer l'image à CaptchaAI

L'endpoint d'envoi est in.php. Vous pouvez transmettre soit un fichier, soit une chaîne base64. Dans les deux cas, recaptcha=1 signale une grille à cliquer plutôt qu'un simple OCR de texte.

Envoi par fichier (Python)

Le plus direct quand vous avez enregistré la capture sur disque à l'étape précédente.

import requests
import time

API_KEY = "YOUR_API_KEY"

with open("captcha_grid.png", "rb") as f:
    response = requests.post("https://ocr.captchaai.com/in.php",
        data={
            "key": API_KEY,
            "method": "post",
            "recaptcha": 1,
            "json": 1
        },
        files={"file": f}
    )

data = response.json()
task_id = data["request"]
print(f"Task: {task_id}")

Envoi en base64 (Python)

Pratique quand vous avez extrait l'image directement du DOM, sans fichier intermédiaire.

response = requests.post("https://ocr.captchaai.com/in.php", data={
    "key": API_KEY,
    "method": "post",
    "body": image_b64,
    "recaptcha": 1,
    "json": 1
})

task_id = response.json()["request"]

Node.js

const axios = require('axios');
const fs = require('fs');

async function submitGridCaptcha(imagePath) {
  const imageB64 = fs.readFileSync(imagePath).toString('base64');

  const { data } = await axios.post('https://ocr.captchaai.com/in.php', null, {
    params: {
      key: 'YOUR_API_KEY',
      method: 'post',
      body: imageB64,
      recaptcha: 1,
      json: 1
    }
  });

  return data.request;
}

L'envoi renvoie un identifiant de tâche (task_id) que vous utiliserez ensuite pour récupérer la solution.


Étape 3 : interroger le résultat

La résolution n'est pas instantanée. Interrogez régulièrement res.php avec l'identifiant de tâche jusqu'à obtenir status = 1, en respectant CAPCHA_NOT_READY tant que le travail est en cours. La boucle ci-dessous laisse jusqu'à environ deux minutes et demie avant d'abandonner.

def get_grid_solution(task_id):
    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,
            "json": 1
        }).json()

        if result.get("status") == 1:
            return result["request"]
        if result.get("request") != "CAPCHA_NOT_READY":
            raise Exception(f"Error: {result['request']}")

    raise Exception("Timeout")

solution = get_grid_solution(task_id)
print(f"Solution: {solution}")
# Returns click coordinates or cell indices

Étape 4 : appliquer la solution

La réponse arrive sous l'une de deux formes : des index de cellules (2,5,6) ou des coordonnées de clic (x=120,y=80;x=250,y=200). Testez le format renvoyé et rejouez les clics en conséquence.

Clic par index de cellule

Quand la réponse liste des numéros de cases, cliquez-les dans l'ordre, puis validez.

# If solution returns cell indices (e.g., "2,5,6")
selected = [int(i) for i in solution.split(",")]
cells = driver.find_elements(By.CSS_SELECTOR, ".grid-cell")

for idx in selected:
    cells[idx - 1].click()
    time.sleep(0.2)

driver.find_element(By.CSS_SELECTOR, ".verify-button").click()

Clic par coordonnées

Quand la réponse contient des positions en pixels, utilisez ActionChains avec un décalage relatif au conteneur.

from selenium.webdriver.common.action_chains import ActionChains

# If solution returns coordinates (e.g., "x=120,y=80;x=250,y=200")
captcha_element = driver.find_element(By.CSS_SELECTOR, "#captcha-container")
actions = ActionChains(driver)

for coord in solution.split(";"):
    parts = dict(p.split("=") for p in coord.split(","))
    x, y = int(parts["x"]), int(parts["y"])
    actions.move_to_element_with_offset(captcha_element, x, y).click()

actions.perform()

Dépannage

Erreur Cause Correctif
ERROR_WRONG_FILE_EXTENSION Format d'image invalide Utilisez du PNG ou du JPEG ; vérifiez que la base64 est valide
ERROR_CAPTCHA_UNSOLVABLE Image trop petite ou floue Capturez en pleine résolution, sans redimensionnement destructeur
Mauvaises cellules sélectionnées Format de solution mal interprété Vérifiez si la réponse est en index ou en coordonnées
ERROR_TOO_BIG_CAPTCHA_FILESIZE L'image dépasse la limite de taille Redimensionnez sous 600 Ko

Bonnes pratiques et fiabilité

Quelques réflexes évitent la majorité des échecs en production :

  • Cadrez la grille au pixel près. Une marge parasite ou un bandeau de consigne mal découpé fausse l'analyse. Capturez le conteneur exact du CAPTCHA, pas toute la page.
  • Restez sous 600 Ko tout en gardant une image lisible : c'est le compromis qui limite à la fois ERROR_TOO_BIG_CAPTCHA_FILESIZE et ERROR_CAPTCHA_UNSOLVABLE.
  • Prévoyez une nouvelle tentative. Si le premier appel renvoie une grille invalide, rechargez le défi et relancez la boucle plutôt que d'insister sur la même image.
  • RGPD : si votre flux traverse un formulaire contenant des données personnelles, minimisez ce que vous journalisez — ne conservez pas les captures d'écran plus longtemps que nécessaire et vérifiez vos obligations RGPD avant toute collecte.

Côté volume, comme la facturation se fait par thread et non à la résolution, un pipeline de scraping web qui traite des grilles en parallèle reste prévisible : vous dimensionnez le nombre de threads, pas une facture au coup par coup.


Exemple entièrement exécutable

Besoin d'un projet complet et prêt à lancer, avec configuration de l'environnement, interrogation du résultat, nouvelles tentatives et gestion des erreurs ?

Voir l'exemple exécutable complet sur GitHub →


FAQ

Les questions qui reviennent le plus souvent lors de l'intégration d'une grille d'images.

Grille ou token : quelle méthode choisir ?

Réservez la résolution par token (method=userrecaptcha) aux vrais défis reCAPTCHA : elle est plus simple et plus fiable. Passez par la grille (method=post avec recaptcha=1) uniquement pour les grilles maison non-reCAPTCHA ou les images à cliquer autonomes.

Sous quelle forme l'API renvoie-t-elle la réponse ?

Sous deux formes possibles : une liste d'index de cellules (2,5,6) ou une suite de coordonnées de clic (x=120,y=80;...). Détectez le format dans votre code et rejouez les clics soit par index, soit avec ActionChains, comme à l'étape 4.

Combien coûte la résolution de grilles ?

La facturation est basée sur les threads, pas sur le nombre de résolutions. Le plan BASIC ($15/mois, 5 threads) offre des résolutions illimitées pour un usage modéré ; montez en gamme (STANDARD à $30/mois, 15 threads, et au-delà) quand vous devez traiter davantage de grilles en parallèle.

Comment réduire les erreurs ERROR_CAPTCHA_UNSOLVABLE ?

Elles viennent presque toujours d'une image trop petite, floue ou mal cadrée. Capturez la grille en pleine résolution, évitez le sur-redimensionnement et assurez-vous que la consigne et toutes les cases sont bien dans le cadre avant l'envoi.


Guides associés

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