最近爬取一个网站时,遇到了验证码的情况。验证码形式是计算题,10以内的数字(可能有少量十以上),加减乘计算。

  开始懒得搞,第一批需要的数据量并不大,想着直接平台打码。

  原因是以前登录新浪微博的时候也是直接打码的,比较熟练,也简便。但打码成本比较高,后续需求量大,所以最好自己能识别。

  看了几篇识别验证码的文章,基本处理流程如下:

    1.去掉颜色;灰度处理,二值化等

    2.去掉干扰,降噪;噪点,线等的处理

    3.切割字符,单独识别

    4.训练字体

    5.自动识别

  我需要识别的验证码形式如下:

  

验证码识别java tessdata 验证码识别全流程实战_ide

  经过反复试验,定了以下几个步骤:

    1.去除干扰,将图片中的干扰点线等删除

    2.识别图片,使用pytesseract识别图片

    3.计算结果,使用识别出来的字符数字计算结果

  以上是代码的流程,实际操作中还需要训练字体,这里留在最后说明。

验证码识别java tessdata 验证码识别全流程实战_验证码_02

  使用这张图片为例,简要介绍一下流程:

  以上几个步骤的基本操作思想:

    1.去噪。通常的思想是根据像素点的相邻关系等去除,我在识别的过程中发现,这里的图片像素值比较单一,

                  噪点的颜色比数字的颜色要浅。简单打印一下RGB值,可以发现除了(255,255,255)表示的白色以外,大致颜色值有以下几种:

      (140,140,140)

      (112,112,112)

         (117,117,117)

                   于是,我通过判断像素点的RGB值,把以上几种点用白色替换(即去掉该点),示例图片转换后得到图像如下

         

验证码识别java tessdata 验证码识别全流程实战_验证码_03

                   可以看到这个结果,基本上就可以拿去识别了。这也是为什么我称这次验证码识别为简单粗暴。

    2.识别。识别使用的是tesseract直接识别,没什么好讲的,贴几个链接自己看。

             关于数据训练,也是直接照着教程做的,连文件名都没改,所幸过程中并未出错。

 

from pytesseract import image_to_string
im = Image.open("1_no_noise.png")
str_img = image_to_string(im, lang='eng', config='-psm 6')
print('识别为:%s' % str_img)

 

 

                   使用原装英文库识别结果如下:

识别为:7x3:?

              使用训练过的库识别:

from pytesseract import image_to_string
im = Image.open("1_no_noise.png")
str_img = image_to_string(im, lang='fontyp', config='-psm 70')
print('识别为:%s' % str_img)

# 识别为:7x3=?

                  ※在使用过程中,尝试修改config的配置,可以帮助更准确地识别。

    3.计算结果。计算结果就是拿识别出来的字符串,简单拆分,分析操作符,做出对应计算,因为识别的时候没有切图,所以直接切割字符串。

               在切割字符串之后的数字转换做了简单的矫正。

  整体代码如下:

 

# encoding=utf-8
__author__ = 'Masako'

from PIL import Image
from io import BytesIO
from pytesseract import image_to_string

NOISE_RGB_LIST = [117, 140, 112]    # 噪点像素值列表
OPERATE_LIST = ['+', 'x', 'X',  '-', '—']

# 去除噪点
def del_point(img):
    pix = img.load()
    width = img.size[0]
    height = img.size[1]
    for x in range(width):
        for y in range(height):
            r, g, b = pix[x, y]
            # print(r, g, b)
            if r in NOISE_RGB_LIST:
                 pix[x, y] = 255, 255, 255
    return img


# 数字识别
def data_ident(data_str):
    num = None
    if data_str == 'q':
        num = 9
    elif data_str == 'z' or data_str == 'Z':
        num = 2
    elif data_str == 'G':
        num = 6
    else:
        try:
            num = int(data_str)
        except Exception as e:
            print("can't identify:" + data_str)
    return num


# 计算结果
def deal_img_str(img_str):
    # str_list = img_str.split(' ')
    # print(str_list)
    calculate_result = None
    try:
        data_str = img_str[:img_str.rindex('=')]
    except Exception as e:
        print(img_str)
        return

    # print(data_str)
    for operate in OPERATE_LIST:
        if operate in data_str:     # 判断操作符
            data_list = data_str.split(operate)
            if len(data_list) == 2:  # 正确分割时的处理
                data_left = data_ident(data_list[0])
                data_right = data_ident(data_list[1])
                if data_left and data_right:
                    if operate == '+':
                        calculate_result = data_left + data_right
                    elif operate == 'x' or operate == 'X':
                        calculate_result = data_left * data_right
                    else:
                        calculate_result = data_left - data_right
        if calculate_result != None:
            break

    return calculate_result


def img_to_captcha_code(img_content):

    image_data = BytesIO(img_content)
    im = Image.open(image_data)
    im = del_point(im)

    str_img = image_to_string(im, lang='fontyp', config='-psm 70')

    result = deal_img_str(str_img)

    return result


if __name__ == "__main__":
    im = Image.open("a.jpg")
    im = del_point(im)
    im.save('a_no.jpg')
    str_img = image_to_string(im, lang='fontyp', config='-psm 70')
    print('识别为:%s' % str_img)