API Tutorials

Urutan Gambar BLS CAPTCHA dan Penanganan Respons Grid

Indeks yang dikembalikan CaptchaAI untuk grid BLS tidak otomatis jadi klik yang benar — Anda memetakannya ke sel, mengurai format respons (indeks, bitmask, atau JSON), lalu mengirim ulang sesuai urutan situs. Satu langkah meleset, tantangan ditolak.

Panduan ini membahas pemetaan grid, parsing respons, dan injeksi solusi ke Selenium atau Puppeteer — kasus umum bagi tim otomasi travel dan visa Indonesia yang menguji booking slot terbatas. Alurnya mengikuti pola CaptchaAI yang sama di semua tipe CAPTCHA: kirim tantangan → simpan task ID → polling res.php → pakai token atau indeks yang dikembalikan.


Tiga Variasi Tantangan Grid BLS dan Cara Memetakannya

Format respons BLS beda tergantung variasi tantangannya, jadi kenali dulu ketiganya sebelum menulis parser.

Urutan gambar

Menyusun gambar sesuai urutan tertentu. Respons berupa daftar indeks berurutan; klik di halaman harus mengikutinya persis.

Pemilihan gambar

Mengklik gambar yang cocok instruksi (misalnya "pilih semua gambar dengan teks"). Urutan klik tidak penting, kombinasi selnya yang harus tepat.

Pencocokan pola

Mencocokkan gambar dengan pola yang ditampilkan. Responsnya biasanya satu atau beberapa indeks tunggal, bukan rangkaian urutan.

Variasi Format respons Urutan klik penting?
Urutan gambar Daftar indeks berurutan Ya
Pemilihan gambar Kombinasi indeks (set) Tidak
Pencocokan pola Satu atau beberapa indeks tunggal Tidak berlaku

Tips: simpan tipe tantangan (ordering, selection, pattern) di samping instruksi mentah saat logging — memudahkan Anda melacak variasi mana yang paling sering gagal di produksi.

Memetakan tata letak grid ke indeks

BLS umumnya memakai grid 3x3 atau 4x4; setiap sel punya indeks datar (flat index) yang dihitung dari baris dan kolom. Fungsi berikut mengonversi dua arah — posisi baris/kolom ke indeks, dan sebaliknya — supaya Anda bisa mencocokkan koordinat CSS dengan indeks yang dikembalikan CaptchaAI.

# grid_mapping.py

# BLS grids typically use 3x3 or 4x4 layouts
# Each cell maps to an index:

# 3x3 grid:
# [0] [1] [2]
# [3] [4] [5]
# [6] [7] [8]

# 4x4 grid:
#  [0]  [1]  [2]  [3]
#  [4]  [5]  [6]  [7]
#  [8]  [9] [10] [11]
# [12] [13] [14] [15]

def grid_position(index, cols=3):
    """Convert flat index to row, column."""
    return index // cols, index % cols


def index_from_position(row, col, cols=3):
    """Convert row, column to flat index."""
    return row * cols + col


# Example: For a 3x3 grid, position (1, 2) = index 5
print(grid_position(5, cols=3))   # (1, 2)
print(index_from_position(1, 2))  # 5

Mengirim dan Mengurai Respons Grid BLS

Mengirim tantangan ke CaptchaAI

Kirim sitekey, pageurl, dan instruksi (jika ada) ke in.php, lalu polling res.php sampai status 1 — sama seperti tipe CAPTCHA lain, hanya method-nya beda (bls). Untuk booking slot visa yang punya jendela waktu ketat, jaga interval polling tetap pendek supaya token tidak kedaluwarsa sebelum sempat dipakai.

# solve_bls_grid.py
import requests
import time
import os
import json


def solve_bls_grid(sitekey, pageurl, instructions=None):
    """Solve a BLS grid CAPTCHA and get response indices."""
    api_key = os.environ["CAPTCHAAI_API_KEY"]

    payload = {
        "key": api_key,
        "method": "bls",
        "sitekey": sitekey,
        "pageurl": pageurl,
        "json": 1,
    }
    if instructions:
        payload["instructions"] = instructions

    resp = requests.post(
        "https://ocr.captchaai.com/in.php",
        data=payload,
        timeout=30,
    )
    result = resp.json()
    if result.get("status") != 1:
        raise RuntimeError(f"Submit failed: {result.get('request')}")

    task_id = result["request"]

    time.sleep(10)
    for _ in range(30):
        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(5)

    raise TimeoutError("BLS grid solve timeout")

Mengurai respons: indeks, bitmask, atau JSON

CaptchaAI tidak selalu mengembalikan format yang sama — kadang JSON, kadang indeks dipisah koma, kadang nilai tunggal. Parser di bawah menormalkan ketiganya jadi satu list Python, lalu format_for_submission() menyiapkan versi bitmask kalau situs membutuhkannya.

# parse_response.py
import json


def parse_grid_response(solution):
    """Parse CaptchaAI BLS response into actionable grid data."""
    # Solution may be JSON or comma-separated indices
    if isinstance(solution, str):
        try:
            parsed = json.loads(solution)
            return parsed
        except json.JSONDecodeError:
            pass

        # Try comma-separated indices
        if "," in solution:
            return [int(x.strip()) for x in solution.split(",")]

        # Single value
        return [solution]

    return solution


def format_for_submission(indices, grid_size=9):
    """Format indices for form submission."""
    # Some sites expect a bitmask
    bitmask = ["0"] * grid_size
    for idx in indices:
        if isinstance(idx, int) and 0 <= idx < grid_size:
            bitmask[idx] = "1"

    return {
        "indices": indices,
        "bitmask": "".join(bitmask),
        "count": len(indices),
    }

Injeksi Solusi ke Halaman dan Alur Lengkap

Injeksi ke Selenium

Cara injeksi tergantung tipe tantangan: klik langsung ke sel, atau isi field tersembunyi kalau responsnya tunggal. click_grid_cells() dan set_order_sequence() beda hanya pada jeda antar klik — ordering butuh jeda lebih panjang karena BLS sering memvalidasi timing klik.

# inject_grid.py
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time


def click_grid_cells(driver, indices):
    """Click specific grid cells based on solution indices."""
    wait = WebDriverWait(driver, 10)

    # Find all grid cells
    cells = wait.until(
        EC.presence_of_all_elements_located(
            (By.CSS_SELECTOR, ".captcha-grid .cell, .bls-grid img, .grid-item")
        )
    )

    for idx in indices:
        if isinstance(idx, int) and idx < len(cells):
            cells[idx].click()
            time.sleep(0.3)  # Brief delay between clicks


def set_order_sequence(driver, ordered_indices):
    """Click grid cells in the correct order for ordering challenges."""
    wait = WebDriverWait(driver, 10)

    cells = wait.until(
        EC.presence_of_all_elements_located(
            (By.CSS_SELECTOR, ".captcha-grid .cell, .bls-grid img")
        )
    )

    for idx in ordered_indices:
        if isinstance(idx, int) and idx < len(cells):
            cells[idx].click()
            time.sleep(0.5)  # Ordering needs pauses between clicks


def inject_hidden_response(driver, solution_value):
    """Set the solution in a hidden input field."""
    driver.execute_script("""
        var inputs = document.querySelectorAll(
            'input[name*="captcha"], input[name*="response"], #captcha-answer'
        );
        for (var i = 0; i < inputs.length; i++) {
            inputs[i].value = arguments[0];
        }
    """, str(solution_value))

Alur lengkap: deteksi sampai submit

handle_bls_grid() merangkai semua langkah di atas: deteksi elemen CAPTCHA, kirim ke CaptchaAI, urai respons, lalu pilih metode injeksi sesuai halaman punya sel grid atau hanya field tersembunyi.

# full_flow.py
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC


def handle_bls_grid(driver, pageurl):
    """Complete BLS grid CAPTCHA handling."""

    wait = WebDriverWait(driver, 15)

    # Wait for CAPTCHA to load
    captcha = wait.until(
        EC.presence_of_element_located(
            (By.CSS_SELECTOR, "[data-sitekey], .bls-captcha")
        )
    )
    sitekey = captcha.get_attribute("data-sitekey")

    # Get instructions
    instructions = None
    try:
        inst = driver.find_element(By.CSS_SELECTOR, ".captcha-instructions")
        instructions = inst.text.strip()
    except Exception:
        pass

    # Solve via CaptchaAI
    solution = solve_bls_grid(sitekey, pageurl, instructions)
    parsed = parse_grid_response(solution)

    # Determine response method
    grid_cells = driver.find_elements(
        By.CSS_SELECTOR, ".captcha-grid .cell, .bls-grid img"
    )

    if grid_cells:
        # Click-based response
        if isinstance(parsed, list) and all(isinstance(x, int) for x in parsed):
            click_grid_cells(driver, parsed)
        else:
            inject_hidden_response(driver, solution)
    else:
        # Hidden input response
        inject_hidden_response(driver, solution)

    # Submit
    submit = driver.find_element(
        By.CSS_SELECTOR, "button[type='submit'], .submit-btn, #verify"
    )
    submit.click()

    return True
Kondisi di halaman Fungsi yang dipakai
Ada sel grid + jawaban berupa daftar indeks click_grid_cells()
Ada sel grid + tantangan urutan gambar set_order_sequence()
Tidak ada sel grid, hanya field tersembunyi inject_hidden_response()

Tips: jalankan handle_bls_grid() dalam retry loop tipis (2–3 percobaan) — tantangan grid BLS kadang memunculkan babak kedua setelah babak pertama lolos, dan kode di atas sudah menyiapkan pengecekan elemen ulang untuk kasus itu.


Kesalahan Umum dan Cara Memperbaikinya

Sebagian besar kegagalan grid BLS berasal dari tiga hal: selector yang salah, klik terlalu cepat, atau format solusi yang tidak sesuai ekspektasi situs. Tabel berikut memetakan gejala ke perbaikannya.

Masalah Penyebab Solusi
Klik jatuh di sel salah Selector grid tidak cocok Sesuaikan selector CSS
Urutan ditolak walau indeks benar Klik terlalu cepat Jeda 300–500 ms, lihat set_order_sequence()
Format solusi ditolak Situs mau bitmask, dapat indeks Konversi dengan format_for_submission()
Grid tampak kosong saat parsing Gambar belum termuat Tunggu penuh sebelum solve_bls_grid()
Tantangan kedua muncul setelah submit Grid susulan setelah lolos Cek .bls-captcha lagi, ulangi alur

Pertanyaan Umum

Apa bedanya urutan gambar dan pemilihan gambar?

Urutan gambar butuh klik berurutan berjeda. Pemilihan gambar tidak peduli urutan — yang penting kombinasi selnya tepat.

Berapa lama satu tantangan grid BLS selesai diproses?

Bervariasi tergantung kompleksitas grid. solve_bls_grid() polling tiap 5 detik, 30 percobaan — cukup untuk kebanyakan kasus produksi.

Format respons apa yang saya terima?

Bisa indeks, bitmask, atau JSON. parse_grid_response() menormalkan semuanya jadi satu list Python.

Bisakah satu solusi grid dipakai ulang?

Tidak. Solusi terikat pada sesi tantangan tertentu — kalau gagal atau grid berubah, selesaikan tantangan baru.

Apakah pola injeksi ini juga berlaku untuk Puppeteer?

Ya secara konsep. Kode di panduan ini pakai Selenium, tapi parse_grid_response() dan format_for_submission() murni Python biasa — tinggal ganti bagian click_grid_cells() dengan page.click() Puppeteer memakai selector yang sama.


Panduan Terkait

Dua panduan berikut melengkapi topik ini kalau Anda baru mulai dengan BLS CAPTCHA:


Ambil API key CaptchaAI dan tangani grid BLS pertama Anda — mulai di sini.

Komentar dinonaktifkan untuk artikel ini.