Node.js tarafında bir otomasyon akışında reCAPTCHA, Cloudflare Turnstile veya resim tabanlı bir CAPTCHA çıktığında iş durur. Bu rehber, Playwright'ı CaptchaAI API'sine bağlayarak bu doğrulamaları tek bir akış içinde çözmenin tam yolunu gösterir — sitekey çıkarımından token enjeksiyonuna ve formun gönderilmesine kadar.
Playwright'ı seçmemizin nedeni pratik: tek API ile Chromium, Firefox ve WebKit'i sürebilmesi, yerleşik otomatik bekleme (auto-waiting) ve rota (route) tabanlı ağ müdahalesi. Aşağıdaki her kod parçası çalışır durumdadır; kopyalayıp kendi Node.js projenize yerleştirebilirsiniz.
Kurulum ve önkoşullar
Öncelikle Playwright'ı ve bir tarayıcı çekirdeğini kurun. Bu rehberde Chromium yeterli:
npm install playwright
npx playwright install chromium
CaptchaAI hesabınızdaki API anahtarını hazır bulundurun; ilk çağrı için ücretsiz kredi yeterlidir. Anahtarı kontrol panelinizin API bölümünden kopyalayabilirsiniz.
Playwright tarayıcısını yapılandırma
Tarayıcıyı standart bir yapılandırmayla başlatın: gerçekçi bir userAgent, sabit bir görüntü alanı ve en-US yerel ayarı. addInitScript içindeki küçük düzeltme, Playwright'ın kendi otomasyon izini nötrlemek içindir.
const { chromium } = require("playwright");
async function createBrowser() {
const browser = await chromium.launch({
headless: false,
args: ["--no-sandbox"],
});
const context = await browser.newContext({
userAgent:
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " +
"(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
viewport: { width: 1920, height: 1080 },
locale: "en-US",
});
// Remove Playwright detection
await context.addInitScript(() => {
Object.defineProperty(navigator, "webdriver", { get: () => undefined });
delete navigator.__proto__.webdriver;
});
const page = await context.newPage();
return { browser, context, page };
}
CaptchaAI çözüm fonksiyonu
Tüm CAPTCHA türleri aynı iki adımlı akışı paylaşır: görevi in.php'ye gönderin, sonucu res.php'den sorgulayın. Aşağıdaki solveCaptcha fonksiyonu bu döngüyü tek yerde toplar; method ve parametreleri değiştirerek reCAPTCHA, Turnstile veya resim CAPTCHA için yeniden kullanabilirsiniz.
const API_KEY = "YOUR_API_KEY";
async function solveCaptcha(method, params) {
// Submit
const submitResp = await fetch("https://ocr.captchaai.com/in.php", {
method: "POST",
body: new URLSearchParams({ key: API_KEY, method, json: "1", ...params }),
});
const submitData = await submitResp.json();
if (submitData.status !== 1) throw new Error(`Submit: ${submitData.request}`);
const taskId = submitData.request;
// Poll
for (let i = 0; i < 30; i++) {
await new Promise((r) => setTimeout(r, 5000));
const pollResp = await fetch(
`https://ocr.captchaai.com/res.php?${new URLSearchParams({
key: API_KEY,
action: "get",
id: taskId,
json: "1",
})}`
);
const data = await pollResp.json();
if (data.status === 1) return data.request;
if (data.request === "ERROR_CAPTCHA_UNSOLVABLE") throw new Error("Unsolvable");
}
throw new Error("Timed out");
}
Sorgulama döngüsü 5 saniyede bir sonucu kontrol eder ve en fazla 30 kez dener. Böylece uzun süren çözümlerde bile fonksiyon takılıp kalmaz, temiz bir zaman aşımı hatası döndürür.
Playwright ile reCAPTCHA v2 çözme
reCAPTCHA v2'de üç adım vardır: sayfadan sitekey değerini okuyun, CaptchaAI'ye gönderin, dönen token'ı g-recaptcha-response alanına enjekte edin. Gizli alanı görünür yapmak ve callback'i tetiklemek, formun token'ı gerçekten kabul etmesi için gereklidir.
async function solveRecaptchaV2(page) {
// Extract sitekey
const sitekey = await page.evaluate(() => {
const el = document.querySelector("[data-sitekey]");
return el ? el.getAttribute("data-sitekey") : null;
});
if (!sitekey) throw new Error("Sitekey not found");
// Solve
const token = await solveCaptcha("userrecaptcha", {
googlekey: sitekey,
pageurl: page.url(),
});
// Inject
await page.evaluate((t) => {
const textarea = document.getElementById("g-recaptcha-response");
if (textarea) {
textarea.value = t;
textarea.style.display = "block";
}
// Trigger callback
if (typeof ___grecaptcha_cfg !== "undefined") {
const clients = ___grecaptcha_cfg.clients;
for (const key in clients) {
for (const prop in clients[key]) {
try {
const cb = clients[key][prop];
if (cb && typeof cb.callback === "function") cb.callback(t);
} catch {}
}
}
}
}, token);
return token;
}
Playwright ile Cloudflare Turnstile çözme
Turnstile'da token, cf-turnstile-response adlı gizli bir alana yazılır. Aşağıdaki fonksiyon önce .cf-turnstile öğesini arar, bulamazsa 0x ile başlayan herhangi bir data-sitekey değerine geri döner — bu, Turnstile widget'ının çeşitli gömme biçimlerini kapsar.
async function solveTurnstile(page) {
// Extract sitekey
const sitekey = await page.evaluate(() => {
const el = document.querySelector(".cf-turnstile[data-sitekey]");
if (el) return el.getAttribute("data-sitekey");
// Fallback: any data-sitekey starting with 0x
const all = document.querySelectorAll("[data-sitekey]");
for (const item of all) {
const key = item.getAttribute("data-sitekey");
if (key && key.startsWith("0x")) return key;
}
return null;
});
if (!sitekey) throw new Error("Turnstile sitekey not found");
// Solve
const token = await solveCaptcha("turnstile", {
sitekey,
pageurl: page.url(),
});
// Inject
await page.evaluate((t) => {
document
.querySelectorAll('[name="cf-turnstile-response"]')
.forEach((el) => (el.value = t));
}, token);
return token;
}
Token'ı yalnızca ilk eşleşen alana değil, cf-turnstile-response adını taşıyan tüm alanlara yazmak, çok adımlı formlarda gönderim hatalarını önler.
CAPTCHA türünü otomatik algılama ve çözme
Gerçek projelerde her sayfanın hangi CAPTCHA'yı taşıdığını önceden bilmezsiniz. detectAndSolve, sayfadaki işaretlere bakarak türü belirler ve doğru method ile çözüme yönlendirir. Bu, tek bir botun farklı hedeflerde çalışmasını sağlayan parçadır.
async function detectAndSolve(page) {
const captchaInfo = await page.evaluate(() => {
// Check reCAPTCHA
const recaptcha = document.querySelector("[data-sitekey]");
if (
recaptcha &&
(document.querySelector(".g-recaptcha") ||
document.querySelector('script[src*="recaptcha"]'))
) {
return { type: "recaptcha", sitekey: recaptcha.getAttribute("data-sitekey") };
}
// Check Turnstile
const turnstile = document.querySelector(".cf-turnstile[data-sitekey]");
if (turnstile) {
return { type: "turnstile", sitekey: turnstile.getAttribute("data-sitekey") };
}
// Check image CAPTCHA
const captchaImg = document.querySelector(
'img.captcha, img[alt*="captcha"], img[src*="captcha"]'
);
if (captchaImg) {
return { type: "image" };
}
return { type: null };
});
if (!captchaInfo.type) return null;
console.log(`Detected: ${captchaInfo.type}`);
switch (captchaInfo.type) {
case "recaptcha":
return await solveCaptcha("userrecaptcha", {
googlekey: captchaInfo.sitekey,
pageurl: page.url(),
});
case "turnstile":
return await solveCaptcha("turnstile", {
sitekey: captchaInfo.sitekey,
pageurl: page.url(),
});
case "image":
return await solveImageCaptcha(page);
default:
return null;
}
}
Playwright ile resim CAPTCHA çözme
Klasik resim (OCR) CAPTCHA'larında sitekey yoktur; onun yerine görselin kendisini gönderirsiniz. Playwright'ın öğe düzeyinde ekran görüntüsü alma yeteneği burada işe yarar: CAPTCHA görselini base64'e çevirip base64 yöntemiyle gönderin, dönen metni giriş alanına yazın.
async function solveImageCaptcha(page) {
const captchaImg = page.locator(
'img.captcha, img[alt*="captcha"], img[src*="captcha"]'
).first();
// Screenshot the CAPTCHA element
const imgBuffer = await captchaImg.screenshot();
const imgBase64 = imgBuffer.toString("base64");
// Solve via CaptchaAI
const answer = await solveCaptcha("base64", { body: imgBase64 });
// Type the answer
const input = page.locator(
'input[name="captcha"], input[name="code"], input.captcha-input'
).first();
await input.fill(answer);
return answer;
}
Rota müdahalesiyle CAPTCHA parametrelerini yakalama
Bazı CAPTCHA'lar — örneğin GeeTest v3 — parametrelerini DOM yerine ağ yanıtlarında taşır. Playwright'ın page.on("response") kancasıyla bu yanıtları dinleyip gt ve challenge gibi değerleri akış sırasında toplayabilirsiniz.
async function interceptCaptchaRoutes(page, url) {
const captchaParams = {};
// Intercept responses
page.on("response", async (response) => {
const respUrl = response.url();
// GeeTest parameters
if (respUrl.includes("geetest") || respUrl.includes("gt=")) {
try {
const data = await response.json();
if (data.gt) {
captchaParams.type = "geetest";
captchaParams.gt = data.gt;
captchaParams.challenge = data.challenge;
}
} catch {}
}
});
await page.goto(url, { waitUntil: "networkidle" });
return captchaParams;
}
Uçtan uca otomasyon sınıfı
Parçaları tek bir sınıfta toplamak, kodu bakımı kolay hâle getirir. PlaywrightAutomation, tarayıcıyı başlatmaktan formu doldurmaya, CAPTCHA'yı çözüp token'ı enjekte etmekten formu göndermeye kadar tüm oturum açma akışını yönetir.
Türkiye'de çalışan otomasyon geliştiricileri için tipik senaryo, bir e-ticaret veya fintech ürününün oturum açma ya da ödeme adımının QA testidir. Aşağıdaki örnek, gerçek bir hedef yerine bir staging (staging.example.com) ortamına karşı çalışır — kendi test verilerinizle giriş yaparsınız. Kişisel veri içeren akışlarda KVKK kapsamında yalnızca yetkili test ortamlarında çalıştığınızdan emin olun.
const { chromium } = require("playwright");
class PlaywrightAutomation {
#apiKey;
#browser;
#context;
#page;
constructor(apiKey) {
this.#apiKey = apiKey;
}
async start(headless = false) {
this.#browser = await chromium.launch({
headless,
args: ["--no-sandbox"],
});
this.#context = await this.#browser.newContext({
userAgent:
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0 Safari/537.36",
viewport: { width: 1920, height: 1080 },
});
await this.#context.addInitScript(() => {
Object.defineProperty(navigator, "webdriver", { get: () => undefined });
});
this.#page = await this.#context.newPage();
}
async stop() {
await this.#browser?.close();
}
async navigate(url) {
await this.#page.goto(url, { waitUntil: "networkidle" });
}
async fillForm(fields) {
for (const [selector, value] of Object.entries(fields)) {
await this.#page.fill(selector, value);
}
}
async solveCaptcha() {
return await detectAndSolve(this.#page);
}
async submit(selector = 'button[type="submit"]') {
await this.#page.click(selector);
await this.#page.waitForLoadState("networkidle");
return this.#page.url();
}
async loginWithCaptcha(url, fields, submitSelector) {
await this.navigate(url);
await this.fillForm(fields);
const token = await this.solveCaptcha();
if (token) {
// Inject token
await this.#page.evaluate((t) => {
const re = document.getElementById("g-recaptcha-response");
if (re) re.value = t;
document
.querySelectorAll('[name="cf-turnstile-response"]')
.forEach((el) => (el.value = t));
}, token);
}
return await this.submit(submitSelector);
}
get page() {
return this.#page;
}
}
// Usage
const bot = new PlaywrightAutomation("YOUR_API_KEY");
await bot.start();
try {
const result = await bot.loginWithCaptcha(
"https://staging.example.com/qa-login",
{
"#email": "user@example.com",
"#password": "pass123",
},
"#login-btn"
);
console.log(`Redirected to: ${result}`);
} finally {
await bot.stop();
}
Playwright ve Puppeteer karşılaştırması
Yeni bir projeye başlarken sık sorulan karar: Playwright mı, Puppeteer mı? Aşağıdaki tablo, CAPTCHA otomasyonu açısından önemli farkları özetler.
| Özellik | Playwright | Puppeteer |
|---|---|---|
| Çoklu tarayıcı | Chromium, Firefox, WebKit | Yalnızca Chromium |
| API tarzı | Locator tabanlı | Seçici (selector) tabanlı |
| Otomatik bekleme | Yerleşik | Manuel bekleme |
| Ağ müdahalesi | Rota (route) tabanlı | İstek tabanlı |
| Varsayılan yapılandırma | Dengeli varsayılanlar | Ek eklenti gerekir |
| TypeScript | Yerleşik destek | Topluluk türleri |
Sorun giderme
CAPTCHA otomasyonunda en sık karşılaşılan beş belirti ve çözümleri:
| Belirti | Neden | Çözüm |
|---|---|---|
page.evaluate null döndürüyor |
Öğe henüz yüklenmedi | Önce waitForSelector çağırın |
| Turnstile algılanmıyor | Sayfa yüklendikten sonra JS ile geliyor | .cf-turnstile seçicisini bekleyin |
| Token enjekte edildi ama form gönderilmiyor | reCAPTCHA callback'i tetiklenmedi | callback'i açıkça çağırın |
| Tarayıcı bot olarak işaretleniyor | addInitScript eksik |
Başlatma betiğini ekleyin |
networkidle zaman aşımına uğruyor |
Uzun süreli sorgulama (long-polling) betikleri | Yerine domcontentloaded kullanın |
Sık sorulan sorular
CaptchaAI, Playwright ile hangi CAPTCHA türlerini çözebilir?
reCAPTCHA v2 ve v3 (Invisible ve Enterprise dahil), Cloudflare Turnstile ve Challenge, GeeTest v3, resim/OCR, grid ve BLS CAPTCHA'ları desteklenir. hCaptcha ve FunCaptcha (Arkose Labs) desteklenmez. CaptchaFox, Friendly Captcha ve Lemin beta aşamasındadır; GeeTest v4 için destek çok yakında.
Playwright'ı headless modda çalıştırabilir miyim?
Evet. launch() içinde headless: true ayarlamanız yeterli. CaptchaAI çözümü bağımsız olarak API üzerinden yaptığı için headless mod çözüm başarısını etkilemez; yalnızca yerel hata ayıklamada headless: false görsel takip kolaylığı sağlar.
Token'ı enjekte ettim ama form gönderilmiyor, neden?
Genellikle callback tetiklenmemiştir. reCAPTCHA v2'de g-recaptcha-response alanını doldurmak yeterli olmayabilir; sayfanın kendi callback fonksiyonunu açıkça çağırmanız gerekir. Turnstile'da ise token'ı cf-turnstile-response adlı tüm alanlara yazdığınızdan emin olun.
CaptchaAI fiyatlandırması Playwright otomasyonunda nasıl işler?
CaptchaAI thread bazlı faturalandırır ve her plan sınırsız çözüm içerir. Küçük bir bot için BASIC ($15/ay, 5 thread), yüksek eşzamanlılık gerektiren toplu işler için ADVANCE ($90/ay, 50 thread) uygundur. Fiyatlar USD sabittir; TL dalgalanmasından etkilenmeyen öngörülebilir bir aylık maliyet sunar.
Özet
Node.js Playwright + CaptchaAI; otomatik algılama, rota müdahalesi ve çoklu CAPTCHA desteğiyle modern bir otomasyon yığını oluşturur. PlaywrightAutomation sınıfı, CAPTCHA'lı oturum açma iş akışının tamamını tek bir yerde toplar — kopyalayın, sitekey ve seçicileri kendi hedefinize göre uyarlayın, çalıştırın.