最近爬取一个网站时,遇到了验证码的情况。验证码形式是计算题,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)