API Tutorials

Práticas recomendadas de codificação de imagem CAPTCHA Base64

A resposta rápida: envie a string base64 pura (sem prefixo data:image/...), leia o arquivo em modo binário e mantenha o resultado abaixo de 600 KB — isso resolve a maioria dos ERROR_WRONG_FILE_EXTENSION ao migrar de outro solucionador para a API da CaptchaAI. Veja como codificar arquivo, URL e screenshot do Selenium.

Os 3 erros que mais geram ERROR_WRONG_FILE_EXTENSION:

  • Enviar a string ainda com o prefixo data:image/... colado
  • Codificar em base64 uma saída que já veio em base64 (screenshot do Selenium)
  • Abrir o arquivo em modo texto ("r") em vez de binário ("rb")

Como a API da CaptchaAI recebe imagens em base64

O endpoint in.php recebe CAPTCHAs de imagem em base64 via method=base64 — sem multipart, sem upload de arquivo:

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()

Passo 1: codificar um arquivo local

Para um arquivo já salvo em disco, leia em modo binário (rb) e codifique direto:

# 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")

Passo 2: codificar a partir de uma URL

Baixe o conteúdo da URL e confira o Content-Type antes de codificar — evita gastar uma requisição com uma página de erro disfarçada:

# 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")

Passo 3: codificar um screenshot do Selenium

Recorte só a área do desafio, nunca a tela inteira — em formulários sob a LGPD isso evita reter dado pessoal em logs. Com workers em sa-east-1 (São Paulo), o round-trip até o in.php já é baixo; não desperdice esse ganho com uma imagem grande.

# 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")

Os 3 erros de codificação mais comuns

Erro 1: manter o prefixo do URI de dados

Alguns navegadores devolvem a imagem já com o prefixo data:image/png;base64,, quebrando a decodificação no servidor. O sintoma do lado da CaptchaAI é sempre o mesmo ERROR_WRONG_FILE_EXTENSION, mesmo com o restante dos bytes da imagem intacto:

# 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

Erro 2: codificar duas vezes

element.screenshot_as_base64 do Selenium já devolve base64 pronto; codificar de novo corrompe os dados. É o erro mais fácil de introduzir sem perceber, porque o código roda sem lançar exceção — só o resultado da resolução vem errado:

# 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

Erro 3: abrir o arquivo em modo texto

Abrir a imagem com "r" em vez de "rb" corrompe os bytes antes mesmo de codificar. No Windows o problema aparece com mais frequência, porque o modo texto também reescreve as quebras de linha dentro do binário:

# 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")

Valide antes de enviar (e economize requisições)

Um envio inválido ainda consome uma requisição e volta com erro. Rode esta checagem antes do POST:

# 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")

PNG, JPEG, GIF ou WEBP: qual formato usar

Formato Uso recomendado Tamanho Qualidade
PNG CAPTCHA de texto Maior Sem perdas
JPEG CAPTCHA de foto Menor Com perdas (qualidade ≥ 85)
GIF CAPTCHA animado Variável Cores limitadas
WEBP Navegadores modernos Menor Boa qualidade

Recomendação prática: use PNG para CAPTCHA de texto — a compressão sem perdas preserva as bordas dos caracteres.

Resumo por caso de uso:

  • CAPTCHA de texto/OCR → PNG (padrão recomendado)
  • CAPTCHA baseado em foto → JPEG com qualidade ≥ 85
  • CAPTCHA animado → GIF, mas confira se só o primeiro frame relevante é processado
  • Pipeline que já usa WEBP → converta para PNG antes de enviar, para não depender de decodificação incerta no servidor

Erros da API e como corrigir

Antes de abrir um ticket de suporte, confira esta lista — a grande maioria dos erros de codificação cai em um destes quatro casos.

  • ERROR_WRONG_FILE_EXTENSION — dados base64 inválidos → valide com validate_captcha_image()
  • ERROR_TOO_BIG_CAPTCHA_FILESIZE — imagem acima de 600 KB → redimensione ou compacte
  • ERROR_ZERO_CAPTCHA_FILESIZE — imagem vazia ou corrompida → confira se o download terminou
  • Resultado de resolução errado — JPEG supercomprimido → use PNG ou JPEG com qualidade ≥ 85

Dica: rode validate_captcha_image() no pipeline de CI antes do primeiro deploy que envia CAPTCHA em produção — ela pega os mesmos quatro erros antes de chegarem à API.


Resumo rápido antes de enviar

  • String base64 pura, sem prefixo data:image/...
  • Arquivo lido em modo binário (rb), nunca em modo texto
  • Screenshot do Selenium usado como está — sem recodificar
  • Tamanho final abaixo de 600 KB
  • Formato validado com validate_captcha_image() antes do envio

Perguntas frequentes

Dá para chamar validate_captcha_image() no pipeline, antes de cada envio?

Sim. Pega prefixo de URI, base64 inválido e tamanho fora do limite antes de a requisição falhar.

O screenshot do Selenium já sai em base64 — preciso codificar de novo?

Não. element.screenshot_as_base64 já retorna base64 pronto; codificar de novo corrompe os dados.

Preciso remover o prefixo data:image/png;base64,... antes de enviar?

Sim, sempre — o prefixo quebra a decodificação no servidor.

A mesma função de codificação serve para PNG, JPEG, GIF e WEBP?

Sim. A codificação base64 trabalha em cima dos bytes brutos do arquivo — o formato não muda o processo, só a escolha de qual formato usar (veja a tabela acima).

Uma imagem em base64 maior deixa a resolução mais lenta?

O payload maior aumenta o tempo de upload e de processamento, então vale manter a imagem abaixo de 600 KB mesmo quando o limite formal permite mais — principalmente se os workers da sua aplicação já rodam perto da CaptchaAI (por exemplo, em sa-east-1).


Guias relacionados


Codifique CAPTCHAs corretamente — comece com a CaptchaAI.

Os comentários estão desativados para este artigo.