API Tutorials

Encoder les images CAPTCHA en base64 : les bonnes pratiques

Un CAPTCHA image refusé vient presque toujours du même endroit : un base64 mal formé. Retirez le préfixe data:image/..., lisez le fichier en mode binaire, gardez l'image sous 600 Ko, et l'API CaptchaAI reçoit exactement ce qu'elle attend. Ce guide montre comment encoder proprement une image CAPTCHA — depuis un fichier, une URL ou une capture Selenium — puis comment valider le résultat avant de l'envoyer.

L'encodage base64 transforme des octets binaires en une chaîne de caractères ASCII transportable dans un corps de requête POST. C'est le format attendu par le paramètre method=base64, mais il a un coût : la chaîne encodée pèse environ 33 % de plus que l'image d'origine. Cette marge compte dès que vous approchez de la limite de taille, d'où l'intérêt de redimensionner avant d'encoder plutôt qu'après.


Le format d'envoi en base64

CaptchaAI accepte les CAPTCHA d'images en base64 via le paramètre method=base64. Vous envoyez la chaîne dans le champ body, sans aucun préfixe :

import requests
import base64
import os


def submit_image_captcha(image_base64):
    """Submit base64-encoded image to CaptchaAI."""
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": os.environ["CAPTCHAAI_API_KEY"],
        "method": "base64",
        "body": image_base64,
        "json": 1,
    }, timeout=30)
    return resp.json()

Gardez votre clé API dans une variable d'environnement plutôt qu'en dur dans le code : elle ne doit jamais atterrir dans un dépôt Git ni dans vos logs. La réponse renvoie un identifiant de tâche que vous interrogez ensuite sur res.php pour récupérer le texte résolu.


Encoder depuis un fichier local

Le cas le plus simple : vous avez déjà l'image sur disque. Ouvrez-la en mode binaire (rb), encodez les octets bruts, puis décodez en ascii pour obtenir une chaîne exploitable :

# from_file.py
import base64


def encode_from_file(filepath):
    """Read an image file and return base64 string."""
    with open(filepath, "rb") as f:
        raw = f.read()
    return base64.b64encode(raw).decode("ascii")


# Usage
b64 = encode_from_file("captcha.png")
print(f"Encoded length: {len(b64)} chars")

Le mode="rb" est l'élément clé : en mode texte, Python tenterait de décoder les octets comme du texte et corromprait l'image (voir les erreurs plus bas).


Encoder depuis une URL

Dans un pipeline de scraping, l'image arrive souvent directement depuis une URL. Téléchargez-la, vérifiez que le Content-Type est bien une image, puis encodez le contenu de la réponse :

# from_url.py
import requests
import base64


def encode_from_url(image_url):
    """Download image and return base64 string."""
    resp = requests.get(image_url, timeout=15)
    resp.raise_for_status()

    # Verify it's actually an image
    content_type = resp.headers.get("Content-Type", "")
    if not content_type.startswith("image/"):
        raise ValueError(f"Not an image: {content_type}")

    return base64.b64encode(resp.content).decode("ascii")


# Usage
b64 = encode_from_url("https://example.com/captcha.png")

Le contrôle du Content-Type évite un piège classique : quand la session a expiré, le serveur renvoie souvent une page HTML de connexion à la place de l'image. Sans cette vérification, vous encoderiez du HTML et obtiendriez une erreur côté résolution.


Encoder une capture d'écran Selenium

Quand le CAPTCHA est rendu dans la page et sans URL directe, capturez l'élément avec Selenium. La méthode screenshot_as_base64 renvoie déjà du base64 prêt à l'emploi ; la variante avec recadrage sert quand vous devez isoler une zone précise d'une capture plein écran :

# from_selenium.py
import base64
from selenium.webdriver.common.by import By


def encode_from_element(driver, selector):
    """Screenshot a specific element and return base64."""
    element = driver.find_element(By.CSS_SELECTOR, selector)
    screenshot_b64 = element.screenshot_as_base64
    return screenshot_b64


def encode_from_page_crop(driver, selector):
    """Crop a specific region from the page screenshot."""
    from PIL import Image
    import io

    element = driver.find_element(By.CSS_SELECTOR, selector)
    location = element.location
    size = element.size

    # Full page screenshot
    png = driver.get_screenshot_as_png()
    img = Image.open(io.BytesIO(png))

    # Crop to element bounds
    left = location["x"]
    top = location["y"]
    right = left + size["width"]
    bottom = top + size["height"]
    cropped = img.crop((left, top, right, bottom))

    # Encode
    buffer = io.BytesIO()
    cropped.save(buffer, format="PNG")
    return base64.b64encode(buffer.getvalue()).decode("ascii")

Une capture plein écran peut contenir bien plus que le CAPTCHA — un e-mail affiché, un nom d'utilisateur, un numéro de dossier. Côté RGPD, le réflexe est de ne recadrer que la zone du défi et de ne pas conserver la capture complète : vous limitez les données personnelles transmises et stockées au strict nécessaire.


Les erreurs d'encodage les plus fréquentes

Trois erreurs expliquent la grande majorité des soumissions rejetées. Elles se corrigent en une ligne une fois repérées.

Le préfixe data URI oublié

Une image récupérée depuis un attribut src ou l'API Canvas du navigateur arrive souvent sous forme de data URI (data:image/png;base64,...). CaptchaAI n'attend que la partie base64 : tout ce qui précède la virgule doit être retiré.

# WRONG — includes data URI prefix
bad = "data:image/png;base64,iVBORw0KGgo..."

# RIGHT — raw base64 only
good = "iVBORw0KGgo..."

# Fix: Strip the prefix
def clean_base64(b64_string):
    if "," in b64_string:
        return b64_string.split(",", 1)[1]
    return b64_string

Le double encodage

screenshot_as_base64 renvoie déjà une chaîne base64. La ré-encoder une seconde fois produit une chaîne valide mais illisible pour le solveur — l'erreur est silencieuse, ce qui la rend piégeuse.

# WRONG — encoding an already-encoded string
already_b64 = element.screenshot_as_base64
double_encoded = base64.b64encode(already_b64.encode()).decode()  # BAD

# RIGHT — use as-is
correct = element.screenshot_as_base64  # Already base64

Le texte lu à la place des octets

Ouvrir une image en mode texte ("r") au lieu du mode binaire ("rb") corrompt les octets avant même l'encodage. C'est la cause n°1 des ERROR_ZERO_CAPTCHA_FILESIZE.

# WRONG — reading as text
with open("captcha.png", "r") as f:  # Text mode
    content = f.read()  # Corrupted binary data

# RIGHT — reading as bytes
with open("captcha.png", "rb") as f:  # Binary mode
    content = f.read()
encoded = base64.b64encode(content).decode("ascii")

Valider l'image avant l'envoi

Plutôt que d'attendre le rejet de l'API, validez localement : présence d'un préfixe, base64 décodable, taille cohérente et format reconnu à partir de sa signature d'octets. Une seule fonction couvre tout, et vous évite des allers-retours réseau inutiles :

# validate.py
import base64
import io


def validate_captcha_image(b64_string):
    """Validate base64 image before submitting to CaptchaAI."""
    errors = []

    # Check for data URI prefix
    if b64_string.startswith("data:"):
        errors.append("Contains data URI prefix — strip it")
        b64_string = b64_string.split(",", 1)[1]

    # Try decoding
    try:
        decoded = base64.b64decode(b64_string)
    except Exception as e:
        return {"valid": False, "errors": [f"Invalid base64: {e}"]}

    # Check size
    size_kb = len(decoded) / 1024
    if size_kb < 1:
        errors.append(f"Image too small ({size_kb:.1f} KB) — likely corrupt")
    if size_kb > 500:
        errors.append(f"Image large ({size_kb:.1f} KB) — consider resizing")

    # Check image format
    if decoded[:8] == b'\x89PNG\r\n\x1a\n':
        fmt = "PNG"
    elif decoded[:3] == b'\xff\xd8\xff':
        fmt = "JPEG"
    elif decoded[:4] == b'GIF8':
        fmt = "GIF"
    elif decoded[:4] == b'RIFF':
        fmt = "WEBP"
    else:
        errors.append("Unknown image format")
        fmt = "unknown"

    return {
        "valid": len(errors) == 0,
        "format": fmt,
        "size_kb": round(size_kb, 1),
        "errors": errors,
    }


# Usage
result = validate_captcha_image(b64_string)
if not result["valid"]:
    print(f"Issues: {result['errors']}")
else:
    print(f"Valid {result['format']}, {result['size_kb']} KB")

Pour un pipeline de scraping qui traite plusieurs milliers d'images par heure, ce filtre en amont réduit nettement le bruit dans vos logs. Côté capacité, les offres CaptchaAI sont facturées au thread — par exemple BASIC ($15/mois, 5 threads) — et non à l'image : chaque thread traite une résolution à la fois, puis enchaîne sur la suivante.


Quel format d'image choisir

Format Idéal pour Poids Qualité
PNG CAPTCHA texte, captures d'écran Plus lourd Sans perte
JPEG CAPTCHA basés sur photo Plus léger Avec perte (qualité ≥ 85)
GIF CAPTCHA animés Variable Couleurs limitées
WEBP Navigateurs modernes Le plus léger Bonne qualité

Recommandation : privilégiez le PNG pour les CAPTCHA de texte. Sa compression sans perte préserve les contours des caractères, ce qui aide directement la précision de la résolution OCR.


Dépannage

Problème Cause Correctif
ERROR_WRONG_FILE_EXTENSION Base64 invalide Validez avec validate_captcha_image()
ERROR_TOO_BIG_CAPTCHA_FILESIZE Image de plus de 600 Ko Redimensionnez ou compressez avant d'encoder
ERROR_ZERO_CAPTCHA_FILESIZE Image vide ou corrompue Vérifiez que le téléchargement a réussi
Résolution incorrecte JPEG trop compressé Passez au PNG ou gardez une qualité JPEG ≥ 85

FAQ

Faut-il retirer le préfixe data:image/... avant l'envoi ?

Oui. CaptchaAI n'attend que la chaîne base64 brute. Coupez tout ce qui précède la virgule, comme le fait clean_base64(), sinon la soumission échoue.

Comment corriger l'erreur ERROR_TOO_BIG_CAPTCHA_FILESIZE ?

Votre image dépasse 600 Ko une fois décodée. Redimensionnez ou recompressez-la avant d'encoder — l'encodage augmentant le poids d'environ 33 %, réduire l'image source est plus efficace que de rogner sur le base64.

Le base64 augmente-t-il la taille de ce que j'envoie ?

Oui, d'environ un tiers par rapport au fichier binaire. C'est inhérent au format ; anticipez-le en gardant vos captures compactes plutôt qu'en comptant sur la marge de 600 Ko.

Puis-je envoyer directement une capture Selenium ?

Oui. element.screenshot_as_base64 renvoie déjà du base64 utilisable tel quel. Ne le ré-encodez pas, sous peine de double encodage silencieux.

Puis-je soumettre des images SVG ?

Non. Convertissez d'abord le SVG en PNG avec une bibliothèque comme Pillow ou cairosvg, puis encodez le PNG obtenu.


Guides connexes


Encodez vos CAPTCHA proprement — démarrez avec CaptchaAI.

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