Popular Captcha 求解器
使用 PopularCaptchaImage 或 PopularClassification 任务类型解决对象交互验证码。questionType 字段决定挑战的处理方式。
支持的模式
questionType | 说明 | 响应格式 |
|---|---|---|
objectClassify | 从九宫格中选择所有匹配的图片 | 每张图片一个 boolean[] |
objectClick | 点击目标对象的中心 | {x, y}[] 坐标 |
objectDrag | 将拼图块拖到正确位置 | {start, end}[] |
objectTag | 为图片中的对象打标签 | 标签数组 |
grid | 通用九宫格选择 | 布尔数组 |
bbox | 边界框检测 | 坐标 |
创建任务 —— 九宫格分类
POST
/createTaskHostapi.captchasonic.com
Content-Typeapplication/json
{
"apiKey": "YOUR_API_KEY",
"task": {
"type": "PopularCaptchaImage",
"questionType": "objectClassify",
"question": "Select all objects with a bridge",
"queries": ["BASE64_IMG_1", "BASE64_IMG_2"]
}
}
响应
{
"code": 200,
"msg": "",
"answers": [true, false, true, true, false, true],
"questionType": "objectClassify",
"meta": { "pass_report": true, "fail_report": true }
}
创建任务 —— 对象点击
{
"apiKey": "YOUR_API_KEY",
"task": {
"type": "PopularCaptchaImage",
"questionType": "objectClick",
"question": "Click on the center of the car",
"queries": ["BASE64_MAIN_IMAGE"]
}
}
创建任务 —— 对象拖放
{
"apiKey": "YOUR_API_KEY",
"task": {
"type": "PopularCaptchaImage",
"questionType": "objectDrag",
"question": "Drag the puzzle piece to the gap",
"queries": ["BASE64_BACKGROUND"],
"examples": ["BASE64_PUZZLE_PIECE"]
}
}
任务参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
type | string | ✅ | PopularCaptchaImage 或 PopularClassification |
questionType | string | ✅ | objectClassify、objectClick、objectDrag、objectTag、grid、bbox |
question | string | ✅ | 挑战指令文本 |
queries | string[] | ✅ | base64 编码的图片 |
examples | string[] | 仅拖放 | 拖放任务的目标对象 |
screenshort | boolean | 否 | 若图片是截图,设为 true |
视频验证码求解(动态画布)
某些挑战(例如 hCaptcha 变体)包含动态画布动画,而非静态图片。这些挑战通过 PopularCaptchaImage 任务类型的专用变体来解决,需要客户端捕获/记录画布(将多个视频帧融合成单张图片,或提交原始视频)。
为避免在每次挑战中都记录和处理视频,强烈建议实现客户端缓存机制,将目标问题映射到已解析的画布配置。
缓存策略(客户端)
- 同形字归一化(Homoglyph Normalization): 在查询或保存到缓存之前,清除
question文本中的 Unicode 同形字(例如看起来像英文的西里尔/希腊字符)。这确保了缓存键与后端的归一化管道一致。 - TTL(生存时间): 24 小时。
- 容量: 最多 800 条,基于 LRU(最近最少使用)淘汰算法。
- 缓存键:
"nc_video_cache",将归一化后的问题文本映射到canvasParams。
求解工作流
场景 A:缓存未命中(双阶段流)
sequenceDiagram
participant Client as 客户端
participant Server as 服务端
Note over Client: 步骤 1: 检测到验证码
Client->>Server: POST /createTask (初始标准载荷)
Server-->>Client: 400 Bad Request (questionVariant: "canvasvideo")
Note over Client: 步骤 2: 拦截 canvasParams 并保存至缓存
Note over Client: 步骤 3: 录制画布视频 (例如 2 秒) 并融合帧
Client->>Server: POST /createTask (丰富后的第二阶段载荷)
Server-->>Client: 200 OK (已解决的答案坐标)
- 初始抓取: 向求解器 API (
/createTask) 提交标准的单帧请求。 - 后端信号: 如果后端检测到此目标问题是一个视频验证码,它会响应
400状态码,且questionVariant为"canvasvideo",并包含所需的canvasParams。 - 缓存存储: 在本地保存这些
canvasParams。 - 画布录制与融合:
- 如果
canvasParams.video为true,录制指定时间的画布。 - 根据
canvasParams.format提取并融合/合并帧。
- 如果
- 第二阶段重新提交: 构建包含
canvasVideo: true、queries中的融合帧、以及录制的视频 base64 数组的丰富载荷。重新提交给后端以获得200成功响应。
场景 B:缓存命中(单阶段流)
sequenceDiagram
participant Client as 客户端
participant Server as 服务端
Note over Client: 步骤 1: 查询缓存 (归一化问题匹配)
Note over Client: 步骤 2: 立即录制画布并融合帧
Client->>Server: POST /createTask (直接发送丰富后的载荷)
Server-->>Client: 200 OK (已解决的答案坐标)
Note over Client: 退避方案: 若服务端拒绝,使缓存失效并运行双阶段流
- 求解前缓存检查: 使用归一化的问题文本查询本地缓存。
- 即时画布准备: 如果存在有效的缓存项,立即录制画布并根据缓存的参数融合/合并帧。
- 单阶段提交: 在第一次请求中直接提交完整构建的载荷(融合帧 + 视频数据)。
- 失效回退: 如果服务端拒绝了请求(例如缓存参数已过期或在服务端已更改),使本地缓存的配置失效/删除,并回退到全新的双阶段流。
载荷结构
1. 初始请求(第一阶段 - 缓存未命中)
在知道需要视频处理之前,首次尝试解决验证码时发送。
{
"apiKey": "YOUR_API_KEY",
"task": {
"type": "PopularCaptchaImage",
"queries": ["data:image/jpeg;base64,..."],
"examples": ["data:image/jpeg;base64,..."],
"question": "Please click on the living room",
"screenshot": false,
"questionType": "objectClick",
"websiteURL": "example.com",
"websiteKEY": "sitekey-value"
}
}
2. 响应(指示需要视频验证码)
当后端检测到需要视频验证码时返回。
{
"code": 400,
"msg": "canvasvideo",
"questionVariant": "canvasvideo",
"questionType": "objectClick",
"canvasParams": {
"duration": 2,
"video": true,
"format": ["frameMerge"],
"frames": [0.1, 0.5, 1.0],
"framecount": null
}
}
3. 丰富后的请求(第二阶段重新提交 / 单阶段缓存命中)
包含融合后的图片(位于 queries 中)和录制的画布视频。
{
"apiKey": "YOUR_API_KEY",
"task": {
"type": "PopularCaptchaImage",
"queries": ["data:image/jpeg;base64,..."], // 融合帧结果
"examples": ["data:image/jpeg;base64,..."],
"question": "please click on the living room", // 归一化问题
"screenshot": false,
"questionType": "objectClick",
"websiteURL": "example.com",
"websiteKEY": "sitekey-value",
"choices": [],
"canvasVideo": true,
"format": "frameMerge", // 或 "framecount"
"video": ["data:video/mp4;base64,..."] // Base64 录制的视频
}
}
4. 响应(成功求解)
{
"code": 200,
"status": "ready",
"questionType": "objectClick",
"answers": [
[
{ "x": 109, "y": 179 },
{ "x": 163, "y": 260 }
]
],
"meta": {
"data": "...",
"fail_report": true,
"pass_report": true
}
}
错误码
| 错误码 | 错误 | 说明 |
|---|---|---|
| 1 | KEY_DOES_NOT_EXIST | API 密钥无效或未找到。 |
| 2 | NO_SLOT_AVAILABLE | 所有求解器插槽都被占用。 |
| 3 | ZERO_BALANCE | 账户余额为零。 |
| 10 | ERROR_BAD_PARAMETERS | 请求字段无效或缺失。 |
| 12 | ERROR_CAPTCHA_UNSOLVABLE | 无法解决验证码。 |
| 14 | PLAN_EXPIRED | 您的计划已过期。 |
| 16 | RATE_LIMITED | 请求过多,请放慢速度。 |
| 17 | DAILY_LIMIT_EXCEEDED | 达到每日使用上限。 |
| 18 | QUOTA_LIMIT_EXCEEDED | 计划配额已用完。 |
| 21 | SERVICE_UNAVAILABLE | 后端暂时不可用。 |
代码示例
# 第一步:创建任务
RESPONSE=$(curl -s -X POST "https://api.captchasonic.com/createTask" \
-H "Content-Type: application/json" \
-d '{
"apiKey": "YOUR_API_KEY",
"task": {
"type": "PopularCaptchaImage",
"questionType": "objectClassify",
"question": "Select all objects with a bridge",
"queries": [
"BASE64_IMG_1",
"BASE64_IMG_2"
]
}
}')
echo "Create Task Response: $RESPONSE"
TASK_ID=$(echo $RESPONSE | grep -o '"taskId":"[^"]*"' | cut -d'"' -f4)
# 第二步:轮询结果
while true; do
RESULT=$(curl -s -X POST "https://api.captchasonic.com/getTaskResult" \
-H "Content-Type: application/json" \
-d "{\"apiKey\": \"YOUR_API_KEY\", \"taskId\": \"$TASK_ID\"}")
echo "Result: $RESULT"
echo "$RESULT" | grep -q '"status":"processing"' || break
sleep 2
done
API 演练场
POST
Log in to auto-fill your API key
Payload
Response
Hit Send to see response
⌘ + Enter
Parameters
Error Codes
terminal