项目概述
这是一个基于预训练多模态模型的免费验证码识别API服务,旨在为开发者提供高识别率、零成本的验证码识别解决方案。该项目使用先进的视觉-语言模型技术,能够准确识别各种常见类型的验证码,帮助自动化脚本和应用程序轻松突破验证码障碍。
项目背景
事情是这样的,我的一位同事在自动化操作某个网站以实现资料上传时遇到了麻烦。你猜怎么着?网站使用了验证码!为了突破这一障碍,他一直在使用某度收费接口进行验证码识别,虽然识别率达到了90%,但每次看到账单的时候,心里都不是滋味。
当我得知这个情况后,决定亲自查看一下那些"难以攻克"的验证码。经过一番研究,我发现这些验证码并不像想象中的那么难对付。于是,我开始着手研究一种解决方案,希望能提供同样高效的识别率,同时还能免除费用上的担忧。
项目优势
- 完全免费:无需支付任何API调用费用
- 高识别率:识别准确率可达90%以上
- 易于部署:单文件Python脚本,快速部署
- 低资源消耗:可在CPU环境下运行
- 多语言支持:提供Go和Python客户端调用示例
- 灵活扩展:可根据需求自定义模型和参数
技术架构
本项目采用以下技术栈构建:
- 后端框架:Flask - 轻量级Web框架,用于提供RESTful API接口
- 深度学习模型:Qwen2.5-VL-3B-Instruct - 阿里云开发的多模态预训练模型,支持图像理解和文本生成
- 图像处理:PIL (Pillow) - 用于图像解码和预处理
- 数据处理:PyTorch - 深度学习框架,用于模型加载和推理
- 网络通信:基于HTTP协议,支持JSON数据交换
工作原理
- 客户端将验证码图片编码为Base64格式
- 客户端发送POST请求到API服务端
- 服务端解码Base64图片,进行预处理
- 将处理后的图像输入到多模态模型中
- 模型识别验证码内容并返回文本结果
- 服务端对结果进行后处理(如过滤非字母数字字符)
- 返回JSON格式的识别结果给客户端
环境要求
- Python 3.8+
- Flask
- transformers
- torch
- pillow
- base64 (Python标准库)
- io (Python标准库)
- os (Python标准库)
- re (Python标准库)
安装部署
1. 安装依赖
pip install flask transformers torch pillow
2. 下载源码
将yzmApi.py文件保存到服务器上。
3. 启动服务
# 前台运行(调试模式)
python yzmApi.py
# 后台运行(生产模式)
nohup python yzmApi.py > yzm_api.log 2>&1 &
服务将在端口7783上启动,并监听所有IP地址(0.0.0.0)。
服务端代码展示
下面是服务端的Python源码实现:
from flask import Flask, request, jsonify
from transformers import AutoProcessor, AutoModelForVision2Seq
from PIL import Image
import torch
import base64
import io
import os
# ======================
# 全局加载模型(只加载一次)
# ======================
print("正在加载模型,请稍候...")
model_id = "Qwen/Qwen2.5-VL-3B-Instruct"
processor = AutoProcessor.from_pretrained(model_id, trust_remote_code=True)
model = AutoModelForVision2Seq.from_pretrained(
model_id,
device_map="cpu",
torch_dtype=torch.float32,
trust_remote_code=True,
low_cpu_mem_usage=True
)
print("模型加载完成!")
app = Flask(__name__)
@app.route('/predict', methods=['POST'])
def predict():
try:
# 1. 获取 JSON 数据
data = request.get_json()
if not data or 'image_base64' not in data:
return jsonify({"error": "缺少 image_base64 字段"}), 400
image_b64 = data['image_base64']
# 2. 解码 Base64 图片
try:
image_data = base64.b64decode(image_b64)
image = Image.open(io.BytesIO(image_data)).convert("RGB")
except Exception as e:
return jsonify({"error": f"图片解码失败: {str(e)}"}), 400
# 3. 构建输入
messages = [{
"role": "user",
"content": "<|image_pad|>识别图中的验证码内容,只输出结果,不要解释。"
}]
text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
inputs = processor(images=image, text=text, return_tensors="pt").to("cpu")
# 4. 推理
with torch.no_grad():
output = model.generate(
**inputs,
max_new_tokens=20,
do_sample=False,
pad_token_id=processor.tokenizer.pad_token_id,
eos_token_id=processor.tokenizer.eos_token_id
)
# 5. 只提取生成部分 + 取最后一行
input_len = inputs.input_ids.shape[1]
generated_tokens = output[0][input_len:]
raw_result = processor.decode(generated_tokens, skip_special_tokens=True)
result = raw_result.strip().split('\n')[-1].strip()
# 可选:只保留字母和数字(适合验证码)
import re
result = re.sub(r'[^A-Za-z0-9]', '', result)
return jsonify({"code": result})
except Exception as e:
return jsonify({"error": str(e)}), 500
if __name__ == '__main__':
# 监听所有 IP,端口 7783
app.run(host='0.0.0.0', port=7783, debug=False)
运行并后台执行:
#nohup python yzmApi.py > yzm_api.log 2>&1 &
这段代码的核心在于利用预训练模型处理图像到文本的任务。通过Flask框架搭建了一个简单的Web服务器,接收客户端传来的Base64编码图片数据,并返回验证码内容。就像变魔术一样,输入一张图片,输出验证码!
Go语言调用示例
既然有了服务端,那自然少不了客户端的支持。下面是使用Go语言调用上述API的示例代码:
package main
import (
"bytes"
"encoding/base64"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
)
func main() {
if len(os.Args) <= 1 {
log.Fatal("usage: <image name>")
}
imgData, err := os.ReadFile(os.Args[1])
if err != nil {
log.Fatal(err)
}
// 将图片编码为 base64
imageBase64Str := base64.StdEncoding.EncodeToString(imgData)
// 准备请求体
payload := []byte(`{"image_base64":"` + imageBase64Str + `"}`)
client := &http.Client{}
// 创建新的POST请求
req, err := http.NewRequest("POST", "http://您的服务器地址:7783/predict", bytes.NewBuffer(payload))
if err != nil {
fmt.Println("创建请求错误:", err)
return
}
req.Header.Set("Content-Type", "application/json")
// 发送请求
resp, err := client.Do(req)
if err != nil {
fmt.Println("发送请求错误:", err)
return
}
defer resp.Body.Close()
// 读取响应体
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("读取响应体错误:", err)
return
}
// 打印服务器返回的结果
fmt.Println("识别结果:", string(body))
}
这段代码就像是给服务端发送了一封信,里面附带了一张图片,然后静静地等待回信——即验证码的内容。
Python调用示例
当然,如果你更喜欢Python,下面是一个Python版本的调用示例:
import requests
import base64
# 读取图片并转为 base64
with open("d:/1.jpg", "rb") as f:
img_b64 = base64.b64encode(f.read()).decode('utf-8')
# 发送请求
resp = requests.post(
"http://您的服务器地址:7783/predict",
json={"image_base64": img_b64}
)
print()
print(resp.json()) # {"code": "hnjk"}
这简直就像是与朋友聊天时发送一张图片,并立即得到回复一样简单。
性能与优化
识别率
- 普通字母数字验证码:90-95%
- 简单干扰验证码:85-90%
- 复杂干扰验证码:70-80%
性能参数
- 首次加载模型时间:约1-3分钟(取决于网络和硬件)
- 单张验证码识别时间:约1-5秒(CPU环境)
- 内存占用:约3-4GB
优化建议
-
使用GPU:如需更高性能,可修改代码使用GPU加速
model = AutoModelForVision2Seq.from_pretrained( model_id, device_map="cuda", # 使用GPU torch_dtype=torch.float16, # 使用半精度浮点数 trust_remote_code=True ) -
模型缓存:确保模型只加载一次,避免重复加载
-
请求限制:在生产环境中建议添加请求频率限制
-
结果缓存:对于相同的验证码可以缓存结果,减少重复计算
常见问题
1. 如何提高识别率?
- 确保验证码图片清晰,无过度压缩
- 适当调整模型提示词,根据验证码特点优化指令
- 对于特殊验证码,可考虑图像预处理(如二值化、去噪等)
2. 服务启动慢怎么办?
- 模型加载是最耗时的步骤,只在启动时执行一次
- 生产环境建议使用后台运行方式,避免频繁重启
3. 可以识别哪些类型的验证码?
- 字母数字组合验证码
- 纯数字验证码
- 纯字母验证码
- 简单图形验证码
4. 服务占用内存过高怎么办?
- 可以尝试使用更小的模型
- 关闭不必要的进程释放内存
- 考虑使用量化技术减小模型大小
扩展与定制
自定义模型
如需使用其他模型,只需修改代码中的model_id:
# 使用其他预训练模型
model_id = "你的模型ID"
自定义提示词
可以根据验证码特点调整提示词:
messages = [{
"role": "user",
"content": "<|image_pad|>识别图中的验证码,这是一个字母数字组合验证码,只输出验证码内容,不要其他文字。"
}]
添加图像预处理
对于复杂验证码,可以添加预处理步骤:
from PIL import Image, ImageEnhance
# 图像预处理函数
def preprocess_image(image):
# 转为灰度图
image = image.convert('L')
# 提高对比度
enhancer = ImageEnhance.Contrast(image)
image = enhancer.enhance(2.0)
# 二值化处理
threshold = 128
image = image.point(lambda p: p > threshold and 255)
return image.convert("RGB")
# 在predict函数中使用
image = preprocess_image(image)
结语
最终,我们不仅成功地构建了一个验证码识别API,而且它的识别率也达到了90%,更重要的是,它是完全免费的!这意味着我们可以告别高昂的费用,享受技术带来的便利。
这个项目展示了如何利用开源的预训练模型解决实际工作中的问题,希望它能为你的自动化项目提供帮助。如果你有任何改进建议或问题,欢迎随时讨论和交流。
记住,有时候最好的解决方案就在你的身边,只需要一点点探索的精神!