最近爬取一個網站時,遇到了驗證碼的情況。驗證碼形式是計算題,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