API Tutorials

Рекомендации по кодированию изображений CAPTCHA Base64

ERROR_ZERO_CAPTCHA_FILESIZE при нормальном на вид изображении почти всегда означает не капчу, а кривой base64: лишний префикс data:image/..., повторное кодирование уже закодированной строки или чтение файла как текста. Ниже — код на Python для каждого сценария и функция валидации, которая ловит эти ошибки заранее.

Три причины закрывают почти все жалобы на кодирование: префикс data:image/... не срезан, строка закодирована в base64 дважды или файл прочитан в текстовом режиме вместо бинарного. Проверьте эти три пункта первыми, до разбора логов in.php.


Как отправить CAPTCHA в base64 через API

CaptchaAI принимает изображение CAPTCHA напрямую как base64-строку в теле запроса, без промежуточной загрузки файла на сервер, — параметр method=base64:

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

В ответе (при status: 1) поле request содержит ID задачи — сохраните его: тот же ID передаётся параметром id при опросе res.php до получения решения.


Кодирование файла с диска

Частый случай — картинка уже сохранена локально:

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

Длину строки стоит логировать отдельно от самого запроса — она первой сигнализирует о проблеме, если open() внезапно открыл не тот файл.


Кодирование изображения по URL

Если CAPTCHA отдаётся ссылкой, скачайте её и проверьте, что сервер вернул картинку, а не HTML при истёкшей сессии:

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

Кодирование скриншота из Selenium

Когда прямой ссылки на картинку нет (canvas, наложение через CSS), проще снять скриншот нужного элемента:

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

Снимая весь driver.get_screenshot_as_png(), помните: в кадр может попасть чужая форма с персональными данными. Кодируйте только нужный регион (152-ФЗ / GDPR).


Частые ошибки кодирования

Три сценария ниже покрывают почти весь трафик тикетов про кодирование — от самого частого к самому редкому.

Ошибка 1: лишний префикс data URI

# 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

Эта ошибка чаще всего приходит из готовых сниппетов «как получить base64 картинки», скопированных из фронтенд-кода — там префикс нужен для <img src>, а для in.php его нужно срезать.

Ошибка 2: двойное base64-кодирование

# 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

Проверить на глаз почти невозможно: обе строки выглядят как валидный base64, а validate_captcha_image() первой же строкой сообщит Invalid base64 только на грубой поломке — на двойном кодировании decode пройдёт, но картинка получится битой.

Ошибка 3: чтение файла как текста, а не как байтов

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

Проверка base64 перед отправкой

Функция ниже ловит все три ошибки до отправки в in.php — дешевле поймать проблему локально, чем тратить поток на заведомо неверный запрос:

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

Пример: проверка кодирования в CI для распределённой команды

Команда, где часть тестировщиков сидит в Европе, а стенды поднимаются в дата-центрах Казахстана или Центральной Азии, регулярно ловит одну и ту же проблему: локально скриншот кодируется нормально, а на CI-раннере в другом регионе — нет, потому что раннер тянет другую версию Pillow или запускает Selenium в другом режиме рендеринга. Добавлять validate_captcha_image() в сам API-клиент уже поздно: к этому моменту поток уже потрачен на заведомо неверный запрос.

Практичнее вынести проверку в отдельный шаг CI, до любого обращения к in.php:

  • Прогоняйте каждый новый скриншот через validate_captcha_image() в юнит-тесте, а не только вручную при отладке.
  • Логируйте поля format и size_kb из результата — по ним регрессия видна быстрее, чем по HTTP-ответу in.php.
  • Если раннеры разнесены по регионам, добавляйте retry с экспоненциальной задержкой на сам запрос к in.php, а не на шаг кодирования: кодирование почти никогда не флапает, а сеть между регионами — да.
  • Никогда не логируйте саму base64-строку целиком: в кадр скриншота может попасть форма с персональными данными (152-ФЗ / GDPR) — храните только метаданные из result.

Какой формат изображения выбрать

Формат Когда использовать Вес файла Качество
PNG Текстовые CAPTCHA, скриншоты элементов Больше Без потерь
JPEG CAPTCHA на основе фотографий Меньше С потерями (качество ≥ 85)
GIF Анимированные CAPTCHA Переменный Ограниченная палитра цветов
WEBP Современные браузеры Самый компактный Хорошее качество

Как выбрать формат:

  • Текстовая CAPTCHA — PNG: точность решения важнее веса файла, сжатие без потерь сохраняет края символов.
  • Скриншот с мобильного канала или высокая задержка до региона хостинга (например, Казахстан/Центральная Азия при скрапере из Европы) — WEBP: риск таймаута на in.php ниже.
  • Анимированная CAPTCHA — GIF, но сначала прогоните файл через validate_captcha_image(): формат там определяется по сигнатуре байтов, а не по расширению файла, и подмену легко пропустить глазами.

Диагностика ошибок API

Коды ошибок in.php, характерные именно для этого сценария, и что с ними делать:

Ошибка Причина Решение
ERROR_WRONG_FILE_EXTENSION Некорректные base64-данные Проверьте строку функцией validate_captcha_image()
ERROR_TOO_BIG_CAPTCHA_FILESIZE Изображение больше 600 КБ Уменьшите размер или сожмите перед кодированием
ERROR_ZERO_CAPTCHA_FILESIZE Пустое или повреждённое изображение Убедитесь, что загрузка прошла успешно
Неверный результат решения Слишком сильно сжатый JPEG Используйте PNG или JPEG с качеством ≥ 85

Если код ошибки не совпал ни с одной строкой таблицы — это, скорее всего, не про кодирование: проверьте key и текущий баланс потоков в панели управления, прежде чем снова отлаживать base64.


Часто задаваемые вопросы

Какой максимальный размер base64-строки принимает CaptchaAI?

600 КБ для декодированного изображения. Если скриншот больше:

  • уменьшите разрешение перед кодированием;
  • или обрежьте кадр до нужного элемента, а не всей страницы.

Почему ERROR_ZERO_CAPTCHA_FILESIZE появляется при рабочем скриншоте?

Чаще всего файл открыт в текстовом режиме ("r" вместо "rb"), байты повреждаются при чтении. Проверьте функцию чтения и validate_captcha_image().

Быстрая проверка перед тем, как копать глубже: сохраните base64.b64decode(b64_string) локально в файл и откройте как картинку — не загружайте строку в сторонние онлайн-декодеры. Если файл не открывается — проблема в кодировании, а не в CaptchaAI.

Нужно ли сжимать PNG перед кодированием?

Обычно нет — PNG уже без потерь. Если payload великоват, обрежьте изображение до элемента, а не сжимайте весь скриншот.

Как проверить base64-строку перед отправкой?

Коротко — так же, как это делает validate_captcha_image():

  • Декодируйте строку через base64.b64decode() в try/except.
  • Сверьте первые байты декодированных данных с сигнатурой формата (PNG/JPEG/GIF/WEBP).
  • Проверьте размер в КБ — меньше 1 КБ или больше 500 КБ почти всегда означает проблему.

Можно ли отправлять CAPTCHA в формате SVG?

Нет, только растровые форматы. Сконвертируйте SVG в PNG заранее — например, через Pillow или cairosvg.

Что логировать при ошибке кодирования, если нельзя сохранять сам base64?

Только результат validate_captcha_image(), без самой строки:

  • поле format — распознанный тип изображения;
  • поле size_kb — размер после декодирования;
  • список errors — этого достаточно, чтобы отличить сломанное кодирование от сетевой проблемы, и не тянет за собой персональные данные из скриншота.

Похожие материалы


Кодируйте CAPTCHA правильно с первого раза — начните с CaptchaAI.

Комментарии для этой статьи отключены.