最近爬取一个网站时,遇到了验证码的情况。验证码形式是计算题,10以内的数字(可能有少量十以上),加减乘计算。
开始懒得搞,第一批需要的数据量并不大,想着直接平台打码。
原因是以前登录新浪微博的时候也是直接打码的,比较熟练,也简便。但打码成本比较高,后续需求量大,所以最好自己能识别。
看了几篇识别验证码的文章,基本处理流程如下:
1.去掉颜色;灰度处理,二值化等
2.去掉干扰,降噪;噪点,线等的处理
3.切割字符,单独识别
4.训练字体
5.自动识别
我需要识别的验证码形式如下:
经过反复试验,定了以下几个步骤:
1.去除干扰,将图片中的干扰点线等删除
2.识别图片,使用pytesseract识别图片
3.计算结果,使用识别出来的字符数字计算结果
以上是代码的流程,实际操作中还需要训练字体,这里留在最后说明。
使用这张图片为例,简要介绍一下流程:
以上几个步骤的基本操作思想:
1.去噪。通常的思想是根据像素点的相邻关系等去除,我在识别的过程中发现,这里的图片像素值比较单一,
噪点的颜色比数字的颜色要浅。简单打印一下RGB值,可以发现除了(255,255,255)表示的白色以外,大致颜色值有以下几种:
(140,140,140)
(112,112,112)
(117,117,117)
于是,我通过判断像素点的RGB值,把以上几种点用白色替换(即去掉该点),示例图片转换后得到图像如下
可以看到这个结果,基本上就可以拿去识别了。这也是为什么我称这次验证码识别为简单粗暴。
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)
参考文章链接:
https://www.cnblogs.com/qqandfqr/p/7866650.html
http://www.cnblogs.com/cnlian/p/5765871.html