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=postavecrecaptcha=1. - Grille reCAPTCHA — chargée depuis une iframe Google, avec un
sitekeyet 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_FILESIZEetERROR_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.