Beberapa situs menggunakan reCAPTCHA di halaman loginnya dan Turnstile di halaman checkoutnya. Tes A/B lainnya antar penyedia — URL yang sama menampilkan reCAPTCHA untuk satu pengunjung dan Turnstile untuk pengunjung lainnya. Melakukan hardcoding pada metode pemecah tunggal berarti otomatisasi Anda rusak setiap kali menemukan jenis lainnya.
Mengapa Situs Menggunakan Beberapa Penyedia CAPTCHA
| Skenario | Bagaimana Kelihatannya |
|---|---|
| Beda halaman, beda penyedia | Masuk = reCAPTCHA, checkout = Turnstile |
| Penyedia pengujian A/B | Halaman yang sama secara acak menampilkan jenis mana pun |
| Migrasi sedang berlangsung | Halaman lama memiliki reCAPTCHA, halaman baru memiliki Turnstile |
| Penggantian pada kegagalan | Penyedia primer gagal → kembali ke sekunder |
| Variasi regional | reCAPTCHA untuk pengunjung AS, Turnstile untuk UE (GDPR) |
Python: Deteksi dan Solve Otomatis
import requests
import time
import re
from dataclasses import dataclass
API_KEY = "YOUR_API_KEY"
SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"
@dataclass
class CaptchaInfo:
provider: str # "recaptcha" or "turnstile"
method: str # API method name
sitekey: str
pageurl: str
response_field: str # Form field name for the token
def detect_captcha_type(html, pageurl):
"""
Detect which CAPTCHA provider is on the page.
Returns CaptchaInfo or None.
"""
# Check for Turnstile
turnstile_match = re.search(
r'class=["\'][^"\']*cf-turnstile[^"\']*["\'][^>]*data-sitekey=["\']([^"\']+)["\']',
html,
)
if not turnstile_match:
turnstile_match = re.search(
r'data-sitekey=["\']([^"\']+)["\'][^>]*class=["\'][^"\']*cf-turnstile',
html,
)
if turnstile_match:
return CaptchaInfo(
provider="turnstile",
method="turnstile",
sitekey=turnstile_match.group(1),
pageurl=pageurl,
response_field="cf-turnstile-response",
)
# Check for reCAPTCHA
recaptcha_match = re.search(
r'class=["\'][^"\']*g-recaptcha[^"\']*["\'][^>]*data-sitekey=["\']([^"\']+)["\']',
html,
)
if not recaptcha_match:
recaptcha_match = re.search(
r'data-sitekey=["\']([^"\']+)["\'][^>]*class=["\'][^"\']*g-recaptcha',
html,
)
# Also check for script-rendered reCAPTCHA
if not recaptcha_match:
recaptcha_match = re.search(
r'grecaptcha\.render\([^,]+,\s*\{[^}]*["\']sitekey["\']\s*:\s*["\']([^"\']+)["\']',
html,
)
if recaptcha_match:
return CaptchaInfo(
provider="recaptcha",
method="userrecaptcha",
sitekey=recaptcha_match.group(1),
pageurl=pageurl,
response_field="g-recaptcha-response",
)
return None
def solve_captcha(info):
"""Solve any detected CAPTCHA type via CaptchaAI."""
params = {
"key": API_KEY,
"method": info.method,
"json": 1,
}
if info.method == "userrecaptcha":
params["googlekey"] = info.sitekey
params["pageurl"] = info.pageurl
elif info.method == "turnstile":
params["sitekey"] = info.sitekey
params["pageurl"] = info.pageurl
resp = requests.post(SUBMIT_URL, data=params, timeout=30).json()
if resp.get("status") != 1:
raise RuntimeError(f"Submit failed: {resp.get('request')}")
task_id = resp["request"]
for _ in range(60):
time.sleep(5)
poll = requests.get(RESULT_URL, params={
"key": API_KEY, "action": "get",
"id": task_id, "json": 1,
}, timeout=15).json()
if poll.get("request") == "CAPCHA_NOT_READY":
continue
if poll.get("status") == 1:
return poll["request"]
raise RuntimeError(f"Solve failed: {poll.get('request')}")
raise RuntimeError("Timeout")
def process_page(session, url):
"""Fetch page, detect CAPTCHA type, solve, and return form-ready data."""
response = session.get(url)
captcha_info = detect_captcha_type(response.text, url)
if not captcha_info:
print(f"No CAPTCHA detected on {url}")
return None
print(f"Detected {captcha_info.provider} on {url}")
print(f" Sitekey: {captcha_info.sitekey[:30]}...")
token = solve_captcha(captcha_info)
print(f" Solved: {token[:30]}...")
return {
"provider": captcha_info.provider,
"response_field": captcha_info.response_field,
"token": token,
}
# Usage: Handle multiple pages with different providers
session = requests.Session()
pages = [
"https://staging.example.com/qa-login", # Might have reCAPTCHA
"https://example.com/checkout", # Might have Turnstile
]
for url in pages:
result = process_page(session, url)
if result:
form_data = {result["response_field"]: result["token"]}
# Add other form fields...
# session.post(url, data=form_data)
JavaScript: Deteksi CAPTCHA Dinamis
const API_KEY = "YOUR_API_KEY";
const SUBMIT_URL = "https://ocr.captchaai.com/in.php";
const RESULT_URL = "https://ocr.captchaai.com/res.php";
function detectCaptchaType(html, pageurl) {
// Turnstile
const turnstileMatch = html.match(/cf-turnstile[^>]*data-sitekey=["']([^"']+)["']/);
if (turnstileMatch) {
return { provider: "turnstile", method: "turnstile", sitekey: turnstileMatch[1], pageurl, field: "cf-turnstile-response" };
}
// reCAPTCHA
const recaptchaMatch = html.match(/g-recaptcha[^>]*data-sitekey=["']([^"']+)["']/);
if (recaptchaMatch) {
return { provider: "recaptcha", method: "userrecaptcha", sitekey: recaptchaMatch[1], pageurl, field: "g-recaptcha-response" };
}
// Script-rendered reCAPTCHA
const scriptMatch = html.match(/sitekey["']\s*:\s*["']([^"']+)["']/);
if (scriptMatch) {
return { provider: "recaptcha", method: "userrecaptcha", sitekey: scriptMatch[1], pageurl, field: "g-recaptcha-response" };
}
return null;
}
async function solveCaptcha(info) {
const body = new URLSearchParams({ key: API_KEY, method: info.method, json: "1" });
if (info.method === "userrecaptcha") { body.set("googlekey", info.sitekey); body.set("pageurl", info.pageurl); }
else if (info.method === "turnstile") { body.set("sitekey", info.sitekey); body.set("pageurl", info.pageurl); }
const resp = await (await fetch(SUBMIT_URL, { method: "POST", body })).json();
if (resp.status !== 1) throw new Error(`Submit: ${resp.request}`);
const taskId = resp.request;
for (let i = 0; i < 60; i++) {
await new Promise((r) => setTimeout(r, 5000));
const url = `${RESULT_URL}?key=${API_KEY}&action=get&id=${taskId}&json=1`;
const poll = await (await fetch(url)).json();
if (poll.request === "CAPCHA_NOT_READY") continue;
if (poll.status === 1) return poll.request;
throw new Error(`Solve: ${poll.request}`);
}
throw new Error("Timeout");
}
async function processPage(url) {
const response = await fetch(url);
const html = await response.text();
const info = detectCaptchaType(html, url);
if (!info) { console.log(`No CAPTCHA on ${url}`); return null; }
console.log(`${info.provider} detected on ${url}`);
const token = await solveCaptcha(info);
return { provider: info.provider, field: info.field, token };
}
// Usage
const pages = ["https://staging.example.com/qa-login", "https://example.com/checkout"];
for (const url of pages) {
const result = await processPage(url);
if (result) {
console.log(`Solved ${result.provider}: ${result.token.substring(0, 30)}...`);
}
}
Referensi Deteksi Penyedia
| Penyedia | Penanda HTML | URL skrip | Bidang Respon |
|---|---|---|---|
| reCAPTCHA v2 | class="g-recaptcha" |
google.com/recaptcha/api.js |
g-recaptcha-response |
| Cloudflare Turnstile | class="cf-turnstile" |
challenges.cloudflare.com/turnstile |
cf-turnstile-response |
| hCaptcha | class="h-captcha" |
js.hcaptcha.com/1/api.js |
h-captcha-response |
Pemecahan Masalah
| Masalah | Penyebab | Solusi |
|---|---|---|
| Jenis CAPTCHA yang salah terdeteksi | Regex cocok dengan elemen yang salah | Periksa nama kelas khusus penyedia terlebih dahulu, bukan hanya data-sitekey |
| Token ditolak setelah solve yang benar | Menggunakan nama bidang respons yang salah | Cocokkan nama bidang dengan penyedia: g-recaptcha-response vs cf-turnstile-response |
| Jenis CAPTCHA berubah antar kunjungan | Pengujian A/B atau seleksi berbasis geografis | Selalu deteksi secara dinamis; jangan pernah melakukan hardcode pada penyedia |
| Kedua penyedia terdeteksi pada satu halaman | Satu mungkin disembunyikan/inactive | Periksa visibilitas elemen — selesaikan hanya CAPTCHA yang terlihat |
| Deteksi gagal untuk CAPTCHA yang dirender dengan skrip | Tidak ada penanda HTML di sumber | Periksa panggilan grecaptcha.render() atau turnstile.render() dalam skrip |
Pertanyaan Umum
Bisakah suatu halaman menggunakan reCAPTCHA dan Turnstile secara bersamaan?
Jarang dalam bentuk yang sama, namun terjadi di berbagai bagian situs. Jika keduanya muncul di satu halaman, biasanya hanya satu yang aktif — periksa widget mana yang terlihat dan memiliki data-sitekey yang tidak kosong.
Apakah CaptchaAI menggunakan API yang sama untuk kedua penyedia?
Titik akhir yang sama (in.php / res.php) tetapi nilai method berbeda. reCAPTCHA menggunakan method=userrecaptcha dengan googlekey, sedangkan Turnstile menggunakan method=turnstile dengan sitekey. Pola deteksi otomatis menangani pemetaan ini.
Bagaimana cara menangani failover penyedia?
Jika situs beralih dari reCAPTCHA ke Turnstile di tengah sesi (misalnya, setelah reCAPTCHA gagal dimuat), deteksi ulang jenis CAPTCHA pada setiap permintaan halaman daripada menyimpan cache penyedia dari kunjungan pertama.
Artikel Terkait
- Cara Mengatasi Callback Recaptcha V2 Menggunakan Api
- Cloudflare Challenge Vs Mendeteksi Turnstile
- Perbandingan Geetest Vs Cloudflare Turnstile
Langkah Selanjutnya
Menangani situs dengan beberapa penyedia CAPTCHA —dapatkan kunci API CaptchaAI Andadan menerapkan deteksi otomatis.
Panduan terkait:
- Menangani Beberapa CAPTCHA dalam Satu Halaman
- Rantai CAPTCHA: Bentuk Multi-Langkah Berurutan
- CAPTCHA dalam Modal Pop-Up: Deteksi dan Injeksi