API Tutorials

Praktik Terbaik Encoding Base64 untuk Image CAPTCHA

Ada tiga aturan wajib saat encode base64 image CAPTCHA untuk API CaptchaAI: kirim raw base64 tanpa prefix data URI, jangan encode dua kali, dan selalu baca file gambar sebagai bytes (rb), bukan teks. Lewatkan salah satu saja, dan hasilnya bisa berupa error langsung seperti ERROR_WRONG_FILE_EXTENSION — atau yang lebih menyebalkan, task yang lolos validasi tapi solve-nya meleset karena gambar sudah rusak sebelum sampai ke server CaptchaAI. Panduan ini membahas empat cara encode base64 (dari file, URL, dan dua pendekatan screenshot Selenium), tiga kesalahan encoding paling umum di lapangan, plus checklist validasi yang bisa Anda pasang sebelum setiap request ke in.php.


Format Base64 yang Diterima API CaptchaAI

CaptchaAI menerima gambar CAPTCHA dalam bentuk string base64 lewat parameter method=base64 pada endpoint in.php. Anda tidak perlu upload file secara langsung — cukup encode gambar di sisi Anda, lalu kirim base64 murni sebagai nilai body. Fungsi berikut adalah pola dasar yang dipakai di semua contoh pada panduan ini: encode dulu secara lokal, submit lewat requests.post, lalu tangani respons JSON dari CaptchaAI.

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

Simpan CAPTCHAAI_API_KEY di environment variable, bukan hard-code di skrip — kebiasaan yang sama juga sebaiknya berlaku untuk kredensial lain di pipeline scraping atau automation Anda.


Cara Encode Gambar CAPTCHA dari File Lokal

Kasus paling sederhana: Anda sudah punya file gambar CAPTCHA di disk — hasil download manual, dataset uji, atau screenshot yang disimpan oleh proses sebelumnya. Fungsi encode_from_file di bawah membaca file dalam mode binary (rb) dan mengembalikan base64 sebagai string ASCII, siap dipakai sebagai nilai body pada request ke in.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")

Cetak len(b64) untuk sanity check cepat — string yang jauh lebih pendek dari biasanya menandakan file kosong atau download yang terputus.


Cara Encode Gambar CAPTCHA Langsung dari URL

Kalau CAPTCHA muncul sebagai gambar yang di-hosting di URL publik — bukan elemen halaman yang perlu di-screenshot — Anda bisa download lalu encode dalam satu langkah. Pengecekan Content-Type di bawah ini penting: banyak endpoint yang diblokir atau kena rate limit tetap mengembalikan status 200 berisi halaman HTML error, bukan gambar, dan itu akan lolos ke tahap encode kalau tidak divalidasi lebih dulu.

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

Timeout 15 detik pada contoh ini cukup untuk sebagian besar CDN gambar. Naikkan sedikit kalau worker Anda berjalan di jaringan dengan latensi lebih tinggi ke origin server.


Cara Encode Screenshot Selenium ke Base64

Skenario yang sering muncul di tim price-monitoring atau data scraping Indonesia: worker Selenium jalan di server AWS ap-southeast-1 (Singapura) atau GCP asia-southeast2 (Jakarta), ambil screenshot elemen CAPTCHA dari halaman checkout, langsung encode ke base64 tanpa menyentuh disk. Ini lebih murah dibanding simpan-ke-file dulu, terutama kalau worker jalan dalam jumlah banyak.

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

Pakai encode_from_element kapan pun bisa — sudah mengembalikan base64 langsung, tanpa langkah tambahan. encode_from_page_crop baru diperlukan kalau elemen CAPTCHA tidak bisa di-screenshot langsung (misalnya di dalam iframe lintas origin), sehingga Anda harus crop koordinatnya sendiri dengan Pillow.


Kesalahan Encoding Base64 yang Paling Sering Terjadi

Tiga pola error ini yang paling sering bikin task base64 gagal atau salah solve, berdasarkan pola yang berulang di kode integrasi:

Kesalahan 1: Prefix Data URI Ikut Terkirim

Kalau gambar diambil dari <img src> atau canvas lewat JavaScript, hasilnya sering berupa data URI lengkap (data:image/png;base64,...), bukan base64 murni. CaptchaAI hanya menerima bagian setelah koma — kirim prefix-nya dan Anda dapat ERROR_WRONG_FILE_EXTENSION karena server tidak bisa mendeteksi format gambarnya.

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

# BENAR — raw base64 saja
good = "iVBORw0KGgo..."

# Fix: Hapus prefix-nya
def clean_base64(b64_string):
    if "," in b64_string:
        return b64_string.split(",", 1)[1]
    return b64_string

Kesalahan 2: Encoding Ganda (Double Encoding)

screenshot_as_base64 milik Selenium sudah mengembalikan base64, bukan bytes mentah. Meng-encode ulang hasil itu — mudah lolos code review karena kodenya terlihat masuk akal — menghasilkan base64-dari-base64 yang tidak bisa didekode balik jadi gambar valid.

# SALAH — encode string yang sudah di-encode
already_b64 = element.screenshot_as_base64
double_encoded = base64.b64encode(already_b64.encode()).decode()  # SALAH

# BENAR — gunakan langsung
correct = element.screenshot_as_base64  # Sudah base64

Kesalahan 3: Baca File sebagai Teks, Bukan Bytes

Membuka file gambar dengan mode "r" alih-alih "rb" membuat Python mencoba decode byte gambar sebagai teks, lalu re-encode dengan encoding default sistem. Hasilnya data biner yang rusak sebelum sempat di-base64-kan — kesalahan klasik developer yang baru pindah dari file teks ke file biner.

# SALAH — membaca sebagai teks
with open("captcha.png", "r") as f:  # Text mode
    content = f.read()  # Binary data rusak

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

Validasi Base64 Sebelum Dikirim ke API

CaptchaAI menagih per thread aktif, bukan per solve — jadi base64 yang gagal validasi tetap memakan slot thread sampai timeout atau server menolaknya. Untuk tim yang cost-sensitive (agensi price-monitoring, tim scraping freelance), validasi lokal seperti di bawah ini lebih murah daripada menunggu error balik dari 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")

Jalankan validate_captcha_image() sebagai langkah terakhir sebelum submit_image_captcha(). Kalau result["valid"] bernilai False, perbaiki dulu di sisi Anda daripada menunggu balasan error dari server.


Format Gambar Mana yang Paling Cocok untuk CAPTCHA Anda?

Format gambar memengaruhi akurasi solve, bukan cuma ukuran file yang dikirim. Ringkasannya:

Format Cocok Untuk Ukuran File Kualitas
PNG CAPTCHA teks, screenshot Lebih besar Lossless
JPEG CAPTCHA berbasis foto Lebih kecil Lossy (gunakan kualitas ≥ 85)
GIF CAPTCHA animasi Variabel Warna terbatas
WebP Browser modern Terkecil Kualitas baik

Rekomendasi: Untuk CAPTCHA berbasis teks, PNG tetap pilihan paling aman — kompresi lossless-nya menjaga tepian karakter tetap tajam, dan itu langsung berpengaruh ke akurasi hasil solve.


Solusi Error Encoding yang Sering Muncul

Kalau base64 sudah lolos validasi tapi API tetap menolak, cek tabel berikut dulu sebelum mengubah kode lain yang tidak relevan:

Masalah Penyebab Solusi
ERROR_WRONG_FILE_EXTENSION Data base64 tidak valid Validasi dengan validate_captcha_image()
ERROR_TOO_BIG_CAPTCHA_FILESIZE Gambar melebihi 600 KB Resize atau compress sebelum encode
ERROR_ZERO_CAPTCHA_FILESIZE Gambar kosong atau rusak Periksa download berhasil
Hasil solve salah JPEG terlalu terkompresi Gunakan PNG atau JPEG kualitas ≥ 85

Batas 600 KB berlaku untuk body base64, bukan file gambar sebelum di-encode — base64 menambah ukuran sekitar 33%, jadi gambar mentah idealnya di bawah ~450 KB.


Pertanyaan yang Sering Ditanyakan

Berapa ukuran maksimum gambar yang bisa dikirim sebagai base64?

600 KB untuk body base64 itu sendiri. Karena base64 menambah ukuran sekitar 33%, gambar mentah sebaiknya di bawah 450 KB — resize screenshot besar sebelum di-encode.

Apakah CaptchaAI menerima format WebP?

Ya, WebP termasuk format yang diterima dan menghasilkan ukuran file terkecil di antara format umum. Untuk CAPTCHA berbasis teks, PNG tetap lebih disarankan karena kompresinya lossless dan mempertahankan tepian karakter.

Kenapa hasil solve masih salah walau base64 sudah lolos validasi?

Validasi hanya memastikan base64 valid dan bisa didekode menjadi gambar — bukan menjamin gambarnya jelas. Penyebab paling umum: kompresi JPEG yang terlalu agresif (gunakan kualitas ≥ 85) atau crop screenshot yang memotong sebagian karakter CAPTCHA.

Perlukah saya resize screenshot sebelum encode kalau worker berjalan di jaringan terbatas?

Ya. Screenshot penuh halaman bisa mencapai beberapa ratus KB; crop ke elemen CAPTCHA saja (lihat encode_from_element di atas) memangkas payload dan mempercepat request ke in.php.

Bisakah saya submit gambar SVG langsung?

Tidak. SVG adalah format vektor, bukan raster, sementara API base64 CaptchaAI mengharapkan data piksel. Konversi ke PNG dulu memakai library seperti Pillow atau cairosvg sebelum encode.


Panduan Terkait

Komentar dinonaktifkan untuk artikel ini.