Solve GeeTest v3 lewat CaptchaAI hanya butuh satu panggilan API. Bagian yang justru menggagalkan banyak skrip automation muncul sesudahnya: menaruh tiga nilai hasil solve — geetest_challenge, geetest_validate, dan geetest_seccode — tepat di tempat yang diharapkan halaman. Tempatnya bisa berupa hidden field di dalam form, callback JavaScript milik widget, atau payload XHR saat form dikirim. Artikel ini menunjukkan pola injeksi yang benar untuk Playwright, Puppeteer, dan Selenium, lengkap dengan kode Python dan JavaScript yang bisa langsung Anda pakai.
Konteksnya sederhana: tim QA dan engineer scraping di Indonesia sering menghadapi GeeTest v3 di halaman login dan registrasi, dan yang mereka butuhkan bukan teori, tapi cara memasukkan hasil solve ke sesi browser tanpa membuat widget menolak. Satu catatan kepatuhan: sesuai UU Pelindungan Data Pribadi (UU 27/2022), jalankan otomatisasi hanya pada sistem dan data yang memang Anda kelola atau miliki izinnya.
Secara garis besar, alurnya selalu empat langkah yang sama:
- Ekstrak
gtdanchallengedari halaman. - Kirim keduanya ke CaptchaAI dan tunggu hasil solve.
- Suntikkan tiga nilai balasan ke tempat yang tepat.
- Submit form atau request, lalu verifikasi server menerima.
Sebelum mulai: yang perlu disiapkan
Sebelum menyentuh kode, pastikan hal-hal berikut sudah siap:
- API key CaptchaAI yang aktif dari dashboard Anda.
- Salah satu framework terpasang: Playwright, Puppeteer, atau Selenium.
- Akses ke halaman uji milik sendiri (mis.
staging.example.com/qa-login) yang memuat widget GeeTest v3.
Tiga nilai yang dikembalikan GeeTest v3
CaptchaAI mengembalikan hasil dalam tiga bagian:
{
"geetest_challenge": "a1b2c3d4e5...modified_challenge",
"geetest_validate": "abc123def456_validate",
"geetest_seccode": "abc123def456_validate|jordan"
}
Ketiganya wajib disuntikkan bersama. Jika satu saja hilang, server menolak verifikasi. Masing-masing punya peran: geetest_challenge menandai sesi, geetest_validate membuktikan challenge terselesaikan, dan geetest_seccode menyimpan hash untuk mendeteksi perubahan nilai.
Tiga tempat halaman mengharapkan token
Sebelum menulis kode, tentukan dulu mekanisme mana yang dipakai situs target, karena itu menentukan cara injeksi:
- Hidden form field — nilai ditaruh di
<input type="hidden">lalu ikut terkirim saat form di-submit. Pola paling umum. - Callback JavaScript — widget memanggil fungsi seperti
getValidate()atau callbackonSuccessmilik objekcaptchaObj. Situs membaca hasil dari sana, bukan dari field. - XHR payload — form dikirim lewat request POST asinkron, sehingga nilai harus disisipkan ke body request, bukan ke DOM.
Empat langkah berikut memakai urutan yang sama untuk setiap framework: ekstrak gt dan challenge, solve, lalu suntik tiga nilai.
Langkah 1: ekstrak gt dan challenge lalu solve
Bagian ini berlaku untuk semua framework. Ambil gt dan challenge, kirim ke CaptchaAI, dan tunggu hasilnya lewat polling:
import requests
import time
def solve_geetest(gt, challenge, page_url):
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": "YOUR_API_KEY",
"method": "geetest",
"gt": gt,
"challenge": challenge,
"pageurl": page_url,
"json": 1
})
task_id = resp.json()["request"]
for _ in range(60):
time.sleep(3)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": "YOUR_API_KEY",
"action": "get",
"id": task_id,
"json": 1
})
data = result.json()
if data["status"] == 1:
return data["request"] # Returns dict with three values
raise TimeoutError("GeeTest solve timed out")
Langkah 2: injeksi di Playwright (Python)
Tangkap parameter dari respons register
GeeTest memuat parameter lewat respons register. Sadap respons tersebut untuk mendapat gt dan challenge:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
# Capture GeeTest registration response
geetest_data = {}
def capture_geetest(response):
if "register" in response.url and response.status == 200:
try:
data = response.json()
if "gt" in data and "challenge" in data:
geetest_data.update(data)
except Exception:
pass
page.on("response", capture_geetest)
page.goto("https://staging.example.com/qa-login")
page.wait_for_selector(".geetest_holder")
gt = geetest_data["gt"]
challenge = geetest_data["challenge"]
Suntikkan ke hidden field
Setelah solve selesai, tulis ketiga nilai ke hidden field lalu submit:
# Solve with CaptchaAI
solution = solve_geetest(gt, challenge, page.url)
# Method 1: Set hidden form fields
page.evaluate(f"""
const fields = {{
'geetest_challenge': '{solution["geetest_challenge"]}',
'geetest_validate': '{solution["geetest_validate"]}',
'geetest_seccode': '{solution["geetest_seccode"]}'
}};
for (const [name, value] of Object.entries(fields)) {{
let input = document.querySelector(`input[name="${{name}}"]`);
if (!input) {{
input = document.createElement('input');
input.type = 'hidden';
input.name = name;
document.querySelector('form').appendChild(input);
}}
input.value = value;
}}
""")
# Submit form
page.click("#submit-button")
Metode 2: picu callback GeeTest
Sebagian situs membaca hasil dari callback JavaScript, bukan dari form field. Untuk kasus ini, override getValidate dan picu event sukses:
page.evaluate(f"""
// Find the GeeTest captcha object
if (window.captchaObj) {{
// Simulate a successful solve
const result = {{
geetest_challenge: '{solution["geetest_challenge"]}',
geetest_validate: '{solution["geetest_validate"]}',
geetest_seccode: '{solution["geetest_seccode"]}'
}};
// Override getValidate to return our solution
window.captchaObj.getValidate = function() {{ return result; }};
// Trigger the success callback
const successEvent = new Event('geetest_success');
document.dispatchEvent(successEvent);
}}
""")
Langkah 3: injeksi di Puppeteer (JavaScript)
Polanya sama seperti Playwright: sadap respons register, solve, lalu isi hidden field sebelum klik submit.
const puppeteer = require('puppeteer');
async function solveAndInject() {
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
// Capture GeeTest params
let gt, challenge;
page.on('response', async (response) => {
if (response.url().includes('register') && response.status() === 200) {
try {
const data = await response.json();
if (data.gt && data.challenge) {
gt = data.gt;
challenge = data.challenge;
}
} catch (e) {}
}
});
await page.goto('https://staging.example.com/qa-login');
await page.waitForSelector('.geetest_holder');
// Solve with CaptchaAI (implementation from earlier)
const solution = await solveCaptcha(gt, challenge, page.url());
// Inject the three values
await page.evaluate((sol) => {
// Set hidden inputs
const form = document.querySelector('form');
['geetest_challenge', 'geetest_validate', 'geetest_seccode'].forEach(name => {
let input = document.querySelector(`input[name="${name}"]`);
if (!input) {
input = document.createElement('input');
input.type = 'hidden';
input.name = name;
form.appendChild(input);
}
input.value = sol[name];
});
}, solution);
await page.click('#submit-button');
}
Langkah 4: injeksi di Selenium (Python)
Selenium tidak punya penyadap respons sebawaan seperti dua framework sebelumnya, jadi gt dan challenge diambil langsung dari atribut data-* pada elemen widget:
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
driver = webdriver.Chrome()
driver.get("https://staging.example.com/qa-login")
# Wait for GeeTest widget
WebDriverWait(driver, 15).until(
EC.presence_of_element_located((By.CLASS_NAME, "geetest_holder"))
)
# Extract gt and challenge from the page
gt = driver.execute_script(
"return document.querySelector('[data-gt]')?.dataset.gt"
)
challenge = driver.execute_script(
"return document.querySelector('[data-challenge]')?.dataset.challenge"
)
# Solve with CaptchaAI
solution = solve_geetest(gt, challenge, driver.current_url)
# Inject via JavaScript
driver.execute_script(f"""
var fields = {{
'geetest_challenge': '{solution["geetest_challenge"]}',
'geetest_validate': '{solution["geetest_validate"]}',
'geetest_seccode': '{solution["geetest_seccode"]}'
}};
var form = document.querySelector('form');
for (var name in fields) {{
var input = document.querySelector('input[name="' + name + '"]');
if (!input) {{
input = document.createElement('input');
input.type = 'hidden';
input.name = name;
form.appendChild(input);
}}
input.value = fields[name];
}}
""")
driver.find_element(By.ID, "submit-button").click()
Saat situs submit lewat XHR
Jika form dikirim via XHR alih-alih POST form biasa, DOM tidak akan pernah membawa nilai Anda. Sadap request-nya dan sisipkan tiga nilai ke dalam body sebelum diteruskan:
# Playwright: Intercept the XHR and inject values
def handle_route(route):
if "login" in route.request.url and route.request.method == "POST":
# Modify the POST data to include our solution
post_data = route.request.post_data
# Add GeeTest values to the request
route.continue_(post_data=modified_data)
else:
route.continue_()
page.route("**/api/login**", handle_route)
Untuk beban kerja QA login yang berjalan paralel — misalnya dari region seperti AWS ap-southeast-1 (Singapura) atau ap-southeast-3 (Jakarta) — model harga CaptchaAI berbasis thread memudahkan perencanaan biaya: paket BASIC ($15/bulan, 5 threads) untuk pengujian kecil hingga PREMIUM ($170/bulan, 100 threads) untuk volume tinggi, tanpa biaya per solve.
Cara memastikan injeksi berhasil
Sebelum menganggap alur selesai, konfirmasi tiga hal berikut secara berurutan:
- Buka DevTools, tab Network, dan pastikan request submit membawa ketiga nilai di body atau form data.
- Periksa respons server — kode sukses atau redirect ke halaman berikutnya menandakan verifikasi lolos.
- Ulangi pada sesi baru untuk memastikan hasilnya konsisten, bukan kebetulan satu kali.
Nilai GeeTest v3 cepat kedaluwarsa. Jalankan ekstraksi, solve, dan injeksi tanpa jeda panjang agar
challengemasih berlaku saat disuntikkan.
Tabel pemecahan masalah
| Masalah | Penyebab | Perbaikan |
|---|---|---|
| "GeeTest validation failed" | Satu dari tiga nilai tidak ada | Verifikasi ketiganya disuntikkan |
| Challenge kedaluwarsa sebelum injeksi | Jeda terlalu lama antara ekstraksi dan solve | Ekstrak dan solve secara berurutan |
| Form submit tapi nilai tidak disertakan | Nama field atau selector form salah | Cek form sesungguhnya untuk nama yang benar |
| Callback tidak terpicu | Situs menggunakan nama callback kustom | Temukan callback di opsi initGeetest |
| Nilai disuntikkan tapi widget masih tampil | State widget tidak diperbarui | Trigger success callback secara programatik |
Pertanyaan umum
Apakah panduan ini berlaku untuk versi GeeTest terbaru?
Belum. Pola tiga nilai di sini khusus GeeTest v3, yang saat ini diselesaikan CaptchaAI. Untuk versi v4, dukungan masih berstatus segera hadir sehingga alurnya belum tersedia.
Kapan memilih injeksi form field, callback, atau XHR?
Pakai hidden field jika situs mengirim form biasa, callback jika situs membaca hasil dari objek captchaObj, dan intercept XHR jika submit dilakukan lewat request asinkron. Kalau ragu, buka DevTools dan lihat request apa yang muncul saat form dikirim manual.
Kenapa GeeTest v3 memakai tiga nilai terpisah, bukan satu token?
Karena tiap nilai punya fungsi berbeda dan saling mengunci: geetest_challenge mengikat sesi, geetest_validate menjadi bukti penyelesaian, dan geetest_seccode adalah hash anti-tampering. Server memeriksa ketiganya, jadi injeksi parsial selalu gagal.
Bisakah hasil solve GeeTest dipakai ulang untuk banyak request?
Tidak disarankan. Nilai GeeTest terikat pada sesi dan challenge tertentu serta cepat kedaluwarsa, sehingga solve baru per sesi jauh lebih andal daripada menyimpan dan memakai ulang token lama.
Artikel terkait
Langkah selanjutnya
Injeksi solusi GeeTest v3 di framework apa pun — ambil API key CaptchaAI Anda dan mulai solve challenge pertama.