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.