Решатель капчи на PHP с cURL: рабочий пример (2026)
Для решения капчи на PHP нужно только расширение cURL, которое есть в большинстве установок. Вы отправляете один JSON POST в API Scarnox, читаете solution.token из ответа и отправляете этот токен в форму сайта. Этот гайд проводит по всему потоку PHP для PopularCaptcha, которую Scarnox сегодня решает в варианте proxyless.
Начните с пробного баланса, карта не нужна и проверьте скрипт на странице, которой вы управляете, прежде чем использовать его где-либо ещё.
Требования
- PHP 8.0 или новее с включёнными расширениями
curlиjson. Проверить можно командойphp -m. - Ключ API Scarnox.
- Sitekey виджета PopularCaptcha на странице. Помощь с поиском — в гайде о том, как найти sitekey капчи.
Функция решателя
Эта функция отправляет запрос в API, проверяет errorId и возвращает токен. При любой ошибке она выбрасывает исключение, чтобы вызывающий код мог решить, повторять ли:
<?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, // вызов возвращается, когда токен готов
]);
$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'];
}
Заголовок Authorization использует префикс Bearer. Таймаут — 185 секунд, потому что API блокируется до готовности токена, и шага опроса нет.
Повтор временных сбоев
Неудачное решение не списывается, поэтому несколько повторов стоят недорого. Оберните вызов циклом и ждите чуть дольше после каждого сбоя:
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;
}
Полную политику обработки ошибок, включая то, на каких сбоях останавливаться, см. в обработке ошибок решателя капчи.
Отправка токена с cookie-файлом
Многие сайты привязывают капчу к сессии, которая загрузила форму. Файл cookie позволяет cURL отправлять одни и те же cookie в GET и POST. Функция ниже отправляет токен в форму страницы:
function submitToken(string $pageUrl, string $token): string
{
$cookieFile = sys_get_temp_dir() . '/scarnox-cookies.txt';
// Сначала GET, чтобы сайт выставил cookie сессии.
$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);
// Затем POST формы с токеном в обоих полях капчи.
$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;
}
Важны две вещи. GET выполняется до решения, чтобы cookie уже было на месте. А токен уходит сразу после решения, потому что он истекает примерно через две минуты. Поставьте вызов решения непосредственно перед этой функцией, а не в начале длинной пачки.
Файл cookie общий для всех вызовов в одном процессе. Если вы запускаете несколько регистраций параллельно, дайте каждому аккаунту или сессии свой файл. Иначе одна сессия перезапишет cookie другой, и второй POST упадёт по причинам, которые выглядят как проблема с капчей, хотя ею не являются.
Собираем всё вместе
$pageUrl = 'https://example.com/signup';
$token = solveWithRetry('YOUR-SITEKEY-UUID', $pageUrl);
$html = submitToken($pageUrl, $token);
echo strlen($html) . " bytes returned\n";
Запуск из командной строки:
php signup.php
Невидимые виджеты
Если виджет невидимый, вызывайте solveCaptcha($sitekey, $pageUrl, true). Тогда с запросом отправляется поле is_invisible. Некоторым страницам также нужен JavaScript-колбэк с токеном, который серверный скрипт вызвать не может. В таком случае используйте инструмент на базе браузера. Случаи описаны в гайде невидимая капча.
Тайминг и истечение
Токен действителен около двух минут. Держите решение и отправку рядом во времени. Истечение токена капчи объясняет, как отличить истёкший токен от отклонённого.
Парсинг с ответственностью
Если ваш PHP-скрипт парсит публичные страницы, сначала проверьте robots.txt и условия сайта. Добавьте паузу между запросами и не храните персональные данные, которые вам не нужны. Решение капчи не меняет того, что вам разрешено собирать. Правовая сторона — в гайде законен ли парсинг сайтов.
Тарифы
Вы платите за каждое успешное решение. Неудачные решения не списываются. Актуальные расценки — на странице цен, а начать можно с пробного баланса, карта не нужна.