PHP 使用 cURL 实现验证码求解器:可运行示例(2026)
在 PHP 中求解验证码只需要 cURL 扩展,大多数 PHP 环境都已自带。你向 Scarnox API 发送一次 JSON POST 请求,从响应中读取 solution.token,然后将该 token 提交到网站的表单。本指南完整介绍针对 PopularCaptcha 的 PHP 流程,Scarnox 目前以无代理方式支持该类型的求解。
先获取免费试用额度,无需信用卡,并在你自己的页面上试用这个脚本,然后再用于其他任何地方。
环境要求
- PHP 8.0 或更新版本,并启用
curl和json扩展。使用php -m检查。 - 一个 Scarnox API 密钥。
- 页面 PopularCaptcha 组件的 sitekey。如果你需要帮助找到它,请参阅如何找到验证码 sitekey。
求解函数
该函数向 API 发送请求,检查 errorId,并返回 token。任何失败都会抛出异常,以便调用方决定是否重试:
<?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, // 调用会在 token 准备好后返回
]);
$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 会阻塞直到 token 准备好,不存在轮询步骤。
重试临时性失败
失败的求解不会被计费,因此几次重试的成本很低。在调用外层加上循环,并在每次失败后等待更长的时间:
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 jar 提交 token
许多网站会将验证码与加载表单时所用的会话绑定。cookie 文件可以让 cURL 在 GET 和 POST 中发送相同的 cookie。下面的函数会把 token 提交到页面的表单:
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 提交表单,并在两个验证码字段中都填入 token。
$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 已经就位。token 在求解之后立即发出,因为它约两分钟后过期。请将求解调用紧挨着放在此函数之前,而不是在长时间批处理的开头就执行。
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 字段。有些页面还需要调用一个以 token 为参数的 JavaScript 回调,服务端脚本无法触发它。在这种情况下,请改用浏览器工具。无感验证码指南涵盖了各种情况。
时机与过期
token 约在两分钟内有效。请保持求解与提交的时间间隔很短。验证码 token 过期一文说明了如何区分过期的 token 与被拒绝的 token。
负责任地抓取
如果你的 PHP 脚本抓取的是公开页面,请在开始之前检查 robots.txt 和网站的服务条款。请求之间添加延迟,并且不要存储你不需要的个人数据。求解验证码并不会改变你被允许收集的内容。法律方面的内容请参阅网页抓取是否合法。
价格
你按每次成功的求解付费,失败的求解不收费。当前费率请查看定价页面,并先获取免费试用额度,无需信用卡。