Scarnox · Blog

Solver captcha w PHP z cURL: działający przykład (2026)

Do rozwiązania captcha w PHP wystarczy rozszerzenie cURL, które jest w większości instalacji. Wysyłasz jedno żądanie POST z JSON do API Scarnox, odczytujesz solution.token z odpowiedzi i przesyłasz ten token do formularza strony. Ten przewodnik przechodzi przez pełny przepływ w PHP dla PopularCaptcha, które Scarnox dziś rozwiązuje bez proxy.

Zacznij od darmowego kredytu próbnego, bez karty i wypróbuj skrypt na stronie, którą kontrolujesz, zanim użyjesz go gdziekolwiek indziej.

Wymagania

Funkcja solvera

Ta funkcja wysyła żądanie do API, sprawdza errorId i zwraca token. Przy każdym błędzie rzuca wyjątek, więc wywołujący może zdecydować, czy ponowić:

<?php
declare(strict_types=1);

const SCARNOX_API_KEY = 'scarnox_YOURAPIKEY';
const SCARNOX_API_URL = 'https://api.scarnox.com/api/tasks/create';

function solveCaptcha(string $sitekey, string $pageUrl, bool $invisible = false): string
{
    $body = [
        'captcha_type' => 'PopularCaptchaTaskProxyless',
        'site_key'     => $sitekey,
        'site_url'     => $pageUrl,
    ];
    if ($invisible) {
        $body['is_invisible'] = true;
    }

    $ch = curl_init(SCARNOX_API_URL);
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => json_encode($body, JSON_THROW_ON_ERROR),
        CURLOPT_HTTPHEADER     => [
            'Authorization: Bearer ' . SCARNOX_API_KEY,
            'Content-Type: application/json',
        ],
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 185, // wywołanie zwraca wynik, gdy token będzie gotowy
    ]);

    $raw = curl_exec($ch);
    if ($raw === false) {
        $error = curl_error($ch);
        throw new RuntimeException('cURL error: ' . $error);
    }

    $data = json_decode((string) $raw, true);
    if (!is_array($data)) {
        throw new RuntimeException('Response was not JSON');
    }
    if (($data['errorId'] ?? 1) !== 0) {
        throw new RuntimeException(
            ($data['errorCode'] ?? 'ERROR') . ': ' . ($data['errorDescription'] ?? 'unknown')
        );
    }

    return (string) $data['solution']['token'];
}

Nagłówek Authorization używa prefiksu Bearer. Timeout wynosi 185 sekund, bo API blokuje się, aż token będzie gotowy, bez etapu odpytywania.

Ponawianie przejściowych błędów

Nieudane rozwiązanie nie jest naliczane, więc kilka ponowień jest tanie. Umieść pętlę wokół wywołania i czekaj trochę dłużej po każdym błędzie:

function solveWithRetry(string $sitekey, string $pageUrl, int $attempts = 3): string
{
    $lastError = null;
    for ($i = 1; $i <= $attempts; $i++) {
        try {
            return solveCaptcha($sitekey, $pageUrl);
        } catch (RuntimeException $e) {
            $lastError = $e;
            fwrite(STDERR, "Attempt $i failed: " . $e->getMessage() . PHP_EOL);
            sleep($i);
        }
    }
    throw $lastError;
}

Pełną politykę błędów, w tym to, przy których błędach się zatrzymać, znajdziesz w artykule obsługa błędów solvera captcha.

Wiele serwisów wiąże captcha z sesją, która załadowała formularz. Plik ciasteczek pozwala cURL wysłać te same ciasteczka przy GET i POST. Poniższa funkcja wysyła token do formularza strony:

function submitToken(string $pageUrl, string $token): string
{
    $cookieFile = sys_get_temp_dir() . '/scarnox-cookies.txt';

    // Najpierw GET, aby serwis ustawił ciasteczko sesji.
    $ch = curl_init($pageUrl);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_COOKIEFILE     => $cookieFile,
        CURLOPT_COOKIEJAR      => $cookieFile,
        CURLOPT_TIMEOUT        => 60,
    ]);
    curl_exec($ch);

    // Następnie POST formularza z tokenem w obu polach captcha.
    $fields = http_build_query([
        'email'                 => 'you@example.com',
        'h-captcha-response'    => $token,
        'g-recaptcha-response'  => $token,
    ]);

    curl_setopt_array($ch, [
        CURLOPT_POST       => true,
        CURLOPT_POSTFIELDS => $fields,
        CURLOPT_TIMEOUT    => 60,
    ]);
    $body = curl_exec($ch);
    if ($body === false) {
        throw new RuntimeException('Form submit failed: ' . curl_error($ch));
    }

    return (string) $body;
}

Liczą się dwie rzeczy. GET wykonuje się przed rozwiązaniem, żeby ciasteczko było na miejscu. Token jest wysyłany zaraz po rozwiązaniu, bo wygasa po około dwóch minutach. Umieść wywołanie solvera bezpośrednio przed tą funkcją, a nie na początku długiej partii.

Plik ciasteczek jest współdzielony przez wszystkie wywołania w tym samym procesie, więc jeśli uruchamiasz kilka rejestracji równolegle, daj każdemu kontu lub sesji osobny plik. W przeciwnym razie jedna sesja nadpisze ciasteczka drugiej, a drugi POST zawiedzie z przyczyn, które wyglądają na problem z captcha, ale nim nie są.

Składamy całość

$pageUrl = 'https://example.com/signup';
$token   = solveWithRetry('YOUR-SITEKEY-UUID', $pageUrl);
$html    = submitToken($pageUrl, $token);

echo strlen($html) . " bytes returned\n";

Uruchom z wiersza poleceń:

php signup.php

Niewidoczne widżety

Jeśli widżet jest niewidoczny, wywołaj solveCaptcha($sitekey, $pageUrl, true). Pole is_invisible zostanie wtedy wysłane z żądaniem. Niektóre strony wymagają też wywołania zwrotnego JavaScript z tokenem, którego skrypt po stronie serwera nie może uruchomić. W takim przypadku użyj narzędzia przeglądarkowego. Przewodnik o niewidocznej captcha omawia te przypadki.

Czas i wygasanie

Token jest ważny przez około dwie minuty. Trzymaj rozwiązanie i wysłanie blisko siebie. Wygasanie tokenu captcha wyjaśnia, jak odróżnić token wygasły od odrzuconego.

Scrapuj odpowiedzialnie

Jeśli Twój skrypt PHP scrapuje publiczne strony, sprawdź robots.txt i regulamin serwisu przed startem. Dodaj opóźnienie między żądaniami i nie przechowuj danych osobowych, których nie potrzebujesz. Rozwiązanie captcha nie zmienia tego, co wolno Ci zbierać. Stronę prawną omawia artykuł czy web scraping jest legalny.

Cennik

Płacisz za każde udane rozwiązanie. Nieudane rozwiązania nie są naliczane. Aktualne stawki znajdziesz na stronie cennika, Aby zacząć, odbierz darmowy kredyt próbny, bez karty.

Następne kroki