Troubleshooting

Otomatisasi Browser CAPTCHA Gagal tetapi API Berfungsi: Panduan Debug

Kalau respons dari CaptchaAI sudah status: 1 dan token reCAPTCHA sudah di tangan, tapi form di browser tetap menolak submit, penyebabnya hampir tidak pernah ada di sisi API — CaptchaAI sudah menyelesaikan tugasnya. Masalahnya ada di enam titik umum saat token itu berpindah dari response JSON ke DOM: selector yang salah, callback yang tidak terpicu, token yang keburu kedaluwarsa, pageurl yang tidak cocok, sampai deteksi bot yang sama sekali tidak berhubungan dengan CAPTCHA. Panduan ini membedah keenamnya satu per satu, lengkap dengan kode Selenium yang bisa langsung Anda adaptasi ke Playwright atau Puppeteer.


Pola Kegagalan yang Paling Sering Terjadi

Sebelum masuk ke perbaikan satu per satu, kenali dulu lima pola yang paling sering muncul di laporan bug automation developer — baik yang mengerjakan proyek freelance scraping/otomatisasi di Upwork atau Fastwork, maupun tim internal yang menjalankan bot price-monitoring:

Masalah Gejala Akar Penyebab
Token tidak diinjeksikan ke elemen yang benar Formulir dikirim tanpa token Selektor textarea salah
Token kedaluwarsa sebelum dikirimkan Situs menampilkan CAPTCHA lagi Terlalu lambat antara solve dan submit
Callback tidak terpicu Tombol formulir tetap dinonaktifkan Callback grecaptcha tidak ada
Pageurl salah dikirim ke API Token tidak valid untuk domain ini Ketidakcocokan URL
Deteksi bot di luar CAPTCHA Diblokir setelah token diterima sinyal browser

Detail masing-masing solusi ada di bagian berikut, diurutkan dari yang paling sering ditemui ke yang paling jarang.


Perbaikan 1: suntik token reCAPTCHA v2 dengan benar

Ini penyebab paling umum: token sudah benar, tapi hanya ditulis ke value textarea tanpa memicu callback reCAPTCHA. Google mendesain widget ini supaya form baru menganggap CAPTCHA "selesai" setelah callback jalan — bukan sekadar karena textarea-nya terisi. Fungsi di bawah menangani dua langkah itu sekaligus: mengisi setiap textarea g-recaptcha-response yang ada di halaman (bisa lebih dari satu kalau ada beberapa widget), lalu menelusuri konfigurasi ___grecaptcha_cfg untuk menemukan dan memanggil callback yang sesuai.

from selenium import webdriver
from selenium.webdriver.common.by import By
import time


def inject_recaptcha_token(driver, token):
    """Properly inject reCAPTCHA v2 token in browser."""

    # Step 1: Make the response textarea visible and set the value
    driver.execute_script("""
        // Find all response textareas (may be multiple on page)
        var textareas = document.querySelectorAll('[name="g-recaptcha-response"]');
        textareas.forEach(function(ta) {
            ta.style.display = 'block';
            ta.value = arguments[0];
        });

        // Also set via ID if present
        var byId = document.getElementById('g-recaptcha-response');
        if (byId) {
            byId.style.display = 'block';
            byId.value = arguments[0];
        }
    """, token)

    # Step 2: Trigger the callback
    driver.execute_script("""
        // Try standard callback
        if (typeof ___grecaptcha_cfg !== 'undefined') {
            var clients = ___grecaptcha_cfg.clients;
            for (var key in clients) {
                var client = clients[key];
                // Navigate nested structure to find callback
                for (var prop in client) {
                    var val = client[prop];
                    if (val && typeof val === 'object') {
                        for (var subprop in val) {
                            var subval = val[subprop];
                            if (subval && typeof subval === 'object' && subval.callback) {
                                subval.callback(arguments[0]);
                                return;
                            }
                        }
                    }
                }
            }
        }
        // Fallback: try common callback names
        if (typeof onCaptchaSuccess === 'function') onCaptchaSuccess(arguments[0]);
        else if (typeof captchaCallback === 'function') captchaCallback(arguments[0]);
    """, token)


# Usage
driver = webdriver.Chrome()
driver.get("https://example.com/form")
# ... solve CAPTCHA via CaptchaAI ...
inject_recaptcha_token(driver, token)
time.sleep(1)
driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()

Perbaikan 2: formulir yang menunggu callback aktif dulu

Sebagian situs sengaja mengunci tombol submit sampai callback reCAPTCHA benar-benar terpicu — bukan sekadar mengecek apakah textarea-nya terisi. Kalau inject_recaptcha_token di atas belum berhasil memicu callback, coba dua pendekatan berikut: ambil nama fungsi dari atribut data-callback pada widget, atau telusuri langsung struktur ___grecaptcha_cfg untuk menemukan callback yang tersembunyi di dalamnya.

def find_and_trigger_callback(driver, token):
    """Find the reCAPTCHA callback and trigger it."""

    # Method 1: Extract callback from data attribute
    callback_name = driver.execute_script("""
        var widget = document.querySelector('.g-recaptcha');
        if (widget) {
            return widget.getAttribute('data-callback');
        }
        return null;
    """)

    if callback_name:
        driver.execute_script(f"window['{callback_name}'](arguments[0]);", token)
        return True

    # Method 2: Extract from grecaptcha config
    triggered = driver.execute_script("""
        try {
            var widgetId = 0;
            var callback = ___grecaptcha_cfg.clients[widgetId].aa.l.callback;
            if (typeof callback === 'function') {
                callback(arguments[0]);
                return true;
            }
        } catch(e) {}
        return false;
    """, token)

    return triggered

Perbaikan 3: solve dulu, suntik detik itu juga

Waktu adalah musuh terbesar di sini. Token reCAPTCHA v2 punya masa berlaku yang singkat, dan setiap detik yang terbuang antara solve dan submit — screenshot debug, logging berlebihan, jeda sleep() yang kelamaan — menambah risiko token kedaluwarsa sebelum sempat dipakai. Ini kelihatan jelas kalau bot Anda jalan di server yang jauh dari target: automation developer yang deploy dari region seperti AWS ap-southeast-1 (Singapura) atau GCP asia-southeast2 (Jakarta) ke situs yang server-nya di region lain sering menemukan latensi ekstra yang tidak terlihat saat testing lokal. Fungsi berikut memangkas jeda itu: solve, polling, lalu suntikkan token dalam hitungan milidetik setelah status: 1 diterima — bukan menunggu proses lain selesai dulu.

import requests
import time


def solve_and_inject_fast(driver, api_key, sitekey, page_url):
    """Solve CAPTCHA and inject token with minimal delay."""
    # Submit solve request
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": api_key,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": page_url,
        "json": 1,
    }, timeout=30)
    task_id = resp.json()["request"]

    # Poll for result
    time.sleep(15)
    for _ in range(24):
        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:
            token = data["request"]

            # IMMEDIATELY inject — don't wait
            inject_recaptcha_token(driver, token)

            # Submit form within 5 seconds
            time.sleep(0.5)
            return True

        if data["request"] != "CAPCHA_NOT_READY":
            raise RuntimeError(data["request"])
        time.sleep(5)

    raise TimeoutError("Solve timeout")

Perbaikan 4: pageurl yang tepat untuk SPA dan iframe

Untuk SPA dan halaman yang memuat CAPTCHA di dalam iframe, pageurl yang Anda kirim ke CaptchaAI harus sama persis dengan URL yang benar-benar dilihat reCAPTCHA — bukan URL yang Anda pakai saat memanggil driver.get(). Kalau ada redirect setelah navigasi awal, atau widget-nya dimuat di iframe terpisah, kirim driver.current_url saat itu juga, bukan URL yang sudah Anda catat di awal skrip.

def get_correct_pageurl(driver):
    """Get the URL that reCAPTCHA actually sees."""

    # Check if CAPTCHA is in an iframe
    iframes = driver.find_elements(By.TAG_NAME, "iframe")

    for iframe in iframes:
        src = iframe.get_attribute("src") or ""
        if "recaptcha" in src or "anchor" in src:
            # CAPTCHA is in iframe — use parent page URL
            return driver.current_url

    # For SPAs, use current URL (may differ from initial load)
    return driver.current_url


# WRONG — using the URL you navigated to
pageurl = "https://example.com/form"  # May have redirected

# CORRECT — using the URL the browser is actually on
pageurl = get_correct_pageurl(driver)

Perbaikan 5: deteksi bot yang tidak ada hubungannya dengan CAPTCHA

Token yang valid tidak menjamin form Anda lolos — sebagian situs punya lapisan deteksi bot yang berjalan independen dari CAPTCHA dan baru memblokir setelah submit. Kalau Anda mendapat 403 padahal token diterima, cek dulu sinyal browser otomatis yang mudah dikenali script pihak lain: argumen automation yang masih aktif, ukuran window yang tidak wajar, atau flag otomasi bawaan Selenium yang belum dibersihkan. Terapkan konfigurasi ini hanya pada environment yang memang berwenang Anda otomatisasi — QA internal atau staging milik Anda sendiri, bukan alur login akun orang lain.

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service


def create_stealth_driver():
    """Create a browser that avoids basic bot detection."""
    options = Options()

    # Remove automation indicators
    options.add_experimental_option("excludeSwitches", ["enable-automation"])
    options.add_experimental_option("useAutomationExtension", False)

    # Set realistic window size
    options.add_argument("--window-size=1920,1080")

    driver = webdriver.Chrome(options=options)

    # Konfigurasi browser standar
    driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
        "source": "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
    })

    return driver


driver = create_stealth_driver()

Perbaikan 6: menyuntikkan token Cloudflare Turnstile

Cloudflare Turnstile tidak pakai textarea tersembunyi seperti reCAPTCHA — tokennya masuk ke input cf-turnstile-response, dan sebagian implementasi lama masih fallback ke nama field g-recaptcha-response. Selain mengisi value-nya, Anda juga perlu men-dispatch event change dan input secara manual, karena banyak widget Turnstile memantau event itu untuk tahu kapan harus mengaktifkan tombol submit — mengisi value lewat JavaScript saja tidak otomatis memicu event tersebut.

def inject_turnstile_token(driver, token):
    """Inject Turnstile token into the page."""
    driver.execute_script("""
        // Find Turnstile response input
        var inputs = document.querySelectorAll(
            'input[name="cf-turnstile-response"], ' +
            'input[name="g-recaptcha-response"]'
        );

        inputs.forEach(function(input) {
            input.value = arguments[0];
        });

        // Trigger change event
        inputs.forEach(function(input) {
            input.dispatchEvent(new Event('change', { bubbles: true }));
            input.dispatchEvent(new Event('input', { bubbles: true }));
        });

        // Try Turnstile callback
        if (window.turnstile) {
            var widgets = document.querySelectorAll('[data-callback]');
            widgets.forEach(function(w) {
                var cb = w.getAttribute('data-callback');
                if (typeof window[cb] === 'function') {
                    window[cb](arguments[0]);
                }
            });
        }
    """, token)

Checklist untuk mendiagnosis kegagalan injeksi

Kalau keenam perbaikan di atas belum menyelesaikan masalah, jalankan fungsi ini di browser yang sama untuk melihat persis di mana rantainya putus — apakah textarea-nya ada, apakah reCAPTCHA sudah termuat, atau callback-nya memang tidak ditemukan:

def debug_captcha_injection(driver, token):
    """Print debug info to diagnose injection failures."""
    info = driver.execute_script("""
        var result = {};

        // Check textarea exists
        var ta = document.querySelector('[name="g-recaptcha-response"]');
        result.textarea_found = !!ta;
        result.textarea_value_set = ta ? ta.value.length > 0 : false;

        // Check reCAPTCHA loaded
        result.grecaptcha_loaded = typeof grecaptcha !== 'undefined';

        // Check for callback
        var widget = document.querySelector('.g-recaptcha');
        result.has_data_callback = widget ? !!widget.getAttribute('data-callback') : false;
        result.callback_name = widget ? widget.getAttribute('data-callback') : null;

        // Current URL
        result.current_url = window.location.href;

        // Check for errors in console (basic)
        result.has_submit_button = !!document.querySelector('button[type=submit], input[type=submit]');

        return result;
    """)

    for key, value in info.items():
        print(f"  {key}: {value}")

    return info

Ringkasan gejala, penyebab, dan solusi

Butuh jawaban cepat tanpa membaca ulang enam bagian di atas? Cocokkan gejala Anda dengan baris berikut:

Gejala Penyebab Solusi
Token diset tapi formulir ditolak Callback tidak terpicu Temukan dan panggil fungsi callback
Tombol kirim tetap dinonaktifkan Callback yang mengaktifkannya belum dipanggil Picu callback setelah pengaturan token
"CAPTCHA tidak valid" setelah dikirim Token sudah kedaluwarsa Kurangi waktu antara solve dan submit
403 setelah pengiriman formulir Deteksi bot (bukan CAPTCHA) Gunakan opsi konfigurasi browser standar
Berfungsi di request, gagal di browser URL halaman berbeda Gunakan driver.current_url sebagai pageurl

Sebagian besar tiket dukungan bertema "token valid tapi ditolak browser" yang kami terima berujung pada salah satu dari lima baris ini.


Pertanyaan yang sering ditanyakan

Kenapa token yang sukses lewat curl atau Postman malah ditolak Selenium?

Karena API CaptchaAI hanya bertanggung jawab menghasilkan token yang valid — apa yang terjadi setelah itu di DOM sepenuhnya urusan skrip automation Anda. curl dan Postman tidak menjalankan JavaScript halaman, jadi Anda tidak akan pernah melihat masalah callback dari situ. Begitu token masuk ke browser sungguhan, ikuti Perbaikan 1 dan 2 di atas.

Apakah cara ini juga berlaku untuk Playwright dan Puppeteer, bukan cuma Selenium?

Ya. Contoh kode di panduan ini pakai Selenium karena paling umum dipakai automation developer Indonesia, tapi logikanya sama persis di framework lain — ganti driver.execute_script() dengan page.evaluate(), isi textarea atau input yang sama, lalu picu callback yang sama.

Berapa lama sebaiknya saya menunggu sebelum menyuntikkan token?

Sesingkat mungkin. Begitu res.php mengembalikan status: 1, langsung panggil fungsi injeksi — jangan sisipkan screenshot, logging panjang, atau sleep() yang tidak perlu di antaranya. Kalau bot Anda berjalan di region yang jauh dari server target, anggaran waktu ini makin ketat, jadi urutan solve → polling → injeksi harus sependek mungkin seperti pada Perbaikan 3.

Kalau widget-nya tidak punya atribut data-callback, bagaimana cara menemukan callback-nya?

Telusuri ___grecaptcha_cfg.clients lewat console browser, atau pakai fungsi find_and_trigger_callback pada Perbaikan 2 — callback biasanya tersembunyi beberapa level di dalam struktur objek klien. Kalau masih buntu, jalankan Checklist Debug di atas untuk melihat field mana yang kosong.


Panduan Terkait

Komentar dinonaktifkan untuk artikel ini.