Un moteur OCR ne renvoie que ce qu'il parvient à lire dans les pixels reçus. Quand CaptchaAI retourne une réponse erronée sur un CAPTCHA d'image, la cause se situe presque toujours en amont — dans l'image envoyée — et non dans la résolution. Toutes ces causes se corrigent côté client, dans l'ordre ci-dessous.
Règle de diagnostic : avant de suspecter le moteur, rejouez l'envoi avec le fichier exact que vous transmettez. Neuf fois sur dix, le défaut se voit à l'œil nu dans cette image.
Où se cache la mauvaise réponse : les causes fréquentes
| Cause | Fréquence | Correctif |
|---|---|---|
| Image mal recadrée | Très fréquent | Capturez l'élément CAPTCHA en entier |
| Basse résolution ou compression | Fréquent | Envoyez une image de meilleure qualité |
| Encodage d'image incorrect | Fréquent | Vérifiez l'encodage base64 |
| Indice de langue ou de type absent | Occasionnel | Ajoutez language ou textinstructions |
| Image périmée ou expirée | Occasionnel | Recapturez l'image juste avant la résolution |
Les deux premières lignes concentrent la majorité des tickets. Traitez les causes dans cet ordre, du plus fréquent au plus rare :
- Cadrage et capture de l'élément (correctifs 1 et 5) — la source d'erreur numéro un.
- Encodage base64 (correctif 2) — invisible tant qu'on ne vérifie pas l'aller-retour.
- Qualité et prétraitement (correctif 3) — pour les images pâles ou minuscules.
- Indices de type et de langue (correctif 4) — chiffres contre lettres, casse, longueur.
- Fraîcheur de l'image (correctif 6) — pour les CAPTCHA qui expirent vite.
Remontez cette liste plutôt que de tout tenter en même temps : dans neuf cas sur dix, le défaut se règle dès les deux premières étapes.
Correctif 1 : validez la qualité de l'image avant l'envoi
Filtrez l'image localement d'abord : ce contrôle repère les images trop petites, quasi blanches ou trop lourdes (plus de 600 Ko).
import base64
from io import BytesIO
from PIL import Image
def validate_captcha_image(image_path):
"""Check image quality before submitting to CaptchaAI."""
img = Image.open(image_path)
width, height = img.size
issues = []
# Minimum resolution
if width < 50 or height < 20:
issues.append(f"Too small: {width}x{height}px (min 50x20)")
# Check if mostly blank
pixels = list(img.getdata())
if img.mode == "RGB":
white_count = sum(1 for p in pixels if p[0] > 250 and p[1] > 250 and p[2] > 250)
else:
white_count = sum(1 for p in pixels if p > 250)
blank_ratio = white_count / len(pixels)
if blank_ratio > 0.95:
issues.append(f"Image appears blank ({blank_ratio:.0%} white)")
# File size check
img_bytes = BytesIO()
img.save(img_bytes, format="PNG")
size_kb = img_bytes.tell() / 1024
if size_kb < 1:
issues.append(f"File too small ({size_kb:.1f} KB) — may be empty")
if size_kb > 600:
issues.append(f"File too large ({size_kb:.0f} KB) — submit under 600 KB")
return issues
issues = validate_captcha_image("captcha.png")
if issues:
for issue in issues:
print(f"WARNING: {issue}")
else:
print("Image quality OK")
Correctif 2 : fiabilisez l'encodage base64
Un charabia en sortie trahit presque toujours un encodage cassé — souvent parce qu'on encode le chemin du fichier plutôt que son contenu.
import base64
def encode_captcha(image_path):
"""Properly encode a CAPTCHA image to base64."""
with open(image_path, "rb") as f:
raw = f.read()
encoded = base64.b64encode(raw).decode("ascii")
# Verify round-trip
decoded = base64.b64decode(encoded)
assert decoded == raw, "Base64 encoding corrupted the image"
return encoded
# WRONG — encoding a file path string
bad = base64.b64encode(b"captcha.png").decode() # Encodes filename, not image!
# CORRECT — encoding file contents
with open("captcha.png", "rb") as f:
good = base64.b64encode(f.read()).decode()
Correctif 3 : prétraitez l'image pour l'OCR
Une réponse proche mais fausse signale une image trop petite ou trop pâle : mettez-la à l'échelle, montez le contraste, renforcez la netteté. Une image standard passe très bien sans retouche.
Ne prétraitez que si l'image le justifie, car une retouche appliquée à une image déjà nette peut la dégrader :
- Oui : image minuscule, contraste faible, aliasing visible ou compression JPEG agressive.
- Non : image nette, bien contrastée, capturée directement depuis l'élément — laissez-la telle quelle.
from PIL import Image, ImageFilter, ImageEnhance
from io import BytesIO
import base64
def preprocess_captcha(image_path):
"""Improve image quality for better OCR accuracy."""
img = Image.open(image_path)
# Convert to RGB if needed
if img.mode != "RGB":
img = img.convert("RGB")
# Upscale small images
width, height = img.size
if width < 200:
scale = 200 / width
img = img.resize(
(int(width * scale), int(height * scale)),
Image.LANCZOS,
)
# Increase contrast
enhancer = ImageEnhance.Contrast(img)
img = enhancer.enhance(1.5)
# Sharpen
img = img.filter(ImageFilter.SHARPEN)
# Convert to PNG bytes
buffer = BytesIO()
img.save(buffer, format="PNG")
return base64.b64encode(buffer.getvalue()).decode()
Correctif 4 : précisez le type et la langue attendus
Un CAPTCHA à quatre chiffres traité comme du texte rend des lettres au lieu de chiffres. Chaque paramètre cadre un aspect précis de la réponse attendue :
numeric— restreint la sortie aux chiffres (1) ou aux lettres (2).min_len/max_len— bornent le nombre de caractères, utile quand la longueur est connue.language—1pour le cyrillique,2pour le latin ; laissez0par défaut sinon.textinstructions— une consigne libre (casse exacte, caractères accentués) transmise avec l'image.
Renseignez seulement les indices dont vous êtes sûr : un max_len erroné tronque une réponse correcte.
import requests
def solve_image(api_key, image_base64, **hints):
"""Submit image CAPTCHA with quality hints."""
data = {
"key": api_key,
"method": "base64",
"body": image_base64,
"json": 1,
}
# Add optional hints for better accuracy
if "language" in hints:
data["language"] = hints["language"] # 0=default, 1=Cyrillic, 2=Latin
if "textinstructions" in hints:
data["textinstructions"] = hints["textinstructions"]
if "numeric" in hints:
data["numeric"] = hints["numeric"] # 1=digits only, 2=letters only
if "min_len" in hints:
data["min_len"] = hints["min_len"]
if "max_len" in hints:
data["max_len"] = hints["max_len"]
resp = requests.post("https://ocr.captchaai.com/in.php", data=data, timeout=30)
return resp.json()
# Example: Digits-only CAPTCHA, 4-6 characters
result = solve_image(
"YOUR_API_KEY",
encoded_image,
numeric=1,
min_len=4,
max_len=6,
)
# Example: Case-sensitive text
result = solve_image(
"YOUR_API_KEY",
encoded_image,
textinstructions="Case-sensitive, enter exactly as shown",
)
Correctif 5 : capturez uniquement l'élément CAPTCHA
Une capture de page recadrée à la main introduit marges, décalage et parfois un caractère coupé. Ciblez directement l'élément : la capture est nette et cadrée au pixel près.
from selenium import webdriver
from selenium.webdriver.common.by import By
import base64
def capture_captcha_element(driver, selector):
"""Screenshot only the CAPTCHA element, not the full page."""
element = driver.find_element(By.CSS_SELECTOR, selector)
# Element screenshot (better than page crop)
png_bytes = element.screenshot_as_png
# Verify it's not empty
if len(png_bytes) < 500:
raise ValueError("Screenshot too small — element may not be visible")
return base64.b64encode(png_bytes).decode()
# Usage
driver = webdriver.Chrome()
driver.get("https://example.com")
image_b64 = capture_captcha_element(driver, "img#captchaImage")
Exemple côté production. Sur une VM headless (OVHcloud ou Scaleway), le code qui marche en local renvoie parfois des réponses fausses : le navigateur rend le CAPTCHA à une densité de pixels plus basse et l'image part trop petite. Ce cadrage serré a un bonus RGPD : il évite d'embarquer des données personnelles affichées ailleurs.
Correctif 6 : gérez les CAPTCHA dynamiques ou tournants
Certains CAPTCHA d'image expirent en quelques secondes ou changent à chaque affichage. Capturez et envoyez l'image dans la même foulée, sans la laisser « vieillir ».
import time
def solve_with_fresh_image(driver, api_key, captcha_selector):
"""Capture and solve CAPTCHA immediately to avoid expiry."""
# Wait for CAPTCHA to load fully
time.sleep(2)
# Capture fresh
element = driver.find_element(By.CSS_SELECTOR, captcha_selector)
png_bytes = element.screenshot_as_png
body = base64.b64encode(png_bytes).decode()
# Submit immediately
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": api_key,
"method": "base64",
"body": body,
"json": 1,
}, timeout=30)
result = resp.json()
if result.get("status") != 1:
raise RuntimeError(result.get("request"))
task_id = result["request"]
# Poll — image CAPTCHAs solve fast
time.sleep(5)
for _ in range(12):
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(3)
raise TimeoutError("Image solve timeout")
Tableau de dépannage express
| Symptôme | Diagnostic | Correctif |
|---|---|---|
| Réponse incohérente (charabia) | Encodage base64 incorrect | Vérifiez l'encodage aller-retour |
| Réponse proche mais fausse | Qualité d'image insuffisante | Prétraitez : mise à l'échelle, netteté, contraste |
| Nombre de caractères erroné | Indices de longueur absents | Ajoutez les paramètres min_len / max_len |
| Lettres et chiffres mélangés | Indice de type absent | Ajoutez numeric=1 ou numeric=2 |
| Réponse vide | Image blanche ou corrompue | Validez l'image avant l'envoi |
| Bonne réponse mais refus du site | Sensibilité à la casse | Ajoutez textinstructions pour la casse |
FAQ
Pourquoi l'OCR renvoie-t-il le bon nombre de caractères mais des lettres fausses ?
L'image est lisible mais trop pâle ou trop petite pour distinguer des caractères proches (0/O, 1/l, 5/S). Le correctif 3 — mise à l'échelle, contraste et netteté — suffit dans ce cas.
Le prétraitement d'image est-il toujours nécessaire ?
Non. CaptchaAI traite très bien les images standard sans retouche. Réservez le prétraitement aux cas limites : images minuscules, faible contraste ou compression agressive.
CaptchaAI gère-t-il les CAPTCHA d'image sensibles à la casse ?
Oui, mais c'est votre site cible qui impose la casse exacte. Passez textinstructions pour que la réponse respecte majuscules et minuscules telles qu'affichées.
Combien de temps une image CAPTCHA reste-t-elle valide ?
Cela dépend du site : certaines expirent en quelques secondes. Capturez et envoyez l'image immédiatement (correctif 6) plutôt que de la stocker.
Pourquoi une réponse correcte est-elle refusée par le site cible ?
Le moteur a bien lu l'image, mais le formulaire attend un format précis. Vérifiez la casse avec textinstructions et assurez-vous de ne pas ajouter d'espaces autour de la valeur avant de la soumettre.
Guides connexes
Résolvez vos CAPTCHA d'image avec précision — essayez CaptchaAI.