如何用 PHP 识别 reCAPTCHA v2(含隐形版)

reCAPTCHA v2 有三种变体,在 PHP 中它们是一个带选项数组的方法。复选框是最简单的调用,隐形和 Enterprise 是标志,两者也可以同时设置。PHP 还是唯一一个完全没有异步方案的 CapSkip SDK,这简化了代码,但也改变了你组织批量工作的方式。
设置
# 需要 PHP 8.0 或更高版本,并需要 curl 和 json 扩展, # 这两个扩展几乎在每个 PHP 安装中都自带。 composer require capskip/capskip
CapSkip 在你自己的机器上识别,因此必须运行桌面应用。将客户端指向其设置中显示的端口:
use CapSkip\CapSkip;
$solver = new CapSkip([
'apiKey' => 'capskip', // any string when key validation is off
'host' => '127.0.0.1',
'port' => 8080,
'recaptchaTimeout' => 300, // seconds
]);在生产环境中,从环境变量读取这些值:
use CapSkip\CapSkip;
$solver = new CapSkip([
'apiKey' => getenv('CAPSKIP_API_KEY') ?: 'capskip',
'host' => getenv('CAPSKIP_HOST') ?: '127.0.0.1',
'port' => (int) (getenv('CAPSKIP_PORT') ?: 8080),
]);三种变体
| 变体 | 需添加的选项 |
|---|---|
| 复选框 | 无 |
| 隐形 | ['invisible' => 1] |
| Enterprise | ['enterprise' => 1] |
| 隐形 Enterprise | 两个键都加 |
// Checkbox: sitekey and page URL only.
$result = $solver->recaptcha(
'6Lc...YOUR_SITEKEY',
'https://example.com/login'
);
echo $result['code']; // g-recaptcha-response token
// Invisible.
$result = $solver->recaptcha($sitekey, $pageUrl, ['invisible' => 1]);
// Enterprise, and both together.
$result = $solver->recaptcha($sitekey, $pageUrl, ['enterprise' => 1]);
$result = $solver->recaptcha($sitekey, $pageUrl, [
'enterprise' => 1,
'invisible' => 1,
]);返回值是一个关联数组,因此 $result['code'] 就是 token。 $result['captchaId'] 也一并提供,如果你想记录是哪次识别产生了它。
正确获取输入
sitekey 是 data-sitekey 小组件容器上的属性,或者是传给 grecaptcha.render 的第一个参数——在隐形模式下没有容器可查看时。它始终以 6L 开头,且是公开的。
URL 必须是小组件实际渲染所在的页面。传入你的表单处理程序或登录后的重定向,是 token 识别顺利却随后验证失败的常见原因。
提交 token
$ch = curl_init('https://example.com/login');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS => http_build_query([
'g-recaptcha-response' => $result['code'],
'username' => '...',
'password' => '...',
]),
]);
$response = curl_exec($ch);
curl_close($ch);token 只能使用一次,有效期约两分钟,因此请在流程中尽可能晚地识别。如果网站把 token 交给 JavaScript 回调而不是表单字段,识别相同但提交方式不同。我们的 reCAPTCHA v2 回调识别工具 页面介绍了这种方式。
错误,以及人们容易忽略的命名空间
PHP 把它的异常放在各自的命名空间下,这会让从 Python 或 Node 示例复制导入语句的人栽跟头:
use CapSkip\CapSkip;
use CapSkip\Exceptions\ValidationException;
use CapSkip\Exceptions\NetworkException;
use CapSkip\Exceptions\ApiException;
use CapSkip\Exceptions\TimeoutException;
try {
$result = $solver->recaptcha($sitekey, $pageUrl);
} catch (ValidationException $e) {
// missing or malformed arguments
} catch (NetworkException $e) {
// CapSkip is not running on the configured port
} catch (ApiException $e) {
// the sitekey or pageurl was rejected
} catch (TimeoutException $e) {
// exceeded recaptchaTimeout
}这四个都继承自 CapSkip\Exceptions\CapSkipError,因此如果你更愿意在一处处理失败,只需对基类做一次 catch 即可处理全部情况。
识别多个
PHP 是同步执行的,因此每次识别都会阻塞直到完成:
use CapSkip\CapSkip;
$solver = new CapSkip();
foreach ($targets as $target) {
$results[] = $solver->recaptcha($target['sitekey'], $target['url']);
}该包导出了 AsyncCapSkip,但在 PHP 中它只是一个别名,只是为了让从其他 SDK 移植的代码继续运行。它不会增加并发。要实现真正的并行,请运行多个工作进程或使用队列,而不要指望 SDK 让识别相互重叠。
使用代理
$result = $solver->recaptcha($sitekey, $pageUrl, [
'proxy' => ['type' => 'HTTPS', 'uri' => 'user:[email protected]:3128'],
]);reCAPTCHA、Turnstile 和 极验 支持代理,但图片验证码不支持,因为它们是从图片字节中识别的,永远不会到达目标网站。
常见问题
我需要轮询结果吗?
不需要。SDK 会在内部轮询并返回完成的 token,因此 CAPCHA_NOT_READY 永远不会出现在你的代码里。它在 250 毫秒后开始检查,并从那里逐步退避。
识别会阻塞我的 Web 请求吗?
会,而且这在 PHP 中很关键。一次 reCAPTCHA 识别可能耗时数秒,因此在页面渲染中执行会占用一个工作进程。请把识别放到队列作业或 CLI 工作进程中,而不要阻塞请求线程。
有哪些要求?
PHP 8.0 或更新版本,并启用 curl 和 json 扩展,这两者在大多数安装中都已捆绑。没有其他运行时依赖,因此它可以直接接入现有项目,而不会拉入一大堆包。
小结
一个方法、三种变体,通过 invisible 和 enterprise 放入选项数组。读取 $result['code'],将其作为 g-recaptcha-response,从以下模块导入异常: CapSkip\Exceptions导入异常,并让识别远离你的请求线程。
更完整的 PHP 接口见 PHP 验证码识别 页面,其他语言见 reCAPTCHA v2 识别 页面,还有一个 在线 v2 演示 可供测试。CapSkip 采用 本地验证码识别工具,因此不会按次识别收费。
