Puppeteer ile veri toplarken sayfada bir CAPTCHA belirdiğinde akış durur. Çözüm, doğrulamayı tarayıcının içinde çözmeye çalışmak değil: sitekey'i DOM'dan çıkarıp CaptchaAI API'sine gönderir, dönen token'ı forma enjekte edip gönderirsiniz. Bu rehber, reCAPTCHA v2 ve Cloudflare Turnstile için bu akışı Node.js'te sıfırdan kurar — çözücü modülü, Puppeteer yapılandırması, token enjeksiyonu ve callback tetikleme dahil.
Neye ihtiyacınız var
| Gereksinim | Ayrıntılar |
|---|---|
| Node.js 16+ | npm ile |
| Puppeteer | npm install puppeteer |
| axios | npm install axios |
| CaptchaAI API anahtarı | captchaai.com hesabınızdan |
Token akışı nasıl çalışır?
CaptchaAI CAPTCHA'yı sunucu tarafında çözer; Puppeteer yalnızca parametreleri taşır. Akış dört adımdan oluşur:
- Puppeteer, CAPTCHA'nın bulunduğu sayfaya gider.
- Betiğiniz CAPTCHA sitekey'ini DOM'dan çıkarır.
- CaptchaAI doğrulamayı sunucu tarafında çözer.
- Betiğiniz token'ı enjekte eder ve formu gönderir.
Çözümün sunucu tarafında gerçekleşmesi, testi headless çalıştırabilmeniz anlamına gelir — arayüzü açık tutmanız gerekmez. Bu ayrım önemli: tarayıcının içinde CAPTCHA çözmeye çalışmak yerine, Puppeteer'ın tek görevi sitekey'i okumak ve dönen token'ı doğru alana yazmaktır. Böylece otomasyon kodunuz sade kalır ve her CAPTCHA türü için ayrı bir çözüm mantığı yazmanız gerekmez.
Adım 1: CaptchaAI çözücü modülünü oluşturun
Önce API çağrılarını tek bir dosyada toplayın. Aşağıdaki modül görevi in.php uç noktasına gönderir, ardından token hazır olana kadar res.php'yi 5 saniyede bir sorgular. reCAPTCHA v2 için userrecaptcha, Turnstile için turnstile metodu kullanılır.
// solver.js
const axios = require("axios");
const API_KEY = "YOUR_API_KEY";
const POLL_INTERVAL = 5000;
const MAX_ATTEMPTS = 60;
async function solveRecaptchaV2(siteKey, pageUrl) {
// Submit task
const submitResp = await axios.get("https://ocr.captchaai.com/in.php", {
params: {
key: API_KEY,
method: "userrecaptcha",
googlekey: siteKey,
pageurl: pageUrl,
},
});
if (!submitResp.data.startsWith("OK|")) {
throw new Error(`Submit failed: ${submitResp.data}`);
}
const taskId = submitResp.data.split("|")[1];
console.log(`Task submitted: ${taskId}`);
// Poll for result
for (let i = 0; i < MAX_ATTEMPTS; i++) {
await new Promise((r) => setTimeout(r, POLL_INTERVAL));
const result = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: API_KEY, action: "get", id: taskId },
});
if (result.data === "CAPCHA_NOT_READY") continue;
if (result.data.startsWith("OK|")) {
return result.data.split("|")[1];
}
throw new Error(`Solve failed: ${result.data}`);
}
throw new Error("Solve timed out");
}
async function solveTurnstile(siteKey, pageUrl) {
const submitResp = await axios.get("https://ocr.captchaai.com/in.php", {
params: {
key: API_KEY,
method: "turnstile",
sitekey: siteKey,
pageurl: pageUrl,
},
});
if (!submitResp.data.startsWith("OK|")) {
throw new Error(`Submit failed: ${submitResp.data}`);
}
const taskId = submitResp.data.split("|")[1];
for (let i = 0; i < MAX_ATTEMPTS; i++) {
await new Promise((r) => setTimeout(r, POLL_INTERVAL));
const result = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: API_KEY, action: "get", id: taskId },
});
if (result.data === "CAPCHA_NOT_READY") continue;
if (result.data.startsWith("OK|")) return result.data.split("|")[1];
throw new Error(`Solve failed: ${result.data}`);
}
throw new Error("Solve timed out");
}
module.exports = { solveRecaptchaV2, solveTurnstile };
Adım 2: Puppeteer tarayıcısını başlatın
Standart bir yapılandırmayla tarayıcıyı açın. Burada özel bir şey yok: --no-sandbox ile başlatıyor, makul bir user agent atıyor ve sayfayı hazırlıyoruz.
const puppeteer = require("puppeteer");
async function createBrowser() {
const browser = await puppeteer.launch({
headless: "new",
args: [
"--no-sandbox",
"--disable-setuid-sandbox",
"--no-sandbox",
],
});
const page = await browser.newPage();
await page.setUserAgent(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
);
// Hide automation indicators
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, "webdriver", { get: () => false });
});
return { browser, page };
}
Adım 3: Sayfadaki reCAPTCHA'yı çözün
Şimdi parçaları birleştirin: sayfaya gidin, .g-recaptcha öğesinden sitekey'i okuyun, çözücüyü çağırın ve dönen token'ı g-recaptcha-response alanına yazın. Ardından formu gönderin.
const { solveRecaptchaV2 } = require("./solver");
async function scrapeWithCaptcha(url) {
const { browser, page } = await createBrowser();
try {
await page.goto(url, { waitUntil: "networkidle2" });
// Extract site key
const siteKey = await page.$eval(
".g-recaptcha",
(el) => el.getAttribute("data-sitekey")
);
console.log("Site key:", siteKey);
// Solve with CaptchaAI
const token = await solveRecaptchaV2(siteKey, url);
console.log("Token received:", token.substring(0, 50));
// Inject token
await page.evaluate((token) => {
document.getElementById("g-recaptcha-response").innerHTML = token;
document.getElementById("g-recaptcha-response").style.display = "";
}, token);
// Submit the form
await page.click('button[type="submit"]');
await page.waitForNavigation({ waitUntil: "networkidle2" });
// Scrape the content
const content = await page.content();
console.log("Page loaded successfully");
return content;
} finally {
await browser.close();
}
}
Adım 4: JavaScript callback'lerini tetikleyin
Bazı siteler form göndermek yerine token'ı bir JavaScript callback'ine iletmeyi bekler. Bu durumda token'ı enjekte etmek yetmez; reCAPTCHA'nın callback fonksiyonunu bulup çağırmanız gerekir.
// Trigger the reCAPTCHA callback
await page.evaluate((token) => {
// Method 1: Direct callback
if (typeof ___grecaptcha_cfg !== "undefined") {
const clients = ___grecaptcha_cfg.clients;
Object.keys(clients).forEach((key) => {
const client = clients[key];
// Find the callback function
const findCallback = (obj) => {
for (const prop in obj) {
if (typeof obj[prop] === "function") {
obj[prop](token);
return true;
}
if (typeof obj[prop] === "object" && obj[prop] !== null) {
if (findCallback(obj[prop])) return true;
}
}
return false;
};
findCallback(client);
});
}
}, token);
Yerel bağlam: e-ticaret QA akışları
Türkiye'de otomasyon işlerinin büyük kısmı e-ticaret ve ödeme entegrasyonlarının etrafında döner. Tipik senaryo şudur: bir staging ortamında (staging.example.com/qa-login) giriş akışını her sürümde regresyon testinden geçirmek istersiniz, ama form reCAPTCHA ile korunduğu için otomasyon takılır. CaptchaAI token'ı sunucu tarafında ürettiği için testi headless çalıştırabilirsiniz. Fiyatlandırma USD ve thread bazlıdır — BASIC ($15/ay, 5 thread) küçük bir QA paralelliği için yeterlidir ve TL kurundaki dalgalanma aylık maliyeti değiştirmez. Not: topladığınız veriler kişisel veri içeriyorsa KVKK kapsamına girer; bu akışları yalnızca yetkili test ve veri toplama süreçlerinde kullanın.
Tam çalışan örnek
Aşağıda tek dosyada uçtan uca akış var: tarayıcıyı başlatın, sitekey'i okuyun, çözün, token'ı enjekte edin ve gönderin.
const puppeteer = require("puppeteer");
const axios = require("axios");
const API_KEY = "YOUR_API_KEY";
async function solveCaptcha(siteKey, pageUrl) {
const submit = await axios.get("https://ocr.captchaai.com/in.php", {
params: {
key: API_KEY,
method: "userrecaptcha",
googlekey: siteKey,
pageurl: pageUrl,
},
});
const taskId = submit.data.split("|")[1];
while (true) {
await new Promise((r) => setTimeout(r, 5000));
const result = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: API_KEY, action: "get", id: taskId },
});
if (result.data === "CAPCHA_NOT_READY") continue;
if (result.data.startsWith("OK|")) return result.data.split("|")[1];
throw new Error(result.data);
}
}
(async () => {
const browser = await puppeteer.launch({
headless: "new",
args: ["--no-sandbox"],
});
const page = await browser.newPage();
try {
await page.goto("https://staging.example.com/qa-login", {
waitUntil: "networkidle2",
});
// Get the site key
const siteKey = await page.$eval(".g-recaptcha", (el) =>
el.getAttribute("data-sitekey")
);
// Solve
const token = await solveCaptcha(siteKey, page.url());
// Inject and submit
await page.evaluate((t) => {
document.getElementById("g-recaptcha-response").innerHTML = t;
}, token);
await page.click("#submit-btn");
await page.waitForNavigation();
console.log("Done:", page.url());
} finally {
await browser.close();
}
})();
Çözümü üretimde ölçeklendirin
Tek bir betik için yukarıdaki akış yeterlidir, ama birçok sayfayı paralel işleyen bir kazıma ya da QA hattında birkaç noktaya dikkat edin:
- Eşzamanlılık thread'e bağlıdır. CaptchaAI planları thread bazlıdır ve aynı anda kaç CAPTCHA'nın çözülebileceğini plan belirler. BASIC ($15/ay, 5 thread) beş eşzamanlı görev demektir; daha yüksek paralellik için ADVANCE ($90/ay, 50 thread) gibi bir plana geçin. Aynı anda açtığınız Puppeteer sayfası sayısını thread sayınızla uyumlu tutun.
- Yeniden deneme ekleyin. Ağ hataları ve nadir başarısız çözümler kaçınılmazdır. Başarısız bir görevi doğrudan tekrar denemek yerine, denemeler arasına artan bir bekleme (üstel geri çekilme / exponential backoff) koyun; bu,
res.phpuç noktasını gereksiz yormanızı önler. - Zaman aşımını yönetin.
MAX_ATTEMPTSvePOLL_INTERVALdeğerleri toplam bekleme süresini belirler. Bir görev bu süre içinde tamamlanmazsa token'ı beklemeyi bırakın, hatayı loglayın ve sayfayı sıradaki denemeye bırakın — takılı kalan bir sayfanın tüm kuyruğu bloke etmesine izin vermeyin. - Token'ı taze tutun. Token'lar kısa ömürlüdür. Görevi çözdükten sonra enjeksiyon ile form gönderimi arasına uzun işlemler koymayın; token'ı aldığınız anda kullanın.
Sık karşılaşılan sorunlar
| Sorun | Neden | Çözüm |
|---|---|---|
page.$eval başarısız oluyor |
CAPTCHA ilk render'dan sonra yükleniyor | page.waitForSelector('.g-recaptcha') ile bekleyin |
| Token çalışmıyor | Gönderilmeden önce süresi doldu | Aldıktan hemen sonra enjekte edin |
| Site Puppeteer'ı tespit ediyor | Standart yapılandırma eksik | Varsayılan Puppeteer yapılandırmasını kullanın |
Navigation timeout |
Gönderim sonrası sayfa yönlenmedi | Sitenin form yerine AJAX kullanıp kullanmadığını kontrol edin |
Sık sorulan sorular
Puppeteer'da bir CAPTCHA çözmek ne kadar sürer?
Çözüm süresi CAPTCHA türüne ve o anki yüke göre değişir; genellikle birkaç saniye içinde tamamlanır. Kod res.php uç noktasını 5 saniyede bir sorgular ve token hazır olur olmaz döner.
CaptchaAI hangi CAPTCHA türlerini destekliyor? hCaptcha çözülüyor mu?
reCAPTCHA v2/v3, Cloudflare Turnstile ve Challenge, GeeTest v3, görüntü/OCR ve grid CAPTCHA'ları desteklenir. hCaptcha ve FunCaptcha desteklenmez; GeeTest v4 çok yakında. CaptchaFox (beta), Friendly Captcha (beta) ve Lemin (beta) ise beta aşamasındadır.
Puppeteer entegrasyonu için CaptchaAI ne kadar?
Planlar thread bazlıdır ve USD ile faturalanır. BASIC ($15/ay, 5 thread) ile başlayabilir, paralel çözüm ihtiyacınız arttıkça ADVANCE ($90/ay, 50 thread) gibi üst planlara geçebilirsiniz. Solve başına ayrı ücret yoktur.
Token'ı enjekte ettim ama form yine de reddediyor, neden?
En sık neden token'ın süresinin dolmasıdır: token'ı aldıktan hemen sonra enjekte edip formu gönderin. Ayrıca doğru alanı (g-recaptcha-response) hedeflediğinizden ve sayfanın gerçekten form gönderimi kullandığından emin olun.
Cloudflare Turnstile'ı Puppeteer ile çözebilir miyim?
Evet. data-sitekey'i .cf-turnstile div'inden okuyun ve method=turnstile ile gönderin — yukarıdaki solveTurnstile fonksiyonu bu akışı gösterir.