Skrip Selenium Anda tetap akan bertemu CAPTCHA, bahkan setelah memakai undetected-chromedriver. Pustaka itu menurunkan frekuensi tantangan, bukan menghapusnya — dan sisanya perlu diselesaikan lewat API. Jawaban praktisnya: pakai undetected-chromedriver untuk sesi browser yang bersih, lalu kirim CAPTCHA yang tetap muncul ke CaptchaAI dan suntikkan token yang dikembalikan ke form.
Empat langkah di bawah ini adalah pola yang sama untuk semua tipe CAPTCHA, dan sisa artikel hanya menerjemahkannya ke konteks Selenium:
- Ambil
sitekeydari halaman yang sedang dibuka driver. - Kirim task ke
in.phpbesertapageurl, lalu simpan task ID-nya. - Polling
res.phpsampai token siap. - Suntikkan token ke field yang benar dan picu callback halaman.
Contoh kodenya memakai Python dengan reCAPTCHA v2 dan Cloudflare Turnstile — dua tipe yang paling sering ditemui tim otomatisasi di Indonesia.
Kenapa dua lapisan ini dipasangkan
Pola dua lapis paling terasa manfaatnya pada pekerjaan yang berulang setiap hari, bukan pada eksperimen sekali jalan:
- Monitoring harga harian untuk klien agensi, dijalankan terjadwal dari instance di AWS
ap-southeast-3(Jakarta) atauap-southeast-1(Singapura). - Regression test terhadap form checkout dan login milik sendiri, di lingkungan staging.
- Job pengumpulan data terjadwal milik tim data startup, yang harus selesai sebelum jam kerja dimulai.
Ketiganya punya ciri sama: CAPTCHA hanya muncul pada sebagian kecil eksekusi. Anda tidak butuh solver di setiap iterasi, Anda butuh solver yang siap ketika tantangan benar-benar muncul.
Di situlah model harga berbasis thread relevan untuk pasar kontrak lokal yang sangat sensitif terhadap biaya. CaptchaAI menagih per thread bersamaan, bukan per solve, dengan solve tanpa batas per thread selama periode langganan. Paket BASIC ($15/bulan, 5 thread) cukup untuk satu atau dua worker Selenium sekuensial; STANDARD ($30/bulan, 15 thread) memberi ruang untuk beberapa job paralel. Biaya bulanan tetap sama entah skrip memicu 50 atau 5.000 CAPTCHA — penting untuk pekerjaan berbayar tetap yang tidak bisa menyerap tagihan variabel.
Satu catatan kepatuhan: ambil hanya data yang memang boleh Anda proses, dan hindari data pribadi. UU Pelindungan Data Pribadi (UU 27/2022) berlaku juga untuk hasil scraping, bukan hanya untuk data yang Anda kumpulkan lewat form.
Yang perlu disiapkan sebelum menjalankan kode
| Persyaratan | Detail |
|---|---|
| API key CaptchaAI | captchaai.com |
| Python 3.8+ | |
| Browser Chrome | Terpasang di sistem Anda |
Instal dependensi:
pip install undetected-chromedriver requests
Kalau Anda belum pernah memanggil endpoint CaptchaAI sama sekali, pahami dulu bentuk request dan format responsnya — polanya sama persis di semua bahasa pemrograman.
Konfigurasi driver Selenium
import undetected_chromedriver as uc
import requests
import time
def create_stealth_browser():
"""Create an undetected Chrome browser instance."""
options = uc.ChromeOptions()
options.add_argument("--no-sandbox")
options.add_argument("--window-size=1920,1080")
driver = uc.Chrome(options=options)
return driver
uc.Chrome() mengurus pencocokan versi driver dengan Chrome yang terpasang, jadi Anda tidak perlu mengunduh binary secara manual. Di server tanpa GUI, tambahkan --disable-dev-shm-usage supaya Chrome tidak kehabisan shared memory di dalam container — kasus yang sering muncul di VPS kecil dengan /dev/shm bawaan 64 MB.
Menyelesaikan reCAPTCHA v2 di sesi Selenium
Langkah 1: buka halaman dan ambil sitekey
API_KEY = "YOUR_API_KEY"
def extract_recaptcha_sitekey(driver):
"""Extract reCAPTCHA v2 sitekey from the page."""
try:
element = driver.find_element("css selector", "[data-sitekey]")
return element.get_attribute("data-sitekey")
except Exception:
# Try finding in iframe src
iframes = driver.find_elements("css selector", "iframe[src*='recaptcha']")
for iframe in iframes:
src = iframe.get_attribute("src")
if "k=" in src:
return src.split("k=")[1].split("&")[0]
return None
Atribut data-sitekey adalah jalur paling langsung. Kalau widget baru dirender belakangan oleh JavaScript, fallback ke src iframe reCAPTCHA dan ambil nilai parameter k=. Beri jeda WebDriverWait sebelum pencarian ini pada halaman yang berat.
Langkah 2: kirim task ke CaptchaAI
def solve_recaptcha_v2(sitekey, pageurl):
"""Submit reCAPTCHA v2 to CaptchaAI and return the token."""
submit = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"json": 1
}).json()
if submit.get("status") != 1:
raise RuntimeError(f"Submit error: {submit.get('request')}")
task_id = submit["request"]
time.sleep(20)
for _ in range(30):
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get", "id": task_id, "json": 1
}).json()
if result.get("status") == 1:
return result["request"]
if result.get("request") != "CAPCHA_NOT_READY":
raise RuntimeError(f"Solve error: {result['request']}")
time.sleep(5)
raise TimeoutError("Solve timed out")
Jeda 20 detik sebelum polling pertama itu disengaja: menanyakan hasil lebih awal hanya menghasilkan CAPCHA_NOT_READY berulang kali dan membuang request. Setelah itu, polling tiap 5 detik sudah cukup rapat. Rincian parameter per tipe ada di panduan reCAPTCHA v2 lewat API.
Langkah 3: suntik token dan picu callback
def inject_recaptcha_token(driver, token):
"""Inject the solved token into the page and submit."""
driver.execute_script(f'''
document.getElementById("g-recaptcha-response").innerHTML = "{token}";
document.getElementById("g-recaptcha-response").style.display = "block";
''')
# If there's a callback function, trigger 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 && client.callback) {{
client.callback("{token}");
}}
}}
}}
''')
Mengisi g-recaptcha-response saja sering tidak cukup. Banyak halaman baru mengaktifkan tombol submit setelah callback reCAPTCHA dipanggil, jadi blok kedua bukan tambahan opsional — itu penyebab paling umum ketika token sudah valid tetapi form tetap menolak.
Contoh utuh: alur login dari nol sampai submit
import undetected_chromedriver as uc
import requests
import time
API_KEY = "YOUR_API_KEY"
def solve_recaptcha(sitekey, pageurl):
submit = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY, "method": "userrecaptcha",
"googlekey": sitekey, "pageurl": pageurl, "json": 1
}).json()
if submit.get("status") != 1:
raise RuntimeError(f"Submit error: {submit.get('request')}")
task_id = submit["request"]
time.sleep(20)
for _ in range(30):
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get", "id": task_id, "json": 1
}).json()
if result.get("status") == 1:
return result["request"]
if result.get("request") != "CAPCHA_NOT_READY":
raise RuntimeError(f"Solve error: {result['request']}")
time.sleep(5)
raise TimeoutError("Solve timed out")
def main():
driver = uc.Chrome()
try:
# Navigate to target page
driver.get("https://staging.example.com/qa-login")
time.sleep(3)
# Fill in form fields
driver.find_element("id", "username").send_keys("user")
driver.find_element("id", "password").send_keys("pass")
# Extract sitekey
element = driver.find_element("css selector", "[data-sitekey]")
sitekey = element.get_attribute("data-sitekey")
pageurl = driver.current_url
print(f"Sitekey: {sitekey}")
# Solve CAPTCHA
token = solve_recaptcha(sitekey, pageurl)
print(f"Token: {token[:50]}...")
# Inject token
driver.execute_script(
f'document.getElementById("g-recaptcha-response").innerHTML = "{token}";'
)
# Submit form
driver.find_element("id", "submit-btn").click()
time.sleep(3)
print(f"Current URL: {driver.current_url}")
finally:
driver.quit()
if __name__ == "__main__":
main()
Perhatikan blok finally: driver.quit() wajib dipanggil. Skrip terjadwal yang gagal di tengah jalan tanpa menutup driver akan meninggalkan proses Chrome menumpuk di server sampai memori habis — masalah operasional yang jauh lebih sering merusak job daripada CAPTCHA itu sendiri.
Sebelum mengarahkan skrip ini ke sistem produksi Anda, jalankan dulu terhadap halaman staging milik sendiri. Alamat staging.example.com/qa-login pada contoh di atas ada untuk alasan itu.
Ketika target memakai Cloudflare Turnstile
Alur untuk widget Cloudflare Turnstile identik; yang berubah hanya method dan nama field token:
def solve_turnstile(sitekey, pageurl):
submit = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY, "method": "turnstile",
"sitekey": sitekey, "pageurl": pageurl, "json": 1
}).json()
if submit.get("status") != 1:
raise RuntimeError(f"Submit error: {submit.get('request')}")
task_id = submit["request"]
time.sleep(10)
for _ in range(30):
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get", "id": task_id, "json": 1
}).json()
if result.get("status") == 1:
return result["request"]
if result.get("request") != "CAPCHA_NOT_READY":
raise RuntimeError(f"Solve error: {result['request']}")
time.sleep(5)
raise TimeoutError("Solve timed out")
# Inject Turnstile token
def inject_turnstile_token(driver, token):
driver.execute_script(f'''
var input = document.querySelector('[name="cf-turnstile-response"]');
if (input) input.value = "{token}";
''')
Tiga perbedaan yang perlu diingat:
- Parameternya bernama
sitekey, bukangooglekey. - Token masuk ke input
cf-turnstile-response, bukang-recaptcha-response. - Jeda awal cukup 10 detik karena Turnstile umumnya selesai lebih cepat.
Penjelasan lengkapnya ada di panduan Cloudflare Turnstile lewat API. Kalau target Anda memakai GeeTest v3, metode geetest mengikuti pola kirim-polling-suntik yang persis sama, hanya dengan set parameter yang berbeda.
Ketika skrip gagal: gejala dan penyebabnya
| Gejala | Sebab | Perbaikan |
|---|---|---|
| Versi Chrome tidak cocok | UC tidak menemukan driver yang sesuai | Perbarui Chrome atau tentukan version_main pada uc.Chrome() |
| CAPTCHA tetap muncul | Situs memeriksa sinyal browser tingkat lanjut | Wajar — biarkan CaptchaAI yang menyelesaikannya |
| Injeksi token gagal | ID elemen atau callback tidak sesuai | Periksa implementasi reCAPTCHA pada halaman tersebut |
WebDriverException |
Proses Chrome berhenti mendadak | Tambahkan --no-sandbox dan --disable-dev-shm-usage |
Polling selalu CAPCHA_NOT_READY |
Batas waktu terlalu pendek atau pageurl salah |
Perbanyak iterasi; pastikan pageurl sama persis dengan URL halaman |
Dua baris terakhir menyumbang sebagian besar pertanyaan yang masuk. Nilai pageurl harus URL halaman tempat widget dirender, bukan URL endpoint form-nya — perbedaan yang mudah terlewat ketika form dikirim ke path lain.
Pertanyaan umum
Berapa lama waktu penyelesaian reCAPTCHA v2 lewat CaptchaAI?
Umumnya beberapa puluh detik. Karena itu skrip di atas menunggu 20 detik sebelum polling pertama, lalu memeriksa tiap 5 detik hingga 30 kali. Turnstile biasanya lebih cepat, sehingga jeda awalnya cukup 10 detik.
Berapa thread yang saya butuhkan untuk satu skrip Selenium?
Satu instance Selenium yang berjalan sekuensial hanya memakai satu thread pada satu waktu, jadi BASIC ($15/bulan, 5 thread) sudah longgar. Naikkan ke STANDARD ($30/bulan, 15 thread) begitu Anda menjalankan beberapa worker paralel, misalnya satu proses per klien.
Bisakah pola ini dijalankan di Selenium Grid atau di dalam Docker?
Bisa, tetapi undetected-chromedriver menambal biner driver secara lokal. Untuk setup terdistribusi, siapkan driver yang sudah dipatch di setiap node atau di dalam image, jangan mengandalkan patching saat runtime pada container yang read-only.
hCaptcha dan FunCaptcha: kenapa tidak ada di contoh?
Karena keduanya memang tidak didukung CaptchaAI saat ini, begitu pula GeeTest v4 yang statusnya masih segera hadir. Yang tersedia adalah reCAPTCHA v2 dan v3 beserta varian Enterprise, Cloudflare Turnstile dan Cloudflare Challenge, GeeTest v3, serta CAPTCHA gambar dan grid — ditambah CaptchaFox (beta), Friendly Captcha (beta), dan Lemin (beta). Rencanakan pendekatan lain sejak awal kalau target Anda memakai tipe yang belum tersedia.
Apakah mode headless mengubah kebutuhan solver saya?
Mode headless cenderung memicu lebih banyak tantangan, jadi alokasikan thread sedikit lebih besar daripada saat menjalankan Chrome dengan jendela. Gunakan options.add_argument("--headless=new") untuk generasi headless terbaru Chrome.
Langkah berikutnya
Daftar di captchaai.com, salin API key ke API_KEY, lalu jalankan skrip login di atas terhadap halaman staging milik Anda sendiri. Setelah satu token berhasil disuntikkan, sisanya hanya soal menyesuaikan selector.