Use Cases

Pengiriman Formulir Otomatis dengan Penanganan CAPTCHA

Formulir web yang dipasangi CAPTCHA tidak akan menerima kiriman dari skrip Anda sampai tantangannya diselesaikan lebih dulu. Alurnya sebenarnya pendek: deteksi jenis CAPTCHA di halaman, kirim ke CaptchaAI untuk diselesaikan, suntikkan token hasilnya ke formulir, lalu klik submit. Panduan ini merangkai alur itu dengan Selenium dan Python sehingga satu skrip bisa menangani reCAPTCHA v2, Cloudflare Turnstile, maupun CAPTCHA gambar sekaligus.


Kenapa formulir ber-CAPTCHA menghentikan skrip

CAPTCHA memang dipasang untuk menyaring lalu lintas otomatis. Baik Anda menguji formulir kontak, menjalankan regresi QA di alur pendaftaran, atau memproses form pada situs milik sendiri, langkah "submit" akan selalu gagal selama field respons CAPTCHA masih kosong. Selenium bisa mengisi setiap kolom dan menekan tombol kirim, tetapi ia tidak menyelesaikan tantangan visual atau skor risiko di baliknya.

Di sinilah solver eksternal masuk ke dalam alur. CaptchaAI menerima parameter CAPTCHA (misalnya sitekey dan URL halaman), menyelesaikannya, lalu mengembalikan token yang bisa Anda tempelkan ke formulir persis seperti yang dilakukan pengguna asli. Skrip Anda tetap memegang kendali penuh atas pengisian field dan pengiriman; solver hanya mengurus satu bagian yang tidak bisa ditiru oleh automation biasa.


Alur kerja dari muat form sampai kirim

Empat langkah berikut adalah kerangka yang konsisten untuk hampir semua formulir:

┌────────────┐     ┌──────────────┐     ┌────────────┐     ┌──────────────┐
│ Load Form  │────▶│ Fill Fields  │────▶│ Detect &   │────▶│ Submit Form  │
│ (Selenium) │     │              │     │ Solve      │     │              │
│            │     │              │     │ CAPTCHA    │     │              │
└────────────┘     └──────────────┘     └────────────┘     └──────────────┘

Di dalam tahap "Detect & Solve" berlaku pola empat langkah standar CaptchaAI: kirim task ke in.php, simpan task ID yang dikembalikan, lakukan polling ke res.php sampai hasil siap, lalu pakai token yang dikembalikan. Menyisipkan token sebagai langkah terakhir sebelum submit itu penting — token reCAPTCHA dan Turnstile punya masa berlaku, jadi semakin cepat formulir dikirim setelah token didapat, semakin kecil peluang ditolak.


Membangun komponen inti

Kita pisahkan tanggung jawab menjadi tiga kelas: satu untuk berbicara dengan API solver, satu untuk mengenali jenis CAPTCHA di halaman, dan satu untuk mengorkestrasi pengisian serta pengiriman formulir.

Solver CAPTCHA

Kelas ini membungkus pola kirim-lalu-polling. Ia mengirim parameter ke in.php, lalu menunggu hasil di res.php dengan jeda yang wajar hingga token siap:

import time
import requests


class FormCaptchaSolver:
    BASE = "https://ocr.captchaai.com"

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

    def solve(self, params, initial_wait=10):
        params["key"] = self.api_key
        params["json"] = 1
        resp = requests.post(f"{self.BASE}/in.php", data=params).json()
        if resp["status"] != 1:
            raise Exception(f"Submit error: {resp['request']}")

        task_id = resp["request"]
        time.sleep(initial_wait)

        for _ in range(60):
            result = requests.get(
                f"{self.BASE}/res.php",
                params={"key": self.api_key, "action": "get", "id": task_id, "json": 1},
            ).json()
            if result["request"] == "CAPCHA_NOT_READY":
                time.sleep(5)
                continue
            if result["status"] == 1:
                return result["request"]
            raise Exception(f"Solve error: {result['request']}")
        raise TimeoutError("CAPTCHA solve timed out")

Detektor jenis CAPTCHA

Sebelum bisa menyelesaikan, skrip harus tahu CAPTCHA apa yang sedang dihadapi. Detektor ini memeriksa halaman untuk penanda Turnstile, reCAPTCHA, lalu CAPTCHA gambar — dengan urutan yang sengaja dibuat spesifik-ke-umum agar Turnstile tidak salah dikenali sebagai reCAPTCHA (keduanya sama-sama memakai atribut data-sitekey):

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


class CaptchaDetector:
    def __init__(self, driver):
        self.driver = driver

    def detect(self):
        """Detect CAPTCHA type on current page."""
        html = self.driver.page_source

        # Turnstile
        turnstile = self.driver.find_elements(By.CSS_SELECTOR, ".cf-turnstile, [data-sitekey]")
        for el in turnstile:
            if "cf-turnstile" in (el.get_attribute("class") or ""):
                return "turnstile", el.get_attribute("data-sitekey")

        # reCAPTCHA
        recaptcha = self.driver.find_elements(By.CSS_SELECTOR, "[data-sitekey]")
        if recaptcha:
            sitekey = recaptcha[0].get_attribute("data-sitekey")
            if "recaptcha" in html.lower():
                return "recaptcha_v2", sitekey

        # Image CAPTCHA
        img = self.driver.find_elements(By.CSS_SELECTOR, "img[src*='captcha'], img.captcha")
        if img:
            return "image", img[0].get_attribute("src")

        return "none", None

Automator formulir

Kelas terakhir menyatukan semuanya: mengisi field, memanggil solver sesuai jenis CAPTCHA yang terdeteksi, menyuntikkan token, lalu menekan tombol submit. Perhatikan bahwa setiap cabang memakai field respons yang benar — g-recaptcha-response untuk reCAPTCHA dan cf-turnstile-response untuk Turnstile:

import base64
import requests as req
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


class FormAutomator:
    def __init__(self, api_key):
        self.solver = FormCaptchaSolver(api_key)
        self.driver = webdriver.Chrome()
        self.detector = CaptchaDetector(self.driver)

    def fill_field(self, selector, value):
        field = WebDriverWait(self.driver, 10).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, selector))
        )
        field.clear()
        field.send_keys(value)

    def select_option(self, selector, value):
        from selenium.webdriver.support.ui import Select
        select = Select(self.driver.find_element(By.CSS_SELECTOR, selector))
        select.select_by_value(value)

    def solve_captcha(self):
        captcha_type, data = self.detector.detect()
        page_url = self.driver.current_url

        if captcha_type == "recaptcha_v2":
            token = self.solver.solve({
                "method": "userrecaptcha",
                "googlekey": data,
                "pageurl": page_url,
            })
            self.driver.execute_script(
                f'document.querySelector("[name=g-recaptcha-response]").value = "{token}";'
            )
            return True

        if captcha_type == "turnstile":
            token = self.solver.solve({
                "method": "turnstile",
                "sitekey": data,
                "pageurl": page_url,
            })
            self.driver.execute_script(
                f'document.querySelector("[name=cf-turnstile-response]").value = "{token}";'
            )
            return True

        if captcha_type == "image":
            img_data = req.get(data).content
            img_b64 = base64.b64encode(img_data).decode()
            text = self.solver.solve({"method": "base64", "body": img_b64})
            captcha_input = self.driver.find_element(
                By.CSS_SELECTOR, "input[name*='captcha']"
            )
            captcha_input.clear()
            captcha_input.send_keys(text)
            return True

        return False  # No CAPTCHA detected

    def submit_form(self, url, fields, submit_selector="button[type='submit']"):
        """
        fields: list of (selector, value) tuples
        """
        self.driver.get(url)

        for selector, value in fields:
            self.fill_field(selector, value)

        self.solve_captcha()

        submit = self.driver.find_element(By.CSS_SELECTOR, submit_selector)
        submit.click()

        return self.driver.current_url

    def close(self):
        self.driver.quit()

Contoh lengkap: formulir kontak

Dengan tiga kelas di atas, memproses satu formulir kontak jadi ringkas. Berikan daftar pasangan (selektor, nilai), tentukan selektor tombol kirim, dan biarkan automator mengurus deteksi serta penyelesaian CAPTCHA:

automator = FormAutomator("YOUR_API_KEY")

try:
    result_url = automator.submit_form(
        url="https://example.com/contact",
        fields=[
            ("#name", "John Doe"),
            ("#email", "john@example.com"),
            ("#subject", "Sales inquiry"),
            ("#message", "I'd like to learn more about your services."),
        ],
        submit_selector="#submit-btn",
    )
    print(f"Form submitted. Redirected to: {result_url}")
finally:
    automator.close()

Menyesuaikan untuk jenis formulir lain

Pola yang sama berlaku untuk formulir login, pendaftaran, hingga pencarian — yang berubah hanya daftar field dan selektor tombolnya.

Formulir login

result = automator.submit_form(
    url="https://staging.example.com/qa-login",
    fields=[
        ("#username", "testuser"),
        ("#password", "testpass123"),
    ],
    submit_selector="#login-btn",
)

Formulir pendaftaran

result = automator.submit_form(
    url="https://example.com/register",
    fields=[
        ("#first-name", "Jane"),
        ("#last-name", "Smith"),
        ("#email", "jane@example.com"),
        ("#password", "SecurePass!123"),
        ("#confirm-password", "SecurePass!123"),
    ],
    submit_selector="#register-btn",
)

Formulir pencarian ber-CAPTCHA

result = automator.submit_form(
    url="https://example.com/search",
    fields=[
        ("#query", "python developer"),
        ("#location", "San Francisco"),
    ],
    submit_selector="#search-btn",
)

Skenario nyata: tim QA yang menjalankan ratusan submit per hari

Bayangkan sebuah agensi price-monitoring atau tim data freelance di Jakarta yang menjalankan pengujian formulir dalam jumlah besar setiap hari — misalnya memverifikasi bahwa alur pendaftaran dan kontak di properti milik klien tetap berfungsi setelah setiap rilis. Di skala itu, model harga berbasis thread jauh lebih mudah diprediksi daripada tarif per-solve. Paket BASIC ($15/bulan, 5 thread) cukup untuk alur QA kecil, sedangkan ADVANCE ($90/bulan, 50 thread) menampung pengujian paralel yang jauh lebih padat — semuanya dengan solve tak terbatas per thread, tanpa biaya tambahan per jenis CAPTCHA.

Kalau worker Selenium Anda berjalan di region seperti AWS ap-southeast-3 (Jakarta), latensi jaringan ke endpoint solve umumnya kecil dibanding waktu penyelesaian CAPTCHA itu sendiri, jadi throughput lebih ditentukan oleh alokasi thread daripada oleh jarak. Satu catatan kepatuhan: otomatiskan hanya formulir yang Anda miliki atau yang izinnya sudah Anda kantongi, dan hindari memproses data pribadi tanpa dasar yang sah — relevan dengan UU Pelindungan Data Pribadi (UU 27/2022). Ini bukan nasihat hukum, hanya batas yang sehat untuk alur otomatisasi.


Mengatasi masalah umum

Masalah Penyebab Solusi
Token ditolak Token kedaluwarsa sebelum formulir terkirim Selesaikan CAPTCHA sebagai langkah terakhir, langsung submit
Field tidak ditemukan Halaman memuat kontennya secara dinamis Tambahkan explicit wait pada elemen target
Jenis CAPTCHA salah terdeteksi Ada beberapa elemen CAPTCHA di halaman Periksa kembali urutan deteksi di detektor
Formulir memuat ulang setelah submit Validasi sisi server gagal Pastikan semua field wajib sudah terisi benar
Callback reCAPTCHA tidak terpicu Fungsi callback perlu dipanggil manual Jalankan grecaptcha.execute() setelah token disuntik

Pertanyaan umum

Bisakah formulir dikirim tanpa membuka browser?

Untuk reCAPTCHA dan Turnstile, token bisa diselesaikan lewat HTTP POST langsung tanpa menjalankan browser. Tetapi jika formulir mengandalkan validasi JavaScript sebelum submit, Anda tetap butuh browser seperti pada contoh Selenium di atas.

Jenis CAPTCHA apa saja yang bisa ditangani skrip ini?

Contoh di sini menangani reCAPTCHA v2, Cloudflare Turnstile, dan CAPTCHA gambar/OCR. Di luar itu CaptchaAI juga menyelesaikan reCAPTCHA v3, Cloudflare Challenge, dan GeeTest v3. hCaptcha dan FunCaptcha belum didukung, jadi jangan mengandalkannya untuk formulir yang memakai keduanya.

Berapa biaya untuk menyelesaikan CAPTCHA form bervolume tinggi?

Biaya tidak dihitung per CAPTCHA. CaptchaAI menagih per thread bersamaan dengan solve tak terbatas per thread, sehingga tagihan bulanan tetap berapa pun jumlah formulir yang diproses. Mulai dari BASIC ($15/bulan, 5 thread); untuk pengujian paralel yang padat, naik ke ADVANCE ($90/bulan, 50 thread) atau lebih tinggi.

Bagaimana menangani formulir yang mengirim via AJAX?

Untuk pengiriman AJAX, jangan mengisi kolom tersembunyi. Intersepsi permintaan XHR-nya lalu sisipkan token CAPTCHA ke dalam payload permintaan sebelum dikirim.

Apakah otomatisasi formulir seperti ini sesuai aturan?

Selama formulirnya milik Anda atau pengujiannya sudah diizinkan pemilik situs, ini adalah alur QA dan testing yang wajar. Hindari memproses data pribadi orang lain tanpa dasar yang sah dan jangan menargetkan situs yang tidak Anda kelola.


Panduan terkait


Otomatiskan formulir apa pun — selesaikan CAPTCHA dengan CaptchaAI.

Komentar dinonaktifkan untuk artikel ini.