Kesulitan utama GeeTest v3 bukan pada geser slider-nya, melainkan pada dua nilai yang harus Anda tangkap dalam kondisi segar: gt dan challenge. gt adalah identitas situs yang tetap, sedangkan challenge adalah token sesi yang berganti setiap halaman dimuat. Begitu keduanya ada di tangan sebelum kedaluwarsa, CaptchaAI menuntaskan sisanya lewat satu panggilan API. Panduan ini menunjukkan tiga cara andal untuk mengekstrak kedua parameter itu — intercept panggilan register, membaca sumber halaman, dan hook initGeetest — lalu mengirimkannya untuk diselesaikan.
Yang membuat GeeTest v3 sering gagal di production bukan kode solving-nya, tapi timing ekstraksi. Banyak skrip mengambil challenge di awal, menjalankan proses lain selama beberapa detik, baru submit — dan pada titik itu token sudah basi. Karena itu urutan yang benar adalah: ekstrak challenge sesegar mungkin, langsung kirim ke CaptchaAI, lalu suntikkan hasilnya ke form.
Dua parameter yang menentukan: gt dan challenge
| Parameter | Deskripsi | Statis/Dinamis | Contoh |
|---|---|---|---|
gt |
Identifier situs (32 karakter hex) | Statis per situs | 019924a82c70bb123aae90d483b6a0ed |
challenge |
Token sesi (32 karakter hex) | Dinamis per page load | a3f5c8d2e1b04a6789012345678abcdf |
api_server |
Subdomain API GeeTest (opsional) | Statis per situs | api-na.geetest.com |
Nilai gt tetap sama di semua request, jadi Anda cukup mengambilnya sekali dan menyimpannya. Nilai challenge berbeda tiap kali CAPTCHA dimuat — inilah bagian yang harus Anda ekstrak ulang setiap upaya solve. Nilai api_server jarang wajib, tetapi berguna jika target memakai region GeeTest tertentu.
Metode 1: intercept panggilan API register
Sebagian besar implementasi GeeTest v3 mengambil challenge dari sebuah endpoint registrasi saat halaman dimuat. Cara paling bersih adalah menangkap respons endpoint itu, karena di situlah nilai gt dan challenge datang bersamaan dan masih segar. Responsnya terlihat seperti ini:
{
"success": 1,
"challenge": "a3f5c8d2e1b04a6789012345678abcdf",
"gt": "019924a82c70bb123aae90d483b6a0ed",
"new_captcha": true
}
Python (requests)
Untuk situs yang mengekspos URL register di HTML, requests saja sudah cukup — tanpa browser, tanpa overhead:
import requests
import re
session = requests.Session()
html = session.get("https://staging.example.com/qa-login").text
# Find the register endpoint
register_url = re.search(
r'(https?://[^"\']+(?:register|captcha|geetest)[^"\']*)',
html
)
if register_url:
resp = session.get(register_url.group(1)).json()
gt = resp["gt"]
challenge = resp["challenge"]
print(f"gt: {gt}")
print(f"challenge: {challenge}")
JavaScript (Puppeteer: intercept network)
Kalau endpoint register hanya dipanggil setelah JavaScript berjalan, dengarkan lalu lintas jaringan langsung dari browser. Puppeteer memantau setiap respons dan menangkap parameter begitu muncul:
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch();
const page = await browser.newPage();
let geetestParams = {};
page.on('response', async (response) => {
const url = response.url();
if (url.includes('register') || url.includes('captcha')) {
try {
const json = await response.json();
if (json.gt && json.challenge) {
geetestParams = {
gt: json.gt,
challenge: json.challenge,
};
console.log('Captured GeeTest params:', geetestParams);
}
} catch (e) {}
}
});
await page.goto('https://staging.example.com/qa-login', { waitUntil: 'networkidle2' });
console.log('Final params:', geetestParams);
Metode 2: baca parameter dari sumber halaman
Sebagian situs menyematkan parameter langsung di atribut data atau di dalam skrip inline. Untuk kasus ini Anda tidak perlu browser sama sekali — cukup ambil HTML dan cari polanya dengan regex. Pendekatan ini paling ringan untuk pipeline scraping bervolume tinggi:
import re
# From data attributes
gt_match = re.search(r'data-gt=["\']([a-f0-9]{32})', html)
challenge_match = re.search(r'data-challenge=["\']([a-f0-9]{32})', html)
# From JavaScript variables
if not gt_match:
gt_match = re.search(r'gt\s*[=:]\s*["\']([a-f0-9]{32})', html)
if not challenge_match:
challenge_match = re.search(r'challenge\s*[=:]\s*["\']([a-f0-9]{32})', html)
gt = gt_match.group(1) if gt_match else None
challenge = challenge_match.group(1) if challenge_match else None
print(f"gt={gt}, challenge={challenge}")
Regex mencoba dua sumber: atribut data-gt/data-challenge lebih dulu, lalu variabel JavaScript sebagai cadangan. Kalau keduanya kosong, kemungkinan besar parameter baru dibuat secara dinamis — pindah ke Metode 1 atau 3.
Metode 3: hook initGeetest sebelum skrip berjalan
GeeTest v3 diinisialisasi lewat pemanggilan initGeetest(). Dengan menyisipkan hook sebelum skrip halaman dijalankan, Anda menangkap seluruh objek konfigurasi — termasuk gt, challenge, dan product — tanpa menebak pola regex:
// Puppeteer: inject before page scripts
await page.evaluateOnNewDocument(() => {
window.__geetestConfig = null;
const origInit = window.initGeetest;
Object.defineProperty(window, 'initGeetest', {
set(fn) {
this._initGeetest = function(config, callback) {
window.__geetestConfig = config;
console.log('GeeTest config:', JSON.stringify(config));
return fn(config, callback);
};
},
get() { return this._initGeetest; }
});
});
await page.goto('https://staging.example.com/qa-login', { waitUntil: 'networkidle2' });
const config = await page.evaluate(() => window.__geetestConfig);
// config = { gt: "019924a...", challenge: "a3f5c8d...", product: "bind", ... }
Metode ini paling tahan banting saat situs mengacak nama endpoint atau menyembunyikan parameter di dalam bundle JavaScript yang di-minify, karena Anda membaca nilai persis seperti yang diterima widget.
Menyelesaikan GeeTest v3 lewat CaptchaAI
Setelah gt dan challenge segar di tangan, alurnya mengikuti pola empat langkah yang sama seperti tipe CAPTCHA lain di CaptchaAI: kirim ke in.php, simpan task ID, polling di res.php, lalu pakai token. Kirim method: geetest beserta kedua parameter dan pageurl.
Python
import requests
import time
API_KEY = "YOUR_API_KEY"
# Submit
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "geetest",
"gt": gt,
"challenge": challenge,
"pageurl": "https://staging.example.com/qa-login",
"json": "1",
}).json()
if resp["status"] != 1:
raise Exception(f"Submit error: {resp['request']}")
task_id = resp["request"]
# Poll
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"
}).json()
if result["status"] == 1:
solution = result["request"]
print(f"Solution: {solution}")
break
if result["request"] != "CAPCHA_NOT_READY":
raise Exception(f"Error: {result['request']}")
JavaScript
const submit = await axios.post('https://ocr.captchaai.com/in.php', null, {
params: {
key: 'YOUR_API_KEY',
method: 'geetest',
gt: geetestParams.gt,
challenge: geetestParams.challenge,
pageurl: 'https://staging.example.com/qa-login',
json: 1,
}
});
const taskId = submit.data.request;
let solution = null;
for (let i = 0; i < 30; i++) {
await new Promise(r => setTimeout(r, 5000));
const poll = await axios.get('https://ocr.captchaai.com/res.php', {
params: { key: 'YOUR_API_KEY', action: 'get', id: taskId, json: 1 }
});
if (poll.data.status === 1) {
solution = poll.data.request;
break;
}
}
console.log('Solution:', solution);
Skrip polling memeriksa hasil setiap 5 detik hingga 30 kali. Selama status masih CAPCHA_NOT_READY, teruskan loop; begitu status bernilai 1, Anda menerima solusi.
Menyuntikkan solusi ke dalam form
Respons CaptchaAI untuk GeeTest v3 berisi tiga nilai: challenge, validate, dan seccode. Ketiganya harus masuk ke field tersembunyi di form target dengan nama geetest_challenge, geetest_validate, dan geetest_seccode, lalu form dikirim seperti biasa:
# solution is a pipe-delimited string or JSON object
# Parse and inject into the form
import json
sol = json.loads(solution) if isinstance(solution, str) else solution
driver.execute_script("""
const form = document.querySelector('form');
function addHidden(name, value) {
let input = form.querySelector(`input[name="${name}"]`);
if (!input) {
input = document.createElement('input');
input.type = 'hidden';
input.name = name;
form.appendChild(input);
}
input.value = value;
}
addHidden('geetest_challenge', arguments[0]);
addHidden('geetest_validate', arguments[1]);
addHidden('geetest_seccode', arguments[2]);
""", sol["challenge"], sol["validate"], sol["seccode"])
Menjaga masa berlaku challenge di kondisi jaringan nyata
Untuk tim di Indonesia yang menjalankan scraper price-monitoring atau pipeline QA dari region seperti AWS ap-southeast-3 (Jakarta) atau ap-southeast-1 (Singapura), jendela 60–120 detik masa berlaku challenge jauh lebih ketat daripada yang terlihat. Latency ke server GeeTest, ditambah antrean polling, bisa memakan sebagian besar jendela itu sebelum solusi kembali. Praktik terbaiknya: jangan pisahkan ekstraksi dan submission dengan langkah lambat apa pun. Ekstrak challenge, kirim ke CaptchaAI di baris berikutnya, dan hindari menyimpan token untuk dipakai ulang di request lain.
Kalau Anda menyeleksi data untuk klien freelance atau agensi, satu catatan kepatuhan yang relevan: sesuai UU Pelindungan Data Pribadi (UU 27/2022), ambil hanya data yang memang berhak Anda proses dan hindari data pribadi di balik login milik orang lain. Panduan ini bukan nasihat hukum, tetapi batas itu perlu ditetapkan sejak awal desain alur.
Biaya: model per thread, bukan per solve
Ini poin yang sering menentukan pilihan untuk tim dengan volume besar. CaptchaAI menagih per thread yang berjalan bersamaan, bukan per solve. Paket BASIC ($15/bulan, 5 thread) sudah mencakup solve tanpa batas per thread selama sebulan, dan paket naik ke STANDARD ($30/bulan, 15 thread) serta ADVANCE ($90/bulan, 50 thread) saat Anda butuh lebih banyak solve paralel. Artinya, memproses 500 atau 50.000 challenge GeeTest v3 dalam sebulan tidak mengubah tagihan — biaya Anda ditentukan oleh berapa banyak solve yang berjalan sekaligus, bukan totalnya. Untuk scraper yang berjalan terus-menerus, model biaya tetap ini biasanya lebih mudah diprediksi ketimbang skema per-solve.
Mengatasi error yang sering muncul
| Masalah | Penyebab | Perbaikan |
|---|---|---|
ERROR_BAD_PARAMETERS |
gt atau challenge tidak ada |
Keduanya wajib; ekstrak nilai baru sebelum submit |
| Challenge basi | Token challenge sudah kedaluwarsa | Ekstrak challenge baru tepat sebelum submit |
Nilai gt salah |
Disalin dari situs berbeda | gt spesifik per situs; ekstrak ulang dari target |
| Solusi ditolak situs | Challenge kedaluwarsa saat proses solve | Persempit jeda antara ekstraksi dan submission |
Pertanyaan umum
Bagaimana status GeeTest v4 di CaptchaAI?
Belum tersedia. Saat ini yang didukung adalah GeeTest v3; GeeTest v4 berstatus segera hadir. GeeTest v4 memakai captcha_id dan punya beberapa tipe challenge (klik, cocokkan, geser), sehingga alurnya berbeda dari v3 dan perlu ditangani terpisah begitu dukungan resmi tersedia.
Berapa biaya menyelesaikan GeeTest v3 di CaptchaAI?
CaptchaAI menagih per thread, bukan per solve. Paket BASIC ($15/bulan, 5 thread) memberi solve tanpa batas selama sebulan, jadi biaya tetap sama berapa pun jumlah challenge yang Anda proses — yang menentukan hanyalah berapa banyak solve paralel yang Anda butuhkan.
Kenapa parameter saya ditolak padahal terlihat benar?
Penyebab paling umum adalah challenge yang sudah basi. Token ini biasanya hanya valid sekitar 60–120 detik, jadi ekstrak nilai baru tepat sebelum submit, bukan di awal skrip. Pastikan juga gt benar-benar diambil dari situs target, bukan disalin dari halaman lain.
Apakah saya butuh browser headless untuk ekstraksi?
Tidak selalu. Metode 2 membaca gt dan challenge langsung dari HTML dengan requests saja. Anda baru memerlukan Puppeteer atau Playwright ketika parameter hanya muncul setelah JavaScript berjalan, seperti pada Metode 1 dan 3.
Berapa lama proses solve GeeTest v3 berjalan?
Umumnya beberapa puluh detik. Skrip polling di atas memeriksa hasil setiap 5 detik hingga 30 kali, dan Anda memakai token begitu status bernilai 1. Untuk menjaga throughput, jalankan beberapa solve secara paralel sesuai jumlah thread pada paket Anda.
Mulai selesaikan GeeTest v3 dengan CaptchaAI
Dapatkan API key Anda di captchaai.com, lalu jalankan ekstraksi dan solve dari contoh di atas.