Entegrasyon yazıldı, API OK| döndü, token elinizde — ama form doğrulamadan geçmiyorsa büyük ihtimalle görünmez varyantla uğraşıyorsunuz. İki varyant aynı sitekey biçimini ve aynı g-recaptcha-response alanını kullanır; ayrım üç noktada toplanır:
- Tespit: görünür widget yoksa HTML'de
data-size="invisible"arayın. - İstek: tek fark
invisible=1parametresidir. - Enjeksiyon: onay kutusunda gizli alan yeter; görünmez sürümde callback'i elle çağırırsınız.
Sıra da bu: önce tespit, sonra çözüm isteği, en sonda enjeksiyon.
Sayfa kaynağında iki varyant
v2 Onay Kutusu
<!-- Standard checkbox widget -->
<div class="g-recaptcha"
data-sitekey="6Le-wvkSAAAAAPBMRTvw..."
data-callback="onSubmit">
</div>
<!-- Widget renders as: -->
<!-- [✓] I'm not a robot reCAPTCHA logo -->
Kullanıcı kutuyu görür ve tıklar; Google şüphelenirse ızgara doğrulaması satır içinde açılır.
v2 Görünmez
<!-- Pattern 1: Invisible widget on a button -->
<button class="g-recaptcha"
data-sitekey="6Le-wvkSAAAAAPBMRTvw..."
data-callback="onSubmit"
data-size="invisible">
Submit
</button>
<!-- Pattern 2: Invisible div (programmatic trigger) -->
<div class="g-recaptcha"
data-sitekey="6Le-wvkSAAAAAPBMRTvw..."
data-size="invisible"
data-callback="onSubmit">
</div>
<!-- Pattern 3: Programmatic render -->
<script>
grecaptcha.render('submit-btn', {
sitekey: '6Le-wvkSAAAAAPBMRTvw...',
callback: onSubmit,
size: 'invisible'
});
</script>
Görünür widget yoktur; reCAPTCHA, öğe tetiklendiğinde veya grecaptcha.execute() çağrıldığında devreye girer. Üç kalıbın tespit zorluğu aynı değildir:
- Düğmedeki widget:
data-size="invisible"HTML'de görünür. - Görünmez div: aynı öznitelik var, tetikleyici JavaScript'te.
- Programatik render: HTML'de iz kalmaz, script içini taramanız gerekir.
Varyantı çalışma anında tespit edin
sitekey değerine bakarak karar veremezsiniz; biçim ikisinde de aynıdır. Ayrım HTML'dedir. Python ile:
import requests
from bs4 import BeautifulSoup
import re
def detect_recaptcha_variant(url):
resp = requests.get(url)
soup = BeautifulSoup(resp.text, "html.parser")
# Check for invisible indicators
invisible_widget = soup.find(attrs={"data-size": "invisible", "class": "g-recaptcha"})
if invisible_widget:
return {
"variant": "invisible",
"sitekey": invisible_widget.get("data-sitekey"),
"callback": invisible_widget.get("data-callback")
}
# Check for programmatic invisible in scripts
for script in soup.find_all("script"):
if script.string and "invisible" in str(script.string):
key_match = re.search(r"sitekey['\"]?\s*[:=]\s*['\"]([^'\"]+)", script.string)
if key_match:
return {
"variant": "invisible-programmatic",
"sitekey": key_match.group(1),
"callback": "check grecaptcha.render() call"
}
# Check for standard checkbox
checkbox_widget = soup.find(class_="g-recaptcha")
if checkbox_widget:
return {
"variant": "checkbox",
"sitekey": checkbox_widget.get("data-sitekey"),
"callback": checkbox_widget.get("data-callback")
}
return None
result = detect_recaptcha_variant("https://staging.example.com/qa-login")
print(result)
Node.js tarafında aynı mantık:
const axios = require("axios");
const cheerio = require("cheerio");
async function detectRecaptchaVariant(url) {
const { data } = await axios.get(url);
const $ = cheerio.load(data);
// Check for invisible
const invisible = $(".g-recaptcha[data-size='invisible']");
if (invisible.length) {
return {
variant: "invisible",
sitekey: invisible.attr("data-sitekey"),
callback: invisible.attr("data-callback"),
};
}
// Check scripts for programmatic invisible
const scripts = $("script")
.map((_, el) => $(el).html())
.get()
.join("\n");
if (scripts.includes("invisible")) {
const keyMatch = scripts.match(/sitekey['"]?\s*[:=]\s*['"]([^'"]+)/);
if (keyMatch) {
return {
variant: "invisible-programmatic",
sitekey: keyMatch[1],
callback: "check render call",
};
}
}
// Check for standard checkbox
const checkbox = $(".g-recaptcha");
if (checkbox.length) {
return {
variant: "checkbox",
sitekey: checkbox.attr("data-sitekey"),
callback: checkbox.attr("data-callback"),
};
}
return null;
}
Elle bakarken konsoldaki tek satır da yeter:
const el = document.querySelector('[data-size="invisible"]');
console.log(el ? "Invisible reCAPTCHA" : "Checkbox reCAPTCHA");
| Algılama sinyali | v2 Onay Kutusu | v2 Görünmez |
|---|---|---|
data-size="invisible" |
Yok | Var |
| Görünür onay kutusu | Evet | Hayır |
grecaptcha.execute() |
Hayır, kullanıcı tıklar | Evet, programatik |
Not: Varyantı sabit varsaymayın — aynı site masaüstünde onay kutusunu, mobilde görünmez sürümü sunabilir.
Özellik karşılaştırması
| Özellik | v2 Onay Kutusu | v2 Görünmez |
|---|---|---|
| Görsel doğrulama | Satır içi ızgara | Sağ alt köşede açılır pencere |
| Kullanıcı sürtünmesi | Orta | Düşük |
| Token alanı | g-recaptcha-response |
g-recaptcha-response |
| Callback | İsteğe bağlı | Neredeyse her zaman zorunlu |
| CaptchaAI yöntemi | method=userrecaptcha |
method=userrecaptcha + invisible=1 |
| Token ömrü | 120 saniye | 120 saniye |
| Çözüm süresi | 10–30 saniye | 10–25 saniye |
Tablodaki gerçek ayrım iki satırda: invisible=1 ve callback zorunluluğu.
Çözüm isteği: tek parametrelik fark
Akış ikisinde de aynı: görevi in.php'ye gönderin, res.php'yi sorgulayın, token'ı alın.
v2 Onay Kutusu
import requests
import time
resp = requests.get("https://ocr.captchaai.com/in.php", params={
"key": "YOUR_API_KEY",
"method": "userrecaptcha",
"googlekey": "6Le-wvkSAAAA...",
"pageurl": "https://example.com/form"
})
task_id = resp.text.split("|")[1]
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": "YOUR_API_KEY", "action": "get", "id": task_id
})
if result.text.startswith("OK|"):
token = result.text.split("|")[1]
break
v2 Görünmez
import requests
import time
resp = requests.get("https://ocr.captchaai.com/in.php", params={
"key": "YOUR_API_KEY",
"method": "userrecaptcha",
"googlekey": "6Le-wvkSAAAA...",
"pageurl": "https://example.com/form",
"invisible": 1 # Only parameter difference
})
task_id = resp.text.split("|")[1]
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": "YOUR_API_KEY", "action": "get", "id": task_id
})
if result.text.startswith("OK|"):
token = result.text.split("|")[1]
break
Tek fark invisible: 1 satırı. Sorgulama döngüsü, zaman aşımı ve hata kodları iki varyantta da aynı.
Token enjeksiyonu: asıl ayrışma noktası
İki akış buraya kadar aynıydı; fark burada çıkıyor.
Onay kutusu: gizli alan çoğu zaman yeter
# Selenium — inject into hidden field
driver.execute_script(
f'document.getElementById("g-recaptcha-response").value = "{token}";'
)
# If the page uses a callback, also call it
callback = driver.find_element("css selector", ".g-recaptcha").get_attribute("data-callback")
if callback:
driver.execute_script(f'{callback}("{token}");')
Görünmez: callback'i çağırmadan olmaz
# Selenium — inject AND call the callback
driver.execute_script(
f'document.getElementById("g-recaptcha-response").value = "{token}";'
)
# CRITICAL: Invisible reCAPTCHA almost always requires calling the callback
callback_name = driver.find_element(
"css selector", ".g-recaptcha[data-size='invisible']"
).get_attribute("data-callback")
driver.execute_script(f'{callback_name}("{token}");')
Puppeteer tarafında aynı yaklaşım:
// Puppeteer — invisible callback injection
await page.evaluate((tok) => {
// Set the hidden field
document.getElementById("g-recaptcha-response").value = tok;
// Find and call the callback function
const widget = document.querySelector("[data-size='invisible']");
const cbName = widget?.getAttribute("data-callback");
if (cbName && typeof window[cbName] === "function") {
window[cbName](tok);
}
}, token);
Neden? Onay kutusunda kullanıcının tıklaması callback'i (geri çağırma) zaten tetikler. Görünmez sürümde o tıklama hiç olmaz; callback'i siz çağırmazsanız form boş doğrulama durumuyla gider.
İki varyantı tek çözücüyle karşılayın
İki ayrı kod yolu bakım maliyetini ikiye katlar; tespiti ve çözümü tek sınıfta toplayın:
import requests
import time
from bs4 import BeautifulSoup
class RecaptchaV2UniversalSolver:
def __init__(self, api_key):
self.api_key = api_key
def detect_and_solve(self, page_url, page_html=None):
if not page_html:
page_html = requests.get(page_url).text
soup = BeautifulSoup(page_html, "html.parser")
# Detect variant
invisible = soup.find(attrs={"data-size": "invisible", "class": "g-recaptcha"})
widget = invisible or soup.find(class_="g-recaptcha")
if not widget:
raise Exception("No reCAPTCHA widget found")
sitekey = widget.get("data-sitekey")
is_invisible = invisible is not None
callback = widget.get("data-callback")
params = {
"key": self.api_key,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url
}
if is_invisible:
params["invisible"] = 1
resp = requests.get("https://ocr.captchaai.com/in.php", params=params)
if not resp.text.startswith("OK|"):
raise Exception(f"Submit failed: {resp.text}")
task_id = resp.text.split("|")[1]
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": self.api_key, "action": "get", "id": task_id
})
if result.text.startswith("OK|"):
return {
"token": result.text.split("|")[1],
"variant": "invisible" if is_invisible else "checkbox",
"callback": callback,
"sitekey": sitekey
}
if result.text != "CAPCHA_NOT_READY":
raise Exception(f"Solve error: {result.text}")
raise Exception("Timed out")
# Usage
solver = RecaptchaV2UniversalSolver("YOUR_API_KEY")
result = solver.detect_and_solve("https://staging.example.com/qa-login")
print(f"Variant: {result['variant']}, Callback: {result['callback']}")
Aynı iskelet başka türlere de genişler; değişen tek şey method değeridir:
- Cloudflare Turnstile — aynı gönder/sorgula döngüsü.
- GeeTest v3 — aynı
in.php/res.phpmantığı. - reCAPTCHA v2 Enterprise — ek alanlar dışında aynı.
Türkiye'den bir senaryo: ödeme akışının QA testi
Buradaki ekiplerin ağırlığı e-ticaret ve fintech entegrasyonlarında; regresyon paketlerinde en sık takılan yer ödeme adımı. Tipik tablo şu: staging'deki giriş formu onay kutusunu, mobil ödeme akışı görünmez sürümü kullanıyor — aynı sitekey, ama mobil senaryolar sessizce düşüyor. Sebep genelde çağrılmayan callback'tir; yukarıdaki çözücü varyantı çalışma anında ayırır.
Maliyet tarafı thread sayısına bağlı, çözüm adedine değil:
- Gecelik pakette en fazla beş eşzamanlı doğrulama varsa BASIC ($15/ay, 5 thread) yeter.
- Paralel senaryolar artınca STANDARD ($30/ay, 15 thread) rahat eder.
- Her push'ta koşan CI için ADVANCE ($90/ay, 50 thread).
Fiyatlar USD üzerinden sabittir. Test verilerinizde kişisel veri varsa KVKK yükümlülüklerini de hesaba katın.
Sorun giderme
| Belirti | Onay kutusu | Görünmez |
|---|---|---|
| Token reddedildi | g-recaptcha-response alanını doldurun |
data-callback fonksiyonunu bulun ve çağırın |
| Widget bulunamadı | .g-recaptcha sınıfını arayın |
data-size="invisible" ve grecaptcha.render() çağrılarına bakın |
| Form gidiyor, doğrulama geçmiyor | Sayfa callback bekliyor olabilir | Callback zorunlu; adını bulup çalıştırın |
| Enjeksiyondan sonra sayfa yenileniyor | JavaScript doğrulaması düşmüştür | Callback'i otomatik gönderimden önce çağırın |
| Token'ın süresi doldu | 120 saniye içinde kullanın | Aynı kural; sorgulama aralığını kısaltın |
Tablo yetmezse şu üçüne bakın:
pageurlformun URL'siyle birebir aynı mı?- Token üretileli kaç saniye oldu?
- Callback, otomatik gönderimden önce mi çalışıyor?
Sık sorulan sorular
invisible=1 göndermezsem ne olur?
Genellikle yine token alırsınız, ama hedef site reddedebilir. Parametre çözümün doğru varyant bağlamında üretilmesini sağlar; görünmez widget'ta atlanması yaygın bir doğrulama hatası nedenidir.
Callback fonksiyonunun adını bulamıyorum, ne yapmalıyım?
Önce widget'ın data-callback özniteliğine, yoksa grecaptcha.render() çağrısının callback alanına bakın. Callback anonimse token'ı yazdıktan sonra gönderim düğmesini tetikleyin.
Görünmez reCAPTCHA yine de görsel doğrulama çıkarır mı?
Evet. Google şüpheli davranış gördüğünde sağ alt köşede doğrulama penceresi açılır; "görünmez", doğrulamanın hiç çıkmaması değil varsayılanda gösterilmemesi demektir.
CaptchaAI hangi reCAPTCHA varyantlarını destekliyor?
reCAPTCHA v2'nin onay kutusu, görünmez, callback ve Enterprise varyantları ile v3 ve v3 Enterprise desteklenir. Listede ayrıca Cloudflare Turnstile, Cloudflare doğrulama akışı, GeeTest v3, görsel/OCR ve ızgara CAPTCHA'ları ile BLS var. hCaptcha ve FunCaptcha (Arkose Labs) desteklenmez; GeeTest v4 için yalnızca "çok yakında" geçerlidir. CaptchaFox, Friendly Captcha ve Lemin beta aşamasındadır.
İlgili kılavuzlar
- reCAPTCHA v2'yi API ile çözme rehberi — uçtan uca entegrasyon
- Görünmez reCAPTCHA'yı API ile çözme — varyanta özel adımlar
- Görünmez reCAPTCHA'da sık görülen hatalar — enjeksiyon ve callback sorunları
- Görünmez reCAPTCHA nasıl çalışır? — mekanizmanın anlatımı
- reCAPTCHA v2 ile v3 karşılaştırması — hangi sürüm hangi işe uygun