captchaocr.cnCaptchaOCR
教程 04

验证码识别 API 多语言调用示例

下面的 cURL、Python、Java、Go、Node.js 和 Rust 示例调用同一个接口:POST /api/v1/recognize。图片放在表单字段 file,密钥放在请求头 X-API-Key。请把密钥保存在环境变量 CAPTCHA_API_KEY 中,不要写进仓库。

查询参数 charset=auto 会保留数字和字母。expected_length=4 按四位验证码校验,位数不同就改这个值,传 0 则关闭长度校验。HTTP 200 只表示请求已处理;先看 accepted,为 false 时不要直接使用 text。

已经持有 Base64 时,改用 Content-Type: application/json,正文为 {"image":"<base64>"},不要再同时上传文件。字段说明见 API 参考,Python 官方客户端见 Python SDK。

cURL

先在服务端将完整密钥保存到 CAPTCHA_API_KEY 环境变量,再上传一张图片。

curl -X POST "https://captchaocr.cn/api/v1/recognize?charset=auto&expected_length=4" \
  -H "X-API-Key: $CAPTCHA_API_KEY" \
  -F "file=@captcha.png"

Python

使用 requests。官方 SDK 见 Python SDK 文档。

import os
import requests

with open("captcha.png", "rb") as image:
    response = requests.post(
        "https://captchaocr.cn/api/v1/recognize?charset=auto&expected_length=4",
        headers={"X-API-Key": os.environ["CAPTCHA_API_KEY"]},
        files={"file": ("captcha.png", image, "image/png")},
        timeout=30,
    )

response.raise_for_status()
result = response.json()
if result["accepted"]:
    print(result["text"], result["confidence"])
else:
    print("rejected:", result["rejection_reason"])

Java

使用 JDK 11 及以上的 java.net.http,不额外引入 HTTP 库。响应体是 JSON,生产代码建议用 Jackson 读取 accepted 与 text。

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;

public class Recognize {
    public static void main(String[] args) throws Exception {
        String boundary = "----CaptchaOcrBoundary";
        byte[] file = Files.readAllBytes(Path.of("captcha.png"));
        byte[] body = multipart(boundary, file);

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://captchaocr.cn/api/v1/recognize?charset=auto&expected_length=4"))
            .header("X-API-Key", System.getenv("CAPTCHA_API_KEY"))
            .header("Content-Type", "multipart/form-data; boundary=" + boundary)
            .POST(HttpRequest.BodyPublishers.ofByteArray(body))
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() >= 400) {
            throw new IllegalStateException(response.body());
        }
        System.out.println(response.body());
    }

    static byte[] multipart(String boundary, byte[] file) {
        String head = "--" + boundary + "\r\n"
            + "Content-Disposition: form-data; name=\"file\"; filename=\"captcha.png\"\r\n"
            + "Content-Type: image/png\r\n\r\n";
        String tail = "\r\n--" + boundary + "--\r\n";
        byte[] prefix = head.getBytes(StandardCharsets.UTF_8);
        byte[] suffix = tail.getBytes(StandardCharsets.UTF_8);
        byte[] body = new byte[prefix.length + file.length + suffix.length];
        System.arraycopy(prefix, 0, body, 0, prefix.length);
        System.arraycopy(file, 0, body, prefix.length, file.length);
        System.arraycopy(suffix, 0, body, prefix.length + file.length, suffix.length);
        return body;
    }
}

Go

仅使用标准库 net/http 与 mime/multipart。

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "mime/multipart"
    "net/http"
    "os"
)

type recognizeResult struct {
    Text             string  `json:"text"`
    Accepted         bool    `json:"accepted"`
    Confidence       float64 `json:"confidence"`
    RejectionReason  *string `json:"rejection_reason"`
}

func main() {
    file, err := os.Open("captcha.png")
    if err != nil {
        panic(err)
    }
    defer file.Close()

    var body bytes.Buffer
    writer := multipart.NewWriter(&body)
    part, err := writer.CreateFormFile("file", "captcha.png")
    if err != nil {
        panic(err)
    }
    if _, err = io.Copy(part, file); err != nil {
        panic(err)
    }
    writer.Close()

    req, err := http.NewRequest(http.MethodPost, "https://captchaocr.cn/api/v1/recognize?charset=auto&expected_length=4", &body)
    if err != nil {
        panic(err)
    }
    req.Header.Set("X-API-Key", os.Getenv("CAPTCHA_API_KEY"))
    req.Header.Set("Content-Type", writer.FormDataContentType())

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    payload, _ := io.ReadAll(resp.Body)
    if resp.StatusCode >= 400 {
        panic(string(payload))
    }

    var result recognizeResult
    if err = json.Unmarshal(payload, &result); err != nil {
        panic(err)
    }
    if result.Accepted {
        fmt.Println(result.Text, result.Confidence)
        return
    }
    fmt.Println("rejected:", result.RejectionReason)
}

Node.js

需要 Node.js 18 及以上,使用全局 fetch 与 FormData。

import { readFile } from "node:fs/promises";

const bytes = await readFile("captcha.png");
const form = new FormData();
form.append("file", new Blob([bytes], { type: "image/png" }), "captcha.png");

const response = await fetch("https://captchaocr.cn/api/v1/recognize?charset=auto&expected_length=4", {
  method: "POST",
  headers: { "X-API-Key": process.env.CAPTCHA_API_KEY },
  body: form,
});
const result = await response.json();
if (!response.ok) {
  throw new Error(result.message ?? "recognize failed");
}
if (result.accepted) {
  console.log(result.text, result.confidence);
} else {
  console.log("rejected:", result.rejection_reason);
}

Rust

使用 reqwest 的 multipart 功能。依赖写在示例顶部的 Cargo.toml 注释里。

// Cargo.toml
// reqwest = { version = "0.12", features = ["multipart", "json"] }
// tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
// serde = { version = "1", features = ["derive"] }

use serde::Deserialize;

#[derive(Deserialize)]
struct Recognize {
    text: String,
    accepted: bool,
    confidence: f64,
    rejection_reason: Option<String>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let bytes = std::fs::read("captcha.png")?;
    let part = reqwest::multipart::Part::bytes(bytes)
        .file_name("captcha.png")
        .mime_str("image/png")?;
    let form = reqwest::multipart::Form::new().part("file", part);

    let response = reqwest::Client::new()
        .post("https://captchaocr.cn/api/v1/recognize?charset=auto&expected_length=4")
        .header("X-API-Key", std::env::var("CAPTCHA_API_KEY")?)
        .multipart(form)
        .send()
        .await?;
    let status = response.status();
    if !status.is_success() {
        return Err(response.text().await?.into());
    }
    let result: Recognize = response.json().await?;
    if result.accepted {
        println!("{} {}", result.text, result.confidence);
    } else {
        println!("rejected: {:?}", result.rejection_reason);
    }
    Ok(())
}