python+tesseract 训练和破解验证码

python+tesseract 训练和破解验证码

本文介绍了一种高效破解验证码的方法,主要针对彩色背景,包含数字和英文字母,有干扰线的简单验证码。

一、使用技术

  1. window 10
  2. python 3.6
  3. tesseract-ocr.exe
  4. jTessBoxEditor-1.8.0 #用来完成tesseract字库的训练
  5. requests, pytesseract, PIL包

二、流程介绍

  1. 环境配置
  2. 验证码图片下载
  3. 验证码图片处理
  4. 基于验证码中字符的tesseract字库训练
  5. 验证码破解

三、环境配置

  1. python 3.6的安装及配置

入门python第一步就是这个,相信大多数人都会。具体方法可以参考blog.csdn.net/nerissa_l


2. tesseract-ocr的安装和配置

Tesseract是一个由HP公司开发(后由Google接手)的开源的OCR(Optical Character Recognition,光学字符识别)引擎,可以识别多种格式的图像文件并将其转换成文本,目前已支持多种语言(包括中文)。

tesseract-ocr的具体安装和环境变量配置方法可以参考我的CSDN博客:

用于图片文本识别的Tesseract-OCR的安装说明(windows10) - CSDN博客blog.csdn.net图标


3. jTessBoxEditor-1.8.0的安装和使用

jTessBoxEditor是训练tesseract词库的一款工具,可以直接从官网下载压缩包,下载地址Browse /jTessBoxEditor at SourceForge.net。解压后,可直接使用。但是这个工具是用java开发的,需要jre7以上的版本支持。

此外, jTessBoxEditor解压包中自带了一个tesseract-ocr文件包。我们需要使用第二步中自己安装tesseract-ocr生成的文件覆盖掉jTessBoxEditor\tesseract-ocr\中的文件。


4. urllib, pytesseract, PIL包的安装

urllib包是python 3.6 自带的, 这里主要是使用urllib.request.urlretrieve 函数来从网页上下载图片到本地。

pytesseract是python操作tesseract-ocr的一个api。PIL是python中图片处理使用的包。这两个包可以直接通过pip install 进行安装。

四、验证码图片下载

验证码图片我是从蘑菇ip代理网站登录页面下载下来的。URL为moguproxy.com/proxy/val。其中,最后面是一个时间戳。通过改变这个时间戳的值,生成300个url,向它们发送请求,获取验证码图片。

具体代码如下:

import random
from urllib.request import urlretrieve
import os
#下载300张验证码图片
url = 'http://www.moguproxy.com/proxy/validateCode/createCode?time={}'
path = os.path.dirname(__file__) + '/origin_imgs/' #将下载的图片保存到当前目录下的origin_imgs文件夹中

for i in range(1531878604000,1531878604300):
    urlretrieve(url.format(i), path + str(i)[-3:] + '.jpg')
    print('成功下载 {} 张图片'.format(str(i)[-3:]))

下载后的结果如下图所示:

图1. 验证码图片集合

五、验证码图片处理

由上图可以看出,这种验证码图片背景为彩色,包含大小写英文字母和数字,每个字符的颜色都不相同,少数字符还紧贴着彼此,此外图片中还存在着多条不同颜色的干扰线。直接使用pytesseract对它们进行识别,识别成功率是很低的。所以需要对图片进行一定的预处理。

这里我们通过颜色提取的方法,一步实现了抛去背景和干扰线,字符分离的目的。具体预处理过程见代码:

from PIL import Image
import os


path = os.path.dirname(__file__)
origin_path = path + '/origin_imgs/'
new_path = path + '/clean_imgs/'   #用来存放处理好的图片

#从100张图片中提取出字符样本
for image in os.listdir(origin_path)[:100]: 
    im = Image.open(origin_path+image)    
    width, height = im.size
    
    #获取图片中的颜色,返回列表[(counts, color)...]
    color_info = im.getcolors(width*height)
    #按照计数从大到小排列颜色,那么颜色计数最多的应该是背景,接下来排名2到6的则对应5个字符。
    sort_color = sorted(color_info, key=lambda x: x[0], reverse=True)    

    #根据颜色,提取出每一个字符,重新放置到一个新建的白色背景image对象上。每个image只放一个字符。
    char_dict = {}
    for i in range(1, 6):
        im2 = Image.new('RGB', im.size, (255, 255, 255))
        for x in range(im.size[0]):
            for y in range(im.size[1]):
                if im.getpixel((x, y)) == sort_color[i][1]:
                    im2.putpixel((x, y), (0, 0, 0))  
                else:
                    im2.putpixel((x, y), (255, 255, 255))
        im2.save(new_path + str(i)+'-'+ image.replace('jpg','tif'))  
    print('成功处理图片{}'.format(image))    

处理后的结果如下图所示:

图2. 处理后的验证码图片

此时如果用pytesseract对图2中所示的图片进行识别:

for image in os.listdir(new_path): 
    im2 = Image.open(new_path+image)
    char = pytesseract.image_to_string(im2, config='--psm 10')  # psm 10表示将图片识别成单个字符。
    print(char)

大部分的字符都可以被正确识别,但是还是有许多不足,例如,不能识别a, b, d, p,q,会把o和O识别成0, z和Z识别成2。

针对这种情况,只能使用jTessBoxEditor来手动建立tesseract的训练集,将不能正确识别的字符添加进去。

六、使用jTessBoxEditor建立训练集

因为所有字符集合其实是26个小写字母+26个大写字母+0到9数字,共62个字符。且每个字符的书写都是一样的,所以从处理后的图片中每个字符挑出一张图片,放入char_imgs文件夹中。

  1. 合并char_imgs中的图片

打开jTessBoxEditor, 点击Tools,选择Merge TIFF, 进入char_imgs目录下,全选所有图片,点击打开。然后将merge(合并)后的文件保存在同目录下,名称为mjchar.tif:


2. 生成mjchar.box文件

打开cmd, 切换到char_imgs目录下,执行tesseract命令:

tesseract mjchar.tif mjchar -psm 10 batch.nochop makebox

运行结果如下,同时char_imgs目录下生成了一个mjchar.box文件。


3. 校正字符集

用 jTessBoxEditor打开生成的图片集 mjchar.tif ,注意 mjchar.tif 与对应的box文件一定要处于同一个文件夹下,然后就可以开始调整了(记得要翻页),调整完之后保存。


4. 训练

首先在char_imgs目录下新建一个名字为“font_properties”的文本文件,并且输入文本 normal 0 0 0 0 0,表示非斜体,粗体的一般字体。

执行 tesseract mjchar.tif mjchar nobatch box.train 进行测试训练。

执行 unicharset_extractor mjchar.box, char_imgs目录下生成一个名为unicharset的文件。

执行 shapeclustering -F font_properties.txt -U unicharset mjchar.tr, char_imgs目录下生成一个名为mjchar.tr的文件

执行 mftraining -F font_properties.txt -U unicharset -O unicharset mjchar.tr

执行 cntraining mjchar.tr

此时char_imgs目录下生成了5个文件:

在这五个文件前加上normal.进行重命名:

执行 combine_tessdata normal, 合并五个文件,此时目录下的normal.traineddata 就是训练好的字库文件。

把normal.traineddata 复制到Tesseract-OCRt程序目录下的“tessdata”目录:

至此,词库训练就完成了。接下来使用pytesseract再去识别处理好的验证码,语言选择normal(即刚训练好的词库),识别成功率接近百分百。

char = pytesseract.image_to_string(im2, lang='normal',config='--psm 10')


七、在线验证码破解完整代码

import requests
from PIL import Image
import pytesseract
from io import BytesIO

headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'}
captcha_url = 'http://www.moguproxy.com/proxy/validateCode/createCode?time={} '.format(int(time.time() * 1000))
r = requests.get(captcha_url, headers=headers) #请求验证码图片链接
im = Image.open(BytesIO(r.content))   #直接读取bytes数据,生成图片对象

width, height = im.size
#获取图片中的颜色,返回列表[(counts, color)...]
color_info = im.getcolors(width*height)
sort_color = sorted(color_info, key=lambda x: x[0], reverse=True)
#将背景全部改为白色, 提取出字,每张图片一个字
char_dict = {}
for i in range(1, 6):
    start_x = 0
    im2 = Image.new('RGB', im.size, (255, 255, 255))
    for x in range(im.size[0]):
        for y in range(im.size[1]):
            if im.getpixel((x, y)) == sort_color[i][1]:
                im2.putpixel((x, y), (0, 0, 0))
                if not start_x:
                    start_x = x  #标记每个字符的起始位置,用于最后字符串的排序
            else:
                im2.putpixel((x, y), (255, 255, 255))
    char = pytesseract.image_to_string(im2, lang='normal',config='--psm 10')
    char_dict[start_x] = char
code = ''.join([item[1] for item in sorted(char_dict.items(), key=lambda i:i[0])])
print(code)


八、结语

以上就是使用python+tesseract识别彩色字母数字验证码的全部过程。通过对验证码的处理(根据颜色计数提取字符)和tesseract词库的训练,基本可以正确识别全部英文字母和数字。这种方法对于识别类似情况的中文验证码也是可以的。

自己摸着石头一步一步实现了整个过程,记录下来加深印象,也希望可以帮助到有需要的朋友。

谢谢!

编辑于 2018-07-20