12306 的验证码是其核心安全机制之一,持续不断地进行技术升级以防止自动化识别,任何针对其验证码的识别方法都可能是临时有效的,并且违反了 12306 的用户协议,本文仅用于技术学习和研究目的,请勿用于任何非法或商业用途,请尊重 12306 的规则,优先使用官方渠道购票。

核心挑战
12306 的验证码识别之所以困难,主要有以下几个原因:
- 动态变化:验证码的图片元素(如文字、干扰线、背景、问题)都是动态生成的,没有固定的模板。
- 多模态识别:它不仅仅是简单的字符识别,还包括了逻辑推理。“请点击包含‘火’字的选项”、“请点击所有交通工具”,这需要 AI 理解图片内容并做出判断。
- 高强度的图形干扰:包括但不限于:
- 严重的字符粘连和重叠。
- 大量、复杂的干扰线和噪点。
- 扭曲、拉伸、旋转的字符。
- 背景纹理和色块干扰。
- 对抗性强:12306 团队会持续分析识别方法,并针对性地更新验证码算法,使得旧的识别模型迅速失效。
技术方案分解
一个完整的 12306 自动化登录/抢票流程通常包括以下几个步骤:
- 获取验证码图片:通过 HTTP 请求从 12306 服务器下载验证码图片。
- 图片预处理:对获取到的图片进行降噪、二值化、分割等操作,以便于后续识别。
- 模型识别:使用深度学习模型对预处理后的图片进行识别。
- 对于字符类验证码:使用 OCR (Optical Character Recognition) 模型。
- 对于逻辑推理类验证码:使用目标检测或图像分类模型来识别图片中的元素,并结合规则进行推理。
- 结果提交与模拟操作:将识别结果提交到服务器,并处理后续的登录、查询等操作。
基于现成 OCR API(最简单,但可能不适用于所有类型)
对于一些相对简单的字符类验证码,可以尝试使用第三方 OCR 服务,如百度 OCR、腾讯云 OCR、Tesseract OCR 等。
示例:使用 Tesseract OCR (Java)
Tesseract 是一个开源的 OCR 引擎,Google 赞助。

准备工作
- 安装 Java 开发环境。
- 下载 Tesseract OCR 引擎:https://github.com/tesseract-ocr/tesseract
- 下载中文语言包
chi_sim.traineddata,并将其放入 Tesseract 的tessdata目录。
Java 依赖
在 pom.xml 中添加 Tess4J 依赖,它是 Tesseract 的 Java 封装。
<dependency>
<groupId>net.sourceforge.tess4j</groupId>
<artifactId>tess4j</artifactId>
<version>5.7.0</version> <!-- 请使用最新版本 -->
</dependency>
代码示例

import net.sourceforge.tess4j.Tesseract;
import net.sourceforge.tess4j.TesseractException;
import java.io.File;
public class SimpleOCR {
public static void main(String[] args) {
// 1. 创建 Tesseract 实例
Tesseract tesseract = new Tesseract();
// 2. 设置训练数据路径(指向 tessdata 目录)
// 如果你的 tesseract 安装在 C:\Program Files\Tesseract-OCR
// 那么路径就是 "C:/Program Files/Tesseract-OCR/tessdata"
tesseract.setDatapath("C:/Program Files/Tesseract-OCR/tessdata");
// 3. 设置语言库
tesseract.setLanguage("chi_sim"); // 使用简体中文库
try {
// 4. 识别图片
File imageFile = new File("path/to/your/12306_captcha.png");
String result = tesseract.doOCR(imageFile);
// 5. 输出结果
System.out.println("识别结果: " + result.trim());
} catch (TesseractException e) {
System.err.println("识别失败: " + e.getMessage());
}
}
}
局限性:
- 对于 12306 当前复杂的验证码,Tesseract 的识别率极低,几乎无法直接使用。
- 无法处理逻辑推理类问题。
基于深度学习(推荐,但技术门槛高)
这是目前唯一有可能有效识别 12306 复杂验证码的方法,核心是使用卷积神经网络。
步骤 1:数据收集与标注
这是最耗时、最关键的一步,你需要:
- 编写脚本:模拟浏览器行为,向 12306 服务器大量请求验证码图片。
- 人工标注:将获取到的图片和对应的正确答案(文字或点击坐标)整理成数据集。
- 字符类:将图片中的字符逐个标注出来。
- 逻辑类:标注出图片中每个物体的类别(如“火车”、“汽车”、“椅子”、“火”字)和位置(使用边界框,如
[x, y, width, height])。
步骤 2:模型选择与训练
根据验证码类型选择合适的模型。
A. 字符识别 (OCR)
可以使用 CRNN (CNN + RNN + CTC) 模型,它结合了 CNN 的特征提取能力和 RNN 的序列建模能力,非常适合识别不定长的文本序列。
- 输入:验证码图片。
- 输出:图片中的字符序列。
- 框架:可以使用 PyTorch 或 TensorFlow 来实现和训练 CRNN 模型。
B. 目标检测(用于逻辑推理)
对于“点击所有交通工具”这类问题,你需要识别出图片中的所有物体。
- 模型:使用 YOLO (You Only Look Once) 或 SSD (Single Shot MultiBox Detector) 等目标检测模型。
- 输入:验证码图片。
- 输出:图片中所有物体的类别和位置。
- 推理过程:
- 用 YOLO 模型识别出图片中的所有物体,得到一个物体列表,如
[ {class: 'train', bbox: [...]}, {class: 'car', bbox: [...]}, ... ]。 - 根据题目(如“所有交通工具”),过滤出符合条件的物体(如 'train', 'car', 'airplane')。
- 将这些物体的坐标信息返回给自动化脚本进行点击。
- 用 YOLO 模型识别出图片中的所有物体,得到一个物体列表,如
步骤 3:模型部署与识别
训练好模型后,你需要将模型集成到你的 Java 项目中。
- 使用 Java 深度学习框架:
- DJL (Deep Java Library):由 AWS 官方维护,是 Java 生态中最现代、功能最全的深度学习库,它支持 PyTorch、TensorFlow、MXNet 等多种后端,是 Java 调用预训练模型的最佳选择。
- Deeplearning4j (DL4J):一个老牌的 Java 深度学习框架,但生态和活跃度不如 DJL。
DJL 集成示例思路 (伪代码)
假设你已经训练好了一个可以识别字符的 CRNN 模型。
import ai.djl.Application;
import ai.djl.ModelException;
import ai.djl.engine.Engine;
import ai.djl.inference.Predictor;
import ai.djl.modality.cv.Image;
import ai.djl.modality.cv.ImageFactory;
import ai.djl.modality.cv.output.DetectedObjects;
import ai.djl.repository.zoo.Criteria;
import ai.djl.repository.zoo.ZooModel;
import ai.djl.translate.TranslateException;
public class CaptchaRecognition {
public static String recognizeCaptcha(String imagePath) throws ModelException, TranslateException {
// 1. 定义模型加载标准
// 这里需要替换成你自己的模型路径和描述文件
Criteria<Image, String> criteria = Criteria.builder()
.optApplication(Application.CV.OBJECT_DETECTION)
.setTypes(Image.class, String.class)
.optEngine(Engine.getDefaultEngineName()) // 使用默认引擎,如 PyTorch
.optFilter("backbone", "crnn_your_model_name") // 根据你的模型命名
.optProgress(System.out::print)
.build();
// 2. 加载模型
try (ZooModel<Image, String> model = criteria.loadModel()) {
// 3. 创建预测器
try (Predictor<Image, String> predictor = model.newPredictor()) {
// 4. 加载图片
Image image = ImageFactory.getInstance().fromFile(new File(imagePath));
// 5. 进行预测
String result = predictor.predict(image);
return result;
}
}
}
public static void main(String[] args) throws ModelException, TranslateException {
String captchaPath = "path/to/your/12306_captcha.png";
String answer = recognizeCaptcha(captchaPath);
System.out.println("AI 识别结果: " + answer);
}
}
第三方服务(商业方案)
市面上有一些专门提供打码服务的公司,他们通过人工或更高级的 AI 来识别各种验证码。
工作流程:
- 你的 Java 程序从 12306 获取验证码图片。
- 将图片的 Base64 编码或文件流发送到打码平台的 API。
- 打码平台识别后,将结果(如答案或点击坐标)返回给你的程序。
- 你的程序使用结果继续操作。
优点:
- 无需自己处理复杂的模型训练和部署。
- 通常识别率较高,且能应对 12306 的更新。
缺点:
- 收费:按次或按量计费。
- 隐私风险:你的验证码图片会发送给第三方公司。
- 延迟:网络传输和识别过程会增加时间。
知名打码平台:如 打码兔、超级鹰 等,你可以在它们的网站上找到 Java SDK 的使用文档。
完整流程整合(以方案二为例)
一个完整的 Java 程序流程如下:
// 1. 导入必要的包 (如 Jsoup, OkHttp, DJL, etc.)
public class TicketBookingBot {
public static void main(String[] args) {
// 初始化登录会话
// ...
while (true) {
try {
// 1. 获取验证码图片
byte[] captchaImageBytes = fetchCaptchaImageFrom12306();
saveImageToFile(captchaImageBytes, "current_captcha.png");
// 2. 使用 DJL 调用训练好的模型进行识别
String answer = recognizeCaptcha("current_captcha.png");
System.out.println("识别出的答案: " + answer);
// 3. 提交答案
boolean submitResult = submitAnswerTo12306(answer);
if (submitResult) {
System.out.println("验证码正确,继续登录...");
// ... 登录逻辑
break;
} else {
System.out.println("验证码错误,重试...");
}
// 短暂休眠,避免请求过于频繁
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 伪函数:从12306获取图片
private static byte[] fetchCaptchaImageFrom12306() { /* ... */ }
// 伪函数:保存图片
private static void saveImageToFile(byte[] data, String filename) { /* ... */ }
// 伪函数:调用DJL模型识别
private static String recognizeCaptcha(String imagePath) throws ModelException, TranslateException {
// ... 使用 DJL 代码 ...
}
// 伪函数:向12306提交答案
private static boolean submitAnswerTo12306(String answer) { /* ... */ }
}
总结与建议
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 现成 OCR API | 简单、快速上手 | 识别率极低,无法处理复杂和逻辑类验证码 | 仅用于学习最基础的 OCR 概念,不适用于 12306 |
| 自研深度学习 | 灵活性高、可定制、一旦成功效果最好 | 技术门槛极高、耗时长(数据标注、模型训练)、维护成本高(模型需不断更新) | 技术研究、个人挑战、有充足时间和技术实力的团队 |
| 第三方打码服务 | 开发周期短、识别率高、能应对更新 | 收费、有隐私风险、存在延迟 | 商业项目、个人抢票工具(但需考虑成本和合规性) |
最终建议:
对于绝大多数开发者而言,方案三(第三方打码服务) 是在有限时间内实现高成功率的最务实选择,如果你是出于学习和研究的目的,那么方案二(自研深度学习) 是一条非常有价值的路,但请务必有耐心和毅力,因为这将是一个长期的“军备竞赛”。
