Bir sayfada iki veya daha fazla CAPTCHA varsa çözüm üç adımdan ibarettir: hepsini tespit edin, aynı anda paralel gönderin ve her token'ı ait olduğu widget'a enjekte edin. Zorluk çözümde değil; her CAPTCHA'nın kendi sitekey'ine, hedef öğesine ve çoğu zaman kendi callback'ine sahip olmasındadır.
Bu senaryo Türkiye'deki ekiplerin sık karşılaştığı bir durumdur: bir e-ticaret staging ortamında ödeme adımını test ederken giriş formunda bir reCAPTCHA, altındaki bülten kaydında ikinci bir reCAPTCHA olabilir ve token'ları karıştırırsanız form yine engellenir. QA ortamınızdan gerçek kullanıcı verisi topluyorsanız KVKK kapsamında kalmayı da unutmayın; bu akışları yetkili test ve veri toplama senaryolarıyla sınırlayın.
En sık yapılan hata tek bir sitekey varsaymak veya her token'ı sayfadaki ilk metin alanına yazmaktır. Aşağıdaki adımlar bu iki tuzağı da ortadan kaldırır: her CAPTCHA'yı ayrı bir kayıt olarak listeler, paralel çözer ve token'ları index üzerinden geri eşleştirir.
Sayfadaki tüm CAPTCHA'ları tespit etme
İlk iş, sayfadaki her CAPTCHA'yı ve sitekey'ini bir listede toplamaktır; sonraki adımlar bu liste üzerinden çalışır. Selenium tarafında reCAPTCHA'lar recaptcha/api2/anchor içeren iframe'ler olarak yüklenir ve sitekey, iframe src'sindeki k= parametresinde durur. Puppeteer tarafında ise .g-recaptcha konteynerini doğrudan okuyup data-sitekey özniteliğini alırsınız. İki yaklaşım da aynı sonucu verir; hangi otomasyon aracını kullandığınıza göre seçin.
Python ve Selenium
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com/multi-captcha-page")
# Find all reCAPTCHA iframes
captcha_iframes = driver.find_elements(
By.CSS_SELECTOR, 'iframe[src*="recaptcha/api2/anchor"]'
)
# Extract sitekeys from each iframe's src
import re
captchas = []
for i, iframe in enumerate(captcha_iframes):
src = iframe.get_attribute("src")
match = re.search(r"k=([A-Za-z0-9_-]+)", src)
if match:
captchas.append({
"index": i,
"sitekey": match.group(1),
"iframe": iframe
})
print(f"Found {len(captchas)} CAPTCHAs on page")
for c in captchas:
print(f" [{c['index']}] sitekey: {c['sitekey']}")
Beklenen çıktı:
Found 2 CAPTCHAs on page
[0] sitekey: 6LcXyzABCDEF-login
[1] sitekey: 6LcAbcDEFGHI-signup
İkinci CAPTCHA'lar çoğu zaman geç yüklenir (lazy loading) veya yalnızca kullanıcı sayfayı kaydırınca belirir. Bu yüzden taramayı yapmadan önce tüm widget'ların DOM'a eklenmesini bekleyin; aksi halde listeniz eksik kalır ve yalnızca ilk CAPTCHA'yı çözersiniz.
JavaScript ve Puppeteer
Puppeteer tarafında .g-recaptcha widget'larını doğrudan DOM'dan okuyup her birinin sitekey'ini, DOM kimliğini ve varsa data-callback adını tek geçişte toplayabilirsiniz. Callback adını burada yakalamanız önemlidir: token'ı enjekte ettikten sonra bu fonksiyonu çağırmanız gerekecek.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
await page.goto('https://example.com/multi-captcha-page', {
waitUntil: 'networkidle2'
});
// Extract all reCAPTCHA widgets from the page
const captchas = await page.evaluate(() => {
const widgets = document.querySelectorAll('.g-recaptcha');
return Array.from(widgets).map((el, i) => ({
index: i,
sitekey: el.getAttribute('data-sitekey'),
elementId: el.id || `captcha-${i}`,
callbackName: el.getAttribute('data-callback') || null
}));
});
console.log(`Found ${captchas.length} CAPTCHAs`);
captchas.forEach(c => console.log(` [${c.index}] ${c.sitekey}`));
})();
Tüm CAPTCHA'ları paralel çözme
Bir CAPTCHA'yı çözmek 15–30 saniye sürer; ikisini sırayla gönderirseniz bu süreyi ikiye katlarsınız, paralel gönderdiğinizde toplam süre tek bir CAPTCHA kadar kalır. Bu yüzden hepsini tek seferde CaptchaAI'ye gönderin ve her biri çözülene kadar res.php üzerinden sorgulayın.
Paralellik doğrudan plan seçiminizle ilgilidir: her eşzamanlı CAPTCHA bir thread tüketir. İki-üç CAPTCHA'lı sayfalar için BASIC ($15/ay, 5 thread) yeterlidir; yoğun test eden ekipler ADVANCE ($90/ay, 50 thread) ile rahat eder. CaptchaAI thread bazlı faturalandırıldığı ve her plan thread başına sınırsız çözüm içerdiği için, hacim arttıkça maliyetiniz sabit kalır — TL kurundaki dalgalanmadan bağımsız, öngörülebilir bir aylık USD gideri planlayabilirsiniz.
Aşağıdaki örnek ThreadPoolExecutor ile önce tüm CAPTCHA'ları gönderir, ardından her görevi ayrı bir thread'de sorgular. poll_result fonksiyonundaki timeout değerini iş yükünüze göre ayarlayın: çok adımlı bir formda 120 saniye güvenli bir üst sınırdır.
Python
import requests
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
API_KEY = "YOUR_API_KEY"
PAGE_URL = "https://example.com/multi-captcha-page"
def submit_captcha(sitekey):
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": PAGE_URL,
"json": "1"
})
result = resp.json()
if result["status"] != 1:
raise Exception(f"Submit error: {result['request']}")
return result["request"]
def poll_result(task_id, timeout=120):
deadline = time.time() + timeout
while time.time() < deadline:
time.sleep(5)
resp = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY,
"action": "get",
"id": task_id,
"json": "1"
})
result = resp.json()
if result["status"] == 1:
return result["request"]
if result["request"] != "CAPCHA_NOT_READY":
raise Exception(f"Poll error: {result['request']}")
raise TimeoutError(f"Task {task_id} timed out")
def solve_all(captchas):
# Submit all in parallel
task_ids = {}
with ThreadPoolExecutor(max_workers=len(captchas)) as pool:
futures = {
pool.submit(submit_captcha, c["sitekey"]): c["index"]
for c in captchas
}
for future in as_completed(futures):
idx = futures[future]
task_ids[idx] = future.result()
print(f"[captcha-{idx}] Submitted → task {task_ids[idx]}")
# Poll all in parallel
tokens = {}
with ThreadPoolExecutor(max_workers=len(task_ids)) as pool:
futures = {
pool.submit(poll_result, tid): idx
for idx, tid in task_ids.items()
}
for future in as_completed(futures):
idx = futures[future]
tokens[idx] = future.result()
print(f"[captcha-{idx}] Solved")
return tokens
# Example usage
captchas = [
{"index": 0, "sitekey": "6LcXyzABCDEF-login"},
{"index": 1, "sitekey": "6LcAbcDEFGHI-signup"}
]
tokens = solve_all(captchas)
Beklenen çıktı:
[captcha-0] Submitted → task 71823456
[captcha-1] Submitted → task 71823457
[captcha-1] Solved
[captcha-0] Solved
Sonuçlar tamamlanma sırasına göre döndüğü için gönderim sırasından farklı gelmesi normaldir. Token'ları her zaman index üzerinden eşleştirin, geldikleri sıraya güvenmeyin.
Token'ı doğru widget'a enjekte etme
Her reCAPTCHA widget'ının kendi g-recaptcha-response metin alanı vardır. Birden fazla widget varken her metin alanı kendi konteynerine gömülüdür; token'ı sayfadaki ilk alana değil, ait olduğu widget'ın içindeki alana yazın. Enjeksiyondan sonra, widget bir callback tanımlıyorsa o fonksiyonu token'la çağırmanız gerekir — özellikle görünmez reCAPTCHA'da form gönderimi çoğu zaman bu callback'e bağlıdır. Callback adını tespit adımında data-callback özniteliğinden aldığınız için burada elinizin altındadır.
Python (Selenium)
def inject_tokens(driver, captchas, tokens):
for c in captchas:
idx = c["index"]
token = tokens[idx]
# Find the textarea within the widget's container
container = driver.find_elements(By.CSS_SELECTOR, ".g-recaptcha")[idx]
textarea = container.find_element(
By.CSS_SELECTOR, 'textarea[name="g-recaptcha-response"]'
)
driver.execute_script(
"arguments[0].value = arguments[1];", textarea, token
)
# Trigger the callback if defined
callback = c.get("callback")
if callback:
driver.execute_script(f"{callback}('{token}');")
print(f"[captcha-{idx}] Token injected")
inject_tokens(driver, captchas, tokens)
JavaScript (Puppeteer)
async function injectTokens(page, captchas, tokens) {
for (const captcha of captchas) {
const token = tokens[captcha.index];
await page.evaluate((idx, tkn, callbackName) => {
const widgets = document.querySelectorAll('.g-recaptcha');
const textarea = widgets[idx].querySelector(
'textarea[name="g-recaptcha-response"]'
);
textarea.value = tkn;
if (callbackName && typeof window[callbackName] === 'function') {
window[callbackName](tkn);
}
}, captcha.index, token, captcha.callbackName);
console.log(`[captcha-${captcha.index}] Token injected`);
}
}
await injectTokens(page, captchas, tokens);
Token'ı yazdıktan sonra form hâlâ engelliyse sebep neredeyse her zaman tetiklenmemiş bir callback'tir: widget'ın data-callback fonksiyonunu token'la çağırın.
Karışık CAPTCHA türleri: reCAPTCHA ve Turnstile bir arada
Aynı sayfada farklı CAPTCHA türleri de olabilir (ör. reCAPTCHA ve Cloudflare Turnstile). Her türü ayrı bir seçiciyle tespit edip kendi method değerini atayın.
def detect_all_captchas(driver):
detected = []
# reCAPTCHA
recaptchas = driver.find_elements(By.CSS_SELECTOR, ".g-recaptcha")
for i, el in enumerate(recaptchas):
detected.append({
"type": "userrecaptcha",
"sitekey": el.get_attribute("data-sitekey"),
"label": f"recaptcha-{i}"
})
# Turnstile
turnstiles = driver.find_elements(By.CSS_SELECTOR, ".cf-turnstile")
for i, el in enumerate(turnstiles):
detected.append({
"type": "turnstile",
"sitekey": el.get_attribute("data-sitekey"),
"label": f"turnstile-{i}"
})
return detected
Her CAPTCHA'yı eşleşen method parametresiyle gönderin: reCAPTCHA için userrecaptcha, Turnstile için turnstile. Turnstile token'ı cf-turnstile-response alanına yazılır, g-recaptcha-response alanına değil — türleri karıştırmayın.
Sorun giderme
| Sorun | Sebep | Düzeltme |
|---|---|---|
| Token enjekte edildi ama form hâlâ engelli | Callback tetiklenmedi | data-callback'i kontrol edin ve token'la çağırın |
| Yalnızca ilk CAPTCHA algılandı | İkinci CAPTCHA geç yükleniyor | Taramadan önce tüm iframe'lerin/widget'ların görünmesini bekleyin |
| Yanlış widget'ta yanlış token | Dizin uyuşmazlığı | Token'ları konumsal dizinle değil sitekey ile eşleştirin |
ERROR_WRONG_GOOGLEKEY |
sitekey hatalı çıkarıldı |
sitekey'i iframe src ya da data-sitekey özniteliğinden doğrulayın |
Sık sorulan sorular
Sayfada hem reCAPTCHA hem Turnstile varsa ikisini nasıl gönderirim?
Her türü kendi method değeriyle ayrı gönderin: reCAPTCHA için userrecaptcha, Turnstile için turnstile. Dönen token'ları ait oldukları alana yazın — reCAPTCHA g-recaptcha-response, Turnstile cf-turnstile-response alanını kullanır.
Paralel çözüm kaç thread tüketir?
Her eşzamanlı CAPTCHA bir thread kullanır. Aynı anda iki CAPTCHA gönderirseniz iki thread meşgul olur; çözüm biter bitmez thread serbest kalır. İki-üç CAPTCHA'lı sayfalar için BASIC ($15/ay, 5 thread) yeterlidir.
İkinci CAPTCHA yalnızca ilk form gönderildikten sonra görünürse ne yaparım?
Her form gönderiminin ardından sayfayı yeniden tarayın. Yeni widget'ın DOM'a eklenmesini beklemek için WebDriverWait (Selenium) veya page.waitForSelector (Puppeteer) kullanın, sonra tespit adımını tekrar çalıştırın.
Widget'lardan biri görünmez reCAPTCHA'ysa ne değişir?
Tespit ve çözüm akışı aynıdır, ancak görünmez reCAPTCHA'da tıklanacak bir onay kutusu yoktur; form gönderimi tamamen callback'e bağlıdır. Token'ı g-recaptcha-response alanına yazdıktan sonra widget'ın data-callback fonksiyonunu mutlaka token'la çağırın, aksi halde form ilerlemez.
CaptchaAI hCaptcha'yı da destekliyor mu?
Hayır. CaptchaAI şu an hCaptcha ve FunCaptcha (Arkose Labs) çözmüyor. Desteklenen türler reCAPTCHA v2/v3, Cloudflare Turnstile ve Challenge, GeeTest v3, görüntü/OCR, grid ve BLS'tir; CaptchaFox (beta), Friendly Captcha (beta) ve Lemin (beta) beta aşamasındadır.
Sayfa başına istediğiniz kadar CAPTCHA'yı çözün
API anahtarınızı şu adresten alın: captchaai.com.