Tidak semua CAPTCHA muncul saat halaman pertama kali dimuat. Banyak situs me-render CAPTCHA secara lazy — setelah klik tombol, fokus form, scroll, atau timer. Jika otomatisasi Anda langsung mengambil source halaman, CAPTCHA belum ada di sana. Panduan ini mencakup cara mendeteksi dan menunggu CAPTCHA yang dimuat secara dinamis.
Trigger Lazy-Load yang Umum
| Trigger | Contoh | Cara mengaktifkan |
|---|---|---|
| Klik tombol | "Submit" menambahkan reCAPTCHA ke form | Klik tombol terlebih dahulu |
| Form focus | CAPTCHA muncul saat input difokuskan | Fokuskan field email/password |
| Posisi scroll | CAPTCHA di-load saat section terlihat | Scroll ke form |
| Timer | CAPTCHA di-load setelah 3 detik | Tunggu delay-nya |
| Kondisi JavaScript | CAPTCHA di-load setelah response AJAX | Trigger request prasyarat |
Metode 1: MutationObserver
Perhatikan DOM untuk elemen CAPTCHA yang ditambahkan:
Puppeteer
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
await page.goto('https://staging.example.com/qa-login');
// Set up MutationObserver before triggering the CAPTCHA
const captchaInfo = await page.evaluate(() => {
return new Promise((resolve) => {
// Check if already present
const existing = document.querySelector('.g-recaptcha, .cf-turnstile, .h-captcha');
if (existing) {
resolve({
type: existing.className,
sitekey: existing.getAttribute('data-sitekey'),
});
return;
}
// Watch for new elements
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (node.nodeType !== 1) continue;
const captcha = node.matches?.('.g-recaptcha, .cf-turnstile, .h-captcha')
? node
: node.querySelector?.('.g-recaptcha, .cf-turnstile, .h-captcha');
if (captcha) {
observer.disconnect();
resolve({
type: captcha.className,
sitekey: captcha.getAttribute('data-sitekey'),
});
return;
}
}
}
});
observer.observe(document.body, { childList: true, subtree: true });
// Timeout after 30 seconds
setTimeout(() => {
observer.disconnect();
resolve(null);
}, 30000);
});
});
console.log('Detected CAPTCHA:', captchaInfo);
Trigger load
// Klik tombol submit untuk trigger CAPTCHA
await page.click('#submit-btn');
// Atau fokuskan input
await page.focus('#email');
// Atau scroll ke form
await page.evaluate(() => {
document.querySelector('#signup-form').scrollIntoView();
});
Metode 2: Tunggu Injeksi Script
CAPTCHA membutuhkan library JavaScript mereka. Pantau saat library tersebut di-load:
// Tunggu script reCAPTCHA selesai di-load
await page.waitForFunction(() => {
return typeof window.grecaptcha !== 'undefined'
&& typeof window.grecaptcha.render === 'function';
}, { timeout: 30000 });
// Sekarang ekstrak parameter
const sitekey = await page.evaluate(() => {
const el = document.querySelector('.g-recaptcha');
return el?.getAttribute('data-sitekey');
});
Untuk Turnstile
await page.waitForFunction(() => {
return typeof window.turnstile !== 'undefined';
}, { timeout: 30000 });
const sitekey = await page.evaluate(() => {
const el = document.querySelector('.cf-turnstile');
return el?.getAttribute('data-sitekey');
});
Metode 3: Intercept Render Call
Hook ke library CAPTCHA sebelum di-render:
// Inject before page scripts run
await page.evaluateOnNewDocument(() => {
window.__captchaDetected = null;
// Hook grecaptcha.render
let _grecaptcha;
Object.defineProperty(window, 'grecaptcha', {
set(val) {
_grecaptcha = val;
const origRender = val.render;
val.render = function(container, params) {
window.__captchaDetected = {
type: 'recaptcha',
sitekey: params.sitekey,
callback: params.callback?.name,
container: typeof container === 'string' ? container : container.id,
};
return origRender.apply(this, arguments);
};
},
get() { return _grecaptcha; },
});
});
await page.goto('https://example.com/signup');
// Trigger the CAPTCHA (click, scroll, etc.)
await page.click('#show-form');
// Wait for detection
await page.waitForFunction(() => window.__captchaDetected !== null, {
timeout: 30000,
});
const detected = await page.evaluate(() => window.__captchaDetected);
console.log('Detected:', detected);
// { type: 'recaptcha', sitekey: '6Le-wvkS...', callback: 'onCaptcha', container: 'recaptcha-box' }
Python (Selenium): Menunggu CAPTCHA Lazy-Loaded
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")
# Trigger the CAPTCHA loading
submit = driver.find_element(By.ID, "submit-btn")
submit.click()
# Wait for CAPTCHA to appear
try:
captcha_el = WebDriverWait(driver, 30).until(
EC.presence_of_element_located((
By.CSS_SELECTOR,
".g-recaptcha, .cf-turnstile, .h-captcha"
))
)
sitekey = captcha_el.get_attribute("data-sitekey")
captcha_class = captcha_el.get_attribute("class")
if "g-recaptcha" in captcha_class:
captcha_type = "recaptcha"
elif "cf-turnstile" in captcha_class:
captcha_type = "turnstile"
else:
captcha_type = "hcaptcha"
print(f"Type: {captcha_type}, Sitekey: {sitekey}")
except Exception:
print("No CAPTCHA appeared within 30 seconds")
Tunggu iframe (reCAPTCHA)
# reCAPTCHA me-load iframe bahkan ketika div ada tapi script masih loading
WebDriverWait(driver, 30).until(
EC.presence_of_element_located((
By.CSS_SELECTOR,
"iframe[src*='recaptcha'], iframe[src*='challenges.cloudflare.com']"
))
)
print("CAPTCHA iframe loaded")
Flow Lengkap: Deteksi + Solve
import requests
import time
def detect_and_solve(driver, api_key, trigger_action=None):
"""Detect a lazy-loaded CAPTCHA, solve it, and inject the token."""
# 1. Trigger the CAPTCHA
if trigger_action:
trigger_action(driver)
# 2. Wait for it to appear
captcha_el = WebDriverWait(driver, 30).until(
EC.presence_of_element_located((
By.CSS_SELECTOR,
".g-recaptcha, .cf-turnstile, .h-captcha"
))
)
sitekey = captcha_el.get_attribute("data-sitekey")
page_url = driver.current_url
captcha_class = captcha_el.get_attribute("class")
# 3. Determine type and method
if "g-recaptcha" in captcha_class:
method, key_param, token_field = "userrecaptcha", "googlekey", "g-recaptcha-response"
elif "cf-turnstile" in captcha_class:
method, key_param, token_field = "turnstile", "sitekey", "cf-turnstile-response"
else:
method, key_param, token_field = "hcaptcha", "sitekey", "h-captcha-response"
# 4. Solve with CaptchaAI
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": api_key, "method": method,
key_param: sitekey, "pageurl": page_url, "json": "1",
}).json()
task_id = resp["request"]
for _ in range(24):
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:
token = result["request"]
break
# 5. Inject
driver.execute_script(f"""
const el = document.querySelector('textarea[name="{token_field}"], input[name="{token_field}"]');
if (el) el.value = arguments[0];
""", token)
return token
Pemecahan Masalah
| Masalah | Penyebab | Perbaikan |
|---|---|---|
| CAPTCHA tidak pernah muncul | Trigger action salah | Periksa halaman untuk menemukan apa yang memicu CAPTCHA |
| Sitekey adalah null | Elemen ada tapi script belum berjalan | Tunggu iframe CAPTCHA, bukan hanya div-nya |
| Observer tidak mendeteksinya | CAPTCHA sudah ada sebelumnya | Periksa elemen yang sudah ada sebelum setup observer |
| Timeout | CAPTCHA hanya di-load untuk pengguna nyata | Gunakan browser penuh dengan sinyal browser yang realistis |
Pertanyaan Umum
Bagaimana saya tahu apakah CAPTCHA di-load secara lazy?
Lihat source halaman (Ctrl+U). Jika div atau script CAPTCHA tidak ada tapi muncul saat Anda berinteraksi dengan halaman, berarti CAPTCHA tersebut lazy-loaded.
Apakah ini berfungsi di browser headless?
Ya, dengan catatan. Beberapa situs hanya me-load CAPTCHA untuk browser non-headless. Gunakan headless: 'new' di Puppeteer atau puppeteer-extra (mode standar).
Solve CAPTCHA Apa Pun dengan CaptchaAI
Dapatkan API key Anda di captchaai.com.
Panduan terkait
- Deteksi CAPTCHA Konsol Browser
- Menangani Beberapa CAPTCHA dalam Satu Halaman
- Mengekstrak Parameter reCAPTCHA