Tutorials

Panduan Integrasi Lengkap Python Selenium + CaptchaAI

Selenium bisa mengeksekusi JavaScript, tapi tidak bisa menyelesaikan CAPTCHA sendiri — itu bukan tugasnya. Begitu reCAPTCHA, Turnstile, atau CAPTCHA gambar muncul di tengah alur login, script Anda macet menunggu widget yang tidak pernah selesai tanpa bantuan luar. CaptchaAI mengisi celah itu: kirim sitekey dari halaman, terima token valid, suntikkan ke DOM sebelum submit. Panduan ini membahas pola integrasi untuk reCAPTCHA v2/v3, Turnstile, dan CAPTCHA gambar — dari driver Selenium yang stabil sampai class Python siap produksi yang mendeteksi jenis CAPTCHA otomatis dan mengirim form tanpa campur tangan manual.


Sebelum Mulai: Instalasi Library

Anda memerlukan tiga package: selenium untuk kontrol browser, requests untuk memanggil API CaptchaAI, dan webdriver-manager supaya ChromeDriver otomatis cocok dengan Chrome yang terpasang.

pip install selenium requests webdriver-manager

Siapkan Driver Selenium yang Stabil

Konfigurasi berikut menonaktifkan flag otomatisasi bawaan Chrome dan menghapus sinyal browser otomatis lewat Chrome DevTools Protocol sebelum halaman dimuat. Tanpa ini, sebagian situs menampilkan pesan "Chrome dikendalikan software pengujian otomatis" dan memblokir checkout — bukan soal menyembunyikan apa pun, sekadar konfigurasi standar yang membuat driver berperilaku wajar.

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

def create_driver():
    """Create a Selenium driver with standar settings."""
    options = webdriver.ChromeOptions()
    options.add_experimental_option("excludeSwitches", ["enable-automation"])
    options.add_experimental_option("useAutomationExtension", False)

    driver = webdriver.Chrome(
        service=Service(ChromeDriverManager().install()),
        options=options,
    )

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

    return driver

Fungsi Helper: Kirim Task ke CaptchaAI

Semua jenis CAPTCHA di panduan ini lewat satu fungsi: solve_captcha(). Fungsi ini mengirim task ke in.php dengan parameter method berbeda per jenis, lalu polling res.php tiap 5 detik sampai status 1 atau muncul ERROR_CAPTCHA_UNSOLVABLE. Pola kirim → simpan task ID → polling → pakai token ini sama di seluruh panduan.

Berapa banyak task yang berjalan bersamaan bergantung jumlah thread di plan Anda — BASIC ($15/bulan, 5 thread) cukup untuk script satu-satu, tim yang menjalankan puluhan sesi paralel biasanya naik ke ADVANCE ($90/bulan, 50 thread). Setiap thread menangani satu CAPTCHA; begitu selesai, thread itu bebas mengambil task berikutnya, tanpa batas solve per bulan.

import time
import requests

API_KEY = "YOUR_API_KEY"

def solve_captcha(method, **params):
    """Generic CaptchaAI solver — works for all supported CAPTCHA types."""
    submit_data = {
        "key": API_KEY,
        "method": method,
        "json": 1,
        **params,
    }

    submit = requests.post("https://ocr.captchaai.com/in.php", data=submit_data, timeout=30)
    data = submit.json()

    if data.get("status") != 1:
        raise Exception(f"Submit error: {data.get('request')}")

    task_id = data["request"]

    for _ in range(30):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY,
            "action": "get",
            "id": task_id,
            "json": 1,
        }, timeout=30).json()

        if result.get("status") == 1:
            return result["request"]
        if result.get("request") == "ERROR_CAPTCHA_UNSOLVABLE":
            raise Exception("CAPTCHA unsolvable")

    raise TimeoutError("Solve timed out")

Solve reCAPTCHA v2 di Selenium

Alur Lengkap: Deteksi Sitekey sampai Submit Form

Fungsi solve_recaptcha_v2_selenium() menjalankan empat langkah: buka halaman, ambil data-sitekey dengan regex, kirim ke solve_captcha(), lalu suntikkan token ke elemen g-recaptcha-response sebelum submit. Bagian yang sering luput: banyak situs punya callback JavaScript yang harus dipanggil eksplisit setelah token disuntikkan, kalau tidak form tetap menganggap CAPTCHA belum selesai. Kode di bawah menangani keduanya lewat ___grecaptcha_cfg.

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import re

def solve_recaptcha_v2_selenium(driver, url):
    """Complete reCAPTCHA v2 solve in Selenium."""
    driver.get(url)

    # Wait for page to load
    WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.TAG_NAME, "body"))
    )

    # Extract sitekey
    page_source = driver.page_source
    match = re.search(r'data-sitekey=["\']([A-Za-z0-9_-]{40})["\']', page_source)
    if not match:
        raise ValueError("reCAPTCHA sitekey not found")
    sitekey = match.group(1)
    print(f"Sitekey: {sitekey}")

    # Solve via CaptchaAI
    token = solve_captcha(
        "userrecaptcha",
        googlekey=sitekey,
        pageurl=url,
    )
    print(f"Token received: {token[:50]}...")

    # Inject token into the page
    driver.execute_script(f"""
        document.getElementById('g-recaptcha-response').value = '{token}';
        document.getElementById('g-recaptcha-response').style.display = 'block';
    """)

    # If there's a callback function, call it
    driver.execute_script(f"""
        if (typeof ___grecaptcha_cfg !== 'undefined') {{
            var clients = ___grecaptcha_cfg.clients;
            for (var key in clients) {{
                var client = clients[key];
                if (client.rr && client.rr.l) {{
                    client.rr.l.callback('{token}');
                }}
            }}
        }}
    """)

    # Submit the form
    submit_button = driver.find_element(By.CSS_SELECTOR, "button[type='submit'], input[type='submit']")
    submit_button.click()

    return token

# Usage
driver = create_driver()
try:
    solve_recaptcha_v2_selenium(driver, "https://staging.example.com/qa-login")
    print(f"Current URL: {driver.current_url}")
finally:
    driver.quit()

Solve Cloudflare Turnstile di Selenium

Pola Turnstile mirip reCAPTCHA v2, tapi sitekey-nya bisa muncul di atribut data-sitekey, atau di konfigurasi JavaScript kalau widget dirender dinamis. solve_turnstile_selenium() mencoba kedua pola regex sebelum menyerah. Token disuntikkan ke semua input name="cf-turnstile-response", karena sebagian implementasi punya lebih dari satu elemen dengan nama itu.

def solve_turnstile_selenium(driver, url):
    """Complete Turnstile solve in Selenium."""
    driver.get(url)

    WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.TAG_NAME, "body"))
    )

    # Extract sitekey
    page_source = driver.page_source
    match = re.search(r'data-sitekey=["\']([0-9x][A-Za-z0-9_-]+)["\']', page_source)
    if not match:
        # Try JavaScript API pattern
        match = re.search(r"sitekey\s*:\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]", page_source)
    if not match:
        raise ValueError("Turnstile sitekey not found")

    sitekey = match.group(1)
    print(f"Turnstile sitekey: {sitekey}")

    # Solve via CaptchaAI
    token = solve_captcha(
        "turnstile",
        sitekey=sitekey,
        pageurl=url,
    )
    print(f"Turnstile token: {token[:50]}...")

    # Inject token
    driver.execute_script(f"""
        // Set the hidden input value
        var inputs = document.querySelectorAll('[name="cf-turnstile-response"]');
        inputs.forEach(function(input) {{ input.value = '{token}'; }});

        // Also try the callback approach
        if (typeof turnstile !== 'undefined' && turnstile.getResponse) {{
            // Widget already rendered
        }}
    """)

    # Submit form
    submit_button = driver.find_element(By.CSS_SELECTOR, "button[type='submit'], input[type='submit']")
    submit_button.click()

    return token

CAPTCHA Gambar/Teks: Screenshot Elemen lalu Solve

CAPTCHA gambar klasik tidak punya sitekey untuk diekstrak — yang dibutuhkan CaptchaAI adalah gambarnya. solve_image_captcha_selenium() mengambil screenshot elemen dari DOM dalam base64, mengirimnya dengan method base64, lalu mengetik jawaban ke input field. Screenshot elemen lebih andal daripada screenshot layar penuh plus crop manual.

import base64

def solve_image_captcha_selenium(driver, captcha_selector):
    """Solve image CAPTCHA visible in the browser."""
    # Find the CAPTCHA image element
    captcha_img = driver.find_element(By.CSS_SELECTOR, captcha_selector)

    # Get image as base64
    img_base64 = captcha_img.screenshot_as_base64

    # Solve via CaptchaAI
    answer = solve_captcha("base64", body=img_base64)
    print(f"CAPTCHA answer: {answer}")

    # Type the answer into the input field
    captcha_input = driver.find_element(
        By.CSS_SELECTOR, "input[name='captcha'], input[name='code'], input.captcha-input"
    )
    captcha_input.clear()
    captcha_input.send_keys(answer)

    return answer

reCAPTCHA v3: Solve Berbasis Skor Tanpa Interaksi

reCAPTCHA v3 tidak punya widget untuk diklik — ia berjalan diam-diam di background dan menghasilkan skor 0.0–1.0 dari perilaku pengguna. Karena tidak ada elemen visual, solve_recaptcha_v3_selenium() menerima sitekey sebagai parameter langsung, biasanya diambil sekali dari page source lalu disimpan sebagai konstanta. Parameter action harus sama persis dengan halaman asli, atau skornya turun drastis.

def solve_recaptcha_v3_selenium(driver, url, sitekey, action="verify"):
    """Solve reCAPTCHA v3 in Selenium."""
    driver.get(url)

    # Solve via CaptchaAI
    token = solve_captcha(
        "userrecaptcha",
        googlekey=sitekey,
        pageurl=url,
        version="v3",
        action=action,
    )

    # Inject token
    driver.execute_script(f"""
        // Set reCAPTCHA response
        var textarea = document.getElementById('g-recaptcha-response');
        if (textarea) {{
            textarea.value = '{token}';
            textarea.style.display = 'block';
        }}

        // Set any hidden input fields
        var inputs = document.querySelectorAll('input[name="g-recaptcha-response"]');
        inputs.forEach(function(input) {{ input.value = '{token}'; }});
    """)

    return token

Class Produksi: Deteksi Otomatis + Solve + Submit

Empat fungsi di atas masing-masing menangani satu jenis CAPTCHA. Class SeleniumCaptchaSolver menggabungkannya: detect_captcha() memeriksa page source untuk menentukan jenis CAPTCHA yang tampil, lalu solve_and_submit() memanggil metode solve yang sesuai. Berguna begitu Anda menangani beberapa situs target dengan jenis CAPTCHA berbeda-beda, tanpa script terpisah per situs.

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
import re
import time
import requests

API_KEY = "YOUR_API_KEY"

class SeleniumCaptchaSolver:
    """Complete Selenium + CaptchaAI automation class."""

    def __init__(self, api_key):
        self.api_key = api_key
        self.driver = None

    def start(self):
        """Initialize the browser."""
        options = webdriver.ChromeOptions()
        options.add_experimental_option("excludeSwitches", ["enable-automation"])
        self.driver = webdriver.Chrome(options=options)
        self.driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
            "source": "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
        })

    def stop(self):
        """Close the browser."""
        if self.driver:
            self.driver.quit()

    def navigate(self, url):
        """Navigate to URL and wait for load."""
        self.driver.get(url)
        WebDriverWait(self.driver, 15).until(
            lambda d: d.execute_script("return document.readyState") == "complete"
        )

    def detect_captcha(self):
        """Detect which CAPTCHA type is on the page."""
        source = self.driver.page_source

        if re.search(r'data-sitekey=["\'][A-Za-z0-9_-]{40}["\']', source):
            if "recaptcha/api.js" in source or "grecaptcha" in source:
                return "recaptcha_v2"

        if "cf-turnstile" in source or "challenges.cloudflare.com/turnstile" in source:
            return "turnstile"

        if re.search(r'recaptcha.*v3|render=[A-Za-z0-9_-]{40}', source):
            return "recaptcha_v3"

        captcha_imgs = self.driver.find_elements(
            By.CSS_SELECTOR, "img.captcha, img[alt*='captcha'], img[src*='captcha']"
        )
        if captcha_imgs:
            return "image"

        return None

    def solve_and_submit(self, url, form_data=None):
        """Navigate, solve CAPTCHA, fill form, and submit."""
        self.navigate(url)
        captcha_type = self.detect_captcha()

        if not captcha_type:
            print("No CAPTCHA detected")
            return self._fill_and_submit(form_data)

        print(f"Detected: {captcha_type}")

        if captcha_type == "recaptcha_v2":
            self._solve_recaptcha_v2()
        elif captcha_type == "turnstile":
            self._solve_turnstile()
        elif captcha_type == "image":
            self._solve_image()

        return self._fill_and_submit(form_data)

    def _solve_recaptcha_v2(self):
        source = self.driver.page_source
        match = re.search(r'data-sitekey=["\']([A-Za-z0-9_-]{40})["\']', source)
        sitekey = match.group(1)

        token = self._api_solve("userrecaptcha", googlekey=sitekey, pageurl=self.driver.current_url)

        self.driver.execute_script(f"""
            document.getElementById('g-recaptcha-response').value = '{token}';
        """)

    def _solve_turnstile(self):
        source = self.driver.page_source
        match = re.search(r'data-sitekey=["\']([0-9x][A-Za-z0-9_-]+)["\']', source)
        sitekey = match.group(1)

        token = self._api_solve("turnstile", sitekey=sitekey, pageurl=self.driver.current_url)

        self.driver.execute_script(f"""
            document.querySelectorAll('[name="cf-turnstile-response"]')
                .forEach(function(el) {{ el.value = '{token}'; }});
        """)

    def _solve_image(self):
        img = self.driver.find_element(
            By.CSS_SELECTOR, "img.captcha, img[alt*='captcha'], img[src*='captcha']"
        )
        answer = self._api_solve("base64", body=img.screenshot_as_base64)

        captcha_input = self.driver.find_element(
            By.CSS_SELECTOR, "input[name='captcha'], input[name='code']"
        )
        captcha_input.clear()
        captcha_input.send_keys(answer)

    def _fill_and_submit(self, form_data):
        if form_data:
            for name, value in form_data.items():
                try:
                    field = self.driver.find_element(By.NAME, name)
                    field.clear()
                    field.send_keys(value)
                except Exception:
                    pass

        submit = self.driver.find_element(
            By.CSS_SELECTOR, "button[type='submit'], input[type='submit']"
        )
        submit.click()
        time.sleep(3)
        return self.driver.current_url

    def _api_solve(self, method, **params):
        submit = requests.post("https://ocr.captchaai.com/in.php", data={
            "key": self.api_key, "method": method, "json": 1, **params,
        }, timeout=30)
        data = submit.json()
        if data.get("status") != 1:
            raise Exception(f"Submit error: {data.get('request')}")

        task_id = data["request"]
        for _ in range(30):
            time.sleep(5)
            result = requests.get("https://ocr.captchaai.com/res.php", params={
                "key": self.api_key, "action": "get", "id": task_id, "json": 1,
            }, timeout=30).json()
            if result.get("status") == 1:
                return result["request"]
        raise TimeoutError("Solve timed out")

# Usage
solver = SeleniumCaptchaSolver(API_KEY)
solver.start()
try:
    result_url = solver.solve_and_submit(
        "https://staging.example.com/qa-login",
        form_data={"username": "user@example.com", "password": "pass123"},
    )
    print(f"Result: {result_url}")
finally:
    solver.stop()

Konteks Lokal: Thread, Region, dan Kepatuhan Data

Buat tim automation dan agensi price-monitoring di Indonesia yang mengerjakan beberapa proyek klien sekaligus, model harga per-thread CaptchaAI relevan: biaya tetap berapa pun volume solve, cocok untuk beban kerja musiman yang naik-turun.

Kalau server automation Anda di-deploy ke region Asia Tenggara — AWS ap-southeast-1 (Singapura), ap-southeast-3 (Jakarta), atau GCP asia-southeast2 (Jakarta) — latensi ke API CaptchaAI umumnya lebih rendah, tapi jaringan mobile-first di banyak lokasi kerja tetap membuat request sesekali gagal. Set timeout di setiap request dan bungkus pemanggilan API dengan retry sederhana.

Kalau target scraping Anda memuat data pribadi, ingat UU Pelindungan Data Pribadi (UU 27/2022) — scrape hanya data yang Anda berwenang memprosesnya. Ini bukan nasihat hukum, tapi poin yang sering terlewat saat automation berjalan tanpa review manual.


Masalah Umum dan Solusinya

Gejala Penyebab Solusi
CAPTCHA terdeteksi tapi token gagal disuntikkan Elemen g-recaptcha-response tidak ditemukan Cek apakah reCAPTCHA ada di iframe (iframe[src*='recaptcha'])
Token disuntikkan tapi form tidak submit Callback JavaScript tidak terpicu Panggil callback reCAPTCHA lewat ___grecaptcha_cfg
Situs mendeteksi otomatisasi meski konfigurasi standar Sinyal browser otomatis belum terhapus Tambahkan perintah CDP sebelum halaman dimuat, bukan sesudahnya
Screenshot elemen kosong/hitam Elemen belum terlihat di viewport scrollIntoView() dulu sebelum screenshot
Token Turnstile ditolak server Sitekey salah atau kedaluwarsa Ekstrak ulang dari page source aktif, jangan cache

Sitekey Turnstile bisa berubah antar deploy — selalu ekstrak dari page source yang baru dimuat, bukan nilai lama.


Pertanyaan Seputar Integrasi Selenium + CaptchaAI

Berapa banyak thread yang saya butuhkan untuk automasi Selenium?

Tergantung berapa sesi Selenium yang berjalan paralel, bukan berapa CAPTCHA yang diselesaikan per bulan — tiap plan sudah termasuk solve tanpa batas per thread. Satu sesi sendiri cukup BASIC ($15/bulan, 5 thread); untuk 10–20 browser paralel, ADVANCE ($90/bulan, 50 thread) lebih masuk akal.

Bagaimana cara menangani reCAPTCHA yang muncul di dalam iframe?

Pindah ke iframe reCAPTCHA dulu: driver.switch_to.frame(driver.find_element(By.CSS_SELECTOR, "iframe[src*='recaptcha']")). Setelah token diinjeksikan, kembali ke halaman utama dengan driver.switch_to.default_content() sebelum submit — kalau lupa, Selenium mencari tombol submit di dalam iframe dan gagal menemukannya.

Bisakah CaptchaAI menyelesaikan hCaptcha di alur Selenium ini?

Belum. hCaptcha tidak didukung saat ini, jadi pola solve_captcha() di panduan ini tidak berlaku untuknya — Anda memerlukan layanan lain. Untuk reCAPTCHA v2/v3, Turnstile, dan CAPTCHA gambar yang dibahas di panduan ini, dukungannya penuh.

Apakah mode headless memengaruhi tingkat keberhasilan solve?

Tidak untuk solve-nya — CaptchaAI memproses di worker sendiri, terlepas dari headless atau tidak. Yang bisa berubah: sebagian situs lebih agresif menampilkan tantangan ke traffic headless. Pakai --headless=new dan pastikan sinyal WebGL/Canvas browser Anda valid.

Apa yang harus dilakukan kalau CaptchaAI mengembalikan ERROR_CAPTCHA_UNSOLVABLE?

Jangan langsung retry dengan sitekey dan pageurl yang sama — kalau CAPTCHA-nya memang tidak bisa diselesaikan, submit ulang cuma mengulang kegagalan yang sama. Cek dulu apakah sitekey sudah benar, baru submit sebagai task baru. Untuk error jaringan sesaat, bungkus solve_captcha() dengan try/except dan 1–2 kali percobaan ulang.


Kesimpulan: Kapan Pola Ini Cocok Dipakai

Pakai pola deteksi-solve-submit ini kapan situs target memerlukan JavaScript penuh atau alur multi-halaman. Class SeleniumCaptchaSolver membungkus empat pola solve jadi satu alur deteksi-otomatis yang bisa dipasang ke script scraping atau testing Anda. Python Selenium + CaptchaAI menangani seluruh siklus: deteksi jenis CAPTCHA, solve lewat API, suntikkan token, submit form. Kalau situs Anda cukup sederhana untuk requests saja, lewati Selenium — overhead-nya jauh lebih kecil.

Baca Juga

Komentar dinonaktifkan untuk artikel ini.