Tutorials

Node.js Puppeteer + CaptchaAI Erweiterte Muster

Das einfache Lösen von Puppeteer + CAPTCHA ist ein gelöstes Problem. In diesem Leitfaden werden die erweiterten Muster behandelt: Anforderungsabfangen, Iframe-Verarbeitung, mehrseitige Formularabläufe und parallele Browserlösung.


Voraussetzungen

npm install puppeteer

Puppeteer-Setup

const puppeteer = require("puppeteer");

const API_KEY = "YOUR_API_KEY";

async function createBrowser() {
  const browser = await puppeteer.launch({
    headless: "new",
    args: [
      "--no-sandbox",
      "--disable-setuid-sandbox",
    ],
  });
  return browser;
}

CaptchaAI Solver-Helfer

function sleep(ms) {
  return new Promise((r) => setTimeout(r, ms));
}

async function solveCaptcha(method, params) {
  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;

  for (let i = 0; i < 30; i++) {
    await sleep(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");
}

reCAPTCHA in Iframes

reCAPTCHA v2 rendert innerhalb eines Iframes. So gehen Sie richtig damit um:

async function solveRecaptchaInIframe(page) {
  // Wait for the reCAPTCHA iframe to load
  await page.waitForSelector('iframe[src*="recaptcha"]', { timeout: 10000 });

  // Get the sitekey from the main page
  const sitekey = await page.evaluate(() => {
    // From data-sitekey attribute
    const el = document.querySelector("[data-sitekey]");
    if (el) return el.getAttribute("data-sitekey");

    // From iframe src
    const iframe = document.querySelector('iframe[src*="recaptcha"]');
    if (iframe) {
      const match = iframe.src.match(/k=([A-Za-z0-9_-]{40})/);
      if (match) return match[1];
    }

    return null;
  });

  if (!sitekey) throw new Error("Sitekey not found");

  // Solve via CaptchaAI
  const token = await solveCaptcha("userrecaptcha", {
    googlekey: sitekey,
    pageurl: page.url(),
  });

  // Inject token into the main page (not the iframe)
  await page.evaluate((t) => {
    document.getElementById("g-recaptcha-response").value = t;
    document.getElementById("g-recaptcha-response").style.display = "block";

    // Trigger the callback
    if (typeof ___grecaptcha_cfg !== "undefined") {
      const clients = ___grecaptcha_cfg.clients;
      for (const key in clients) {
        const client = clients[key];
        for (const prop in client) {
          try {
            if (client[prop] && typeof client[prop].callback === "function") {
              client[prop].callback(t);
            }
          } catch {}
        }
      }
    }
  }, token);

  return token;
}

Fordern Sie das Abfangen zur CAPTCHA-Erkennung an

async function interceptAndSolve(page, url) {
  const captchaData = {};

  // Intercept requests to detect CAPTCHA type
  await page.setRequestInterception(true);
  page.on("request", (request) => {
    const requestUrl = request.url();

    if (requestUrl.includes("recaptcha/api2")) {
      captchaData.type = "recaptcha";
      const match = requestUrl.match(/k=([A-Za-z0-9_-]{40})/);
      if (match) captchaData.sitekey = match[1];
    }

    if (requestUrl.includes("challenges.cloudflare.com/turnstile")) {
      captchaData.type = "turnstile";
    }

    if (requestUrl.includes("geetest") || requestUrl.includes("gt=")) {
      captchaData.type = "geetest";
    }

    request.continue();
  });

  // Intercept responses for CAPTCHA parameters
  page.on("response", async (response) => {
    if (response.url().includes("geetest") || response.url().includes("register")) {
      try {
        const data = await response.json();
        if (data.gt && data.challenge) {
          captchaData.gt = data.gt;
          captchaData.challenge = data.challenge;
        }
      } catch {}
    }
  });

  await page.goto(url, { waitUntil: "networkidle2" });

  // Now solve based on detected type
  if (captchaData.type === "recaptcha" && captchaData.sitekey) {
    return await solveCaptcha("userrecaptcha", {
      googlekey: captchaData.sitekey,
      pageurl: url,
    });
  }

  if (captchaData.type === "turnstile") {
    const sitekey = await page.evaluate(() => {
      const el = document.querySelector("[data-sitekey]");
      return el ? el.getAttribute("data-sitekey") : null;
    });
    if (sitekey) {
      return await solveCaptcha("turnstile", { sitekey, pageurl: url });
    }
  }

  if (captchaData.type === "geetest" && captchaData.gt) {
    return await solveCaptcha("geetest", {
      gt: captchaData.gt,
      challenge: captchaData.challenge,
      pageurl: url,
    });
  }

  return null;
}

Mehrseitiges Formular mit CAPTCHA

async function multiPageFormFlow(browser, startUrl, formSteps) {
  const page = await browser.newPage();

  for (let i = 0; i < formSteps.length; i++) {
    const step = formSteps[i];
    console.log(`Step ${i + 1}: ${step.description}`);

    if (i === 0) {
      await page.goto(startUrl, { waitUntil: "networkidle2" });
    }

    // Fill form fields
    for (const [selector, value] of Object.entries(step.fields || {})) {
      await page.waitForSelector(selector, { visible: true });
      await page.click(selector, { clickCount: 3 }); // Select all
      await page.type(selector, value, { delay: 50 }); // Human-like typing
    }

    // Handle dropdowns
    for (const [selector, value] of Object.entries(step.selects || {})) {
      await page.select(selector, value);
    }

    // Check for CAPTCHA
    const hasCaptcha = await page.evaluate(() => {
      return !!(
        document.querySelector("[data-sitekey]") ||
        document.querySelector(".cf-turnstile") ||
        document.querySelector(".geetest_holder")
      );
    });

    if (hasCaptcha) {
      console.log("CAPTCHA detected, solving...");
      const token = await interceptAndSolve(page, page.url());
      if (token) {
        await page.evaluate((t) => {
          const el = document.getElementById("g-recaptcha-response");
          if (el) el.value = t;
          const cf = document.querySelector('[name="cf-turnstile-response"]');
          if (cf) cf.value = t;
        }, token);
      }
    }

    // Click next/submit
    if (step.submit) {
      await page.click(step.submit);
      await page.waitForNavigation({ waitUntil: "networkidle2" });
    }
  }

  return page;
}

// Usage
const browser = await createBrowser();
const page = await multiPageFormFlow(browser, "https://example.com/register", [
  {
    description: "Personal info",
    fields: { "#name": "John Doe", "#email": "john@example.com" },
    submit: "#next-btn",
  },
  {
    description: "Address",
    fields: { "#address": "123 Main St", "#city": "New York" },
    selects: { "#state": "NY" },
    submit: "#next-btn",
  },
  {
    description: "Verification (has CAPTCHA)",
    fields: {},
    submit: "#submit-btn",
  },
]);

Parallele Browserlösung

async function parallelSolve(urls, maxConcurrent = 3) {
  const browser = await createBrowser();
  const results = [];
  let running = 0;

  const processUrl = async (url) => {
    const page = await browser.newPage();
    try {
      await page.goto(url, { waitUntil: "networkidle2" });

      // Detect and extract sitekey
      const sitekey = await page.evaluate(() => {
        const el = document.querySelector("[data-sitekey]");
        return el ? el.getAttribute("data-sitekey") : null;
      });

      if (sitekey) {
        const token = await solveCaptcha("userrecaptcha", {
          googlekey: sitekey,
          pageurl: url,
        });
        results.push({ url, status: "solved", token });
      } else {
        results.push({ url, status: "no-captcha" });
      }
    } catch (error) {
      results.push({ url, status: "error", error: error.message });
    } finally {
      await page.close();
    }
  };

  // Process with concurrency limit
  const queue = [...urls];
  const workers = [];

  for (let i = 0; i < Math.min(maxConcurrent, urls.length); i++) {
    workers.push(
      (async () => {
        while (queue.length > 0) {
          const url = queue.shift();
          if (url) await processUrl(url);
        }
      })()
    );
  }

  await Promise.all(workers);
  await browser.close();

  return results;
}

Screenshot-basierte CAPTCHA-Lösung

async function solveScreenshotCaptcha(page, captchaSelector) {
  const element = await page.$(captchaSelector);
  if (!element) throw new Error("CAPTCHA element not found");

  // Take a screenshot of just the CAPTCHA element
  const screenshot = await element.screenshot({ encoding: "base64" });

  // Solve via CaptchaAI
  const answer = await solveCaptcha("base64", { body: screenshot });

  // Type the answer
  const input = await page.$(
    'input[name="captcha"], input[name="code"], input.captcha-input'
  );
  if (input) {
    await input.click({ clickCount: 3 });
    await input.type(answer, { delay: 30 });
  }

  return answer;
}

Fehlerbehebung

Symptom Ursache Beheben
Puppeteer als Bot erkannt Browser-Flags fehlen --disable-blink-features=AutomationControlled hinzufügen
Die Injektion des reCAPTCHA-Tokens schlägt fehl Mehrere reCAPTCHA-Widgets Finden Sie den richtigen g-recaptcha-response anhand des Index
Der Iframe-Schalter funktioniert nicht Ursprungsübergreifender Iframe Lösen Sie die Lösung über die API, anstatt in den Iframe zu klicken
page.goto hängt Die Seite wird unendlich oft geladen Verwenden Sie die Option timeout und networkidle2
Parallele Seiten stürzen den Browser ab Kein Speicher mehr Reduzieren Sie die Parallelität oder verwenden Sie --disable-dev-shm-usage

Häufig gestellte Fragen

Sollte ich Puppeteer oder Playwright verwenden?

Puppeteer verfügt über ein größeres Ökosystem und mehr Tutorials. Playwright bietet ein besseres API-Design und Unterstützung für mehrere Browser. Beide funktionieren mit CaptchaAI gleich gut.

Beeinflusst die Browser-Konfiguration das Lösen von CAPTCHAs?

Nein. CaptchaAI löst Token über die API. Die Browser-Konfiguration beeinflusst nur, wie häufig CAPTCHAs ausgelöst werden, nicht die Lösungsrate.

Wie viele Seiten kann ich parallel betreiben?

Hängt vom verfügbaren RAM ab. Jede Seite benötigt ca. 50–100 MB. Bei 8 GB RAM beschränken Sie sich auf 5–10 gleichzeitige Seiten.


Zusammenfassung

Puppeteer + CaptchaAI: Mit Anfragen-Abfang zur CAPTCHA-Erkennung, iframe-fähiger Token-Injektion, mehrseitiger Formularverarbeitung und paralleler Lösung bietet dies eine robuste Browser-Lösung für den Durchsatz.

Verwandte Leitfäden

Diskussionen (0)

Noch keine Kommentare.