Tutorials

Node.js Playwright ile CaptchaAI tam entegrasyonu

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.

İlgili makaleler

Bu makale için yorumlar devre dışı bırakılmıştır.