Scarnox · Blog

Rozwiązywanie PopularCaptcha w Playwright i Puppeteer (Node.js)

Automatyzacja przeglądarki staje w miejscu w chwili, gdy pojawi się PopularCaptcha. Klikanie przez siatkę obrazków ze skryptu jest wolne i kruche. Pobranie tokena z API rozwiązującego i wstawienie go do strony to jedno żądanie, a reszta skryptu działa dalej jak zwykle.

Ten przewodnik omawia Playwright i Puppeteer w Node.js. Przebieg jest taki sam w obu, a przykłady używają rozwiązań proxyless, które Scarnox uruchamia we własnej sieci. PopularCaptcha to typ, który Scarnox rozwiązuje dziś. Inne typy captcha są wkrótce, więc poniższy kod dotyczy tylko PopularCaptcha.

Przebieg

  1. Odczytaj sitekey ze strony.
  2. Poproś Scarnox o token jednym blokującym żądaniem POST.
  3. Wstaw token do pola odpowiedzi albo przekaż go do callbacku strony, a potem wyślij formularz.

Konfiguracja klucza

Załóż konto i zacznij z darmowym kredytem próbnym, bez karty. Trzymaj klucz w zmiennej środowiskowej, nigdy w kodzie testów:

export SCARNOX_KEY="scarnox_YOURAPIKEY"

Node 18 i nowsze zawierają fetch, więc wywołanie API nie wymaga żadnych dodatkowych pakietów.

Playwright

import { chromium } from "playwright";

const API_KEY = process.env.SCARNOX_KEY;

async function solve(sitekey, url) {
  const r = await fetch("https://api.scarnox.com/api/tasks/create", {
    method: "POST",
    headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({ captcha_type: "PopularCaptchaTaskProxyless", site_key: sitekey, site_url: url }),
  });
  const data = await r.json();
  if (data.errorId !== 0) throw new Error(`${data.errorCode}: ${data.errorDescription}`);
  return data.solution.token;
}

const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto("https://example.com/signup");

const sitekey = await page.getAttribute("[data-sitekey]", "data-sitekey");
const token = await solve(sitekey, page.url());

// Wstaw token do obu pól odpowiedzi, które strona może czytać.
await page.evaluate((t) => {
  for (const name of ["h-captcha-response", "g-recaptcha-response"]) {
    document.querySelectorAll(`[name="${name}"]`).forEach((el) => { el.value = t; });
  }
}, token);

await page.fill("#email", "you@example.com");
await page.click("button[type=submit]");

Zwróć uwagę, że site_url pochodzi z page.url(). To strona, na której znajduje się widget, a tego potrzebuje solver. Rozwiązanie odbywa się po załadowaniu strony, więc sitekey jest już w DOM.

Puppeteer

Puppeteer różni się tylko API selektorów. Użyj ponownie funkcji solve z powyższego przykładu i zamień wywołania strony:

const sitekey = await page.$eval("[data-sitekey]", (el) => el.dataset.sitekey);
const token = await solve(sitekey, page.url());

await page.evaluate((t) => {
  document.querySelectorAll('[name="h-captcha-response"],[name="g-recaptcha-response"]')
    .forEach((el) => { el.value = t; });
}, token);

Gdy strona używa callbacku

Niektóre strony nie czytają ukrytego pola. Zamiast tego czekają na callback JavaScriptu widgetu. Znajdź funkcję wskazaną w atrybucie data-callback widgetu i wywołaj ją z tokenem:

await page.evaluate((t) => {
  const name = document.querySelector("[data-sitekey]")?.dataset.callback;
  if (name && typeof window[name] === "function") window[name](t);
}, token);

Jeśli strona używa obu mechanizmów, ustaw pole i wywołaj callback. Zrobienie obu jest nieszkodliwe.

Widgety niewidoczne

Niewidoczny widget nie ma pola wyboru. Strona uruchamia go z własnego kodu, zwykle w chwili, gdy odwiedzający naciska wyślij. Wyślij to samo żądanie z opcjonalnym polem is_invisible ustawionym na true:

body: JSON.stringify({
  captcha_type: "PopularCaptchaTaskProxyless",
  site_key: sitekey,
  site_url: url,
  is_invisible: true,
}),

Przewodnik o niewidocznej captcha wyjaśnia, jak rozpoznać takie widgety i jak przekazać token z powrotem.

Wskazówki dotyczące czasu

Testowanie własnego przepływu

Uruchom przykład na formularzu, który posiadasz, zanim skierujesz go na coś większego. Sprawdź trzy rzeczy: token został zaakceptowany, formularz się wysłał i pojawiła się następna strona. Gdy przepływ działa od początku do końca, utrzymaj go zielonym w CI, z jednym prawdziwym rozwiązaniem na przebieg.

Wypróbuj za darmo

Załóż konto i zacznij z darmowym kredytem próbnym, bez karty. To wystarczy, by podłączyć i sprawdzić cały przepływ. Koszty przy dużym wolumenie znajdziesz w aktualnym cenniku.

Debugowanie odrzuconego tokena

Gdy strona odrzuca token, odtwórz przepływ jednym żądaniem i jednym załadowaniem strony, zanim zmienisz cokolwiek innego. Sprawdź po kolei:

Zapisz HTML strony w momencie błędu za pomocą await page.content(). Porównanie tego HTML z działającym przebiegiem zwykle od razu pokazuje różnicę: inny sitekey, brakujące pole albo widget wyrenderowany w innym trybie.

Dalsze kroki