近期在跟進新項目的時候,總體的業務線很是之長,會一直重複登陸退出不一樣帳號的這個流程,因此想從登陸開始實現部分的自動化。由於是B/S的架構,因此採用的是selenium的框架來實現。大體實現步驟以下:css
1.環境準備html
2.驗證碼爬取python
3.識別方案選擇web
4.圖像處理和識別chrome
5.自動化實現npm
1、環境準備api
系統:macOS瀏覽器
軟件:Pycharm架構
語言:Python 2.7app
瀏覽器:Chrome 70.0.35
依賴庫:selenium 3.14一、xlrd 1.一、aip 1.0.0.五、pytesser、pytesseract 0.2.五、opencv-python 3.4.三、urllib3 1.24.一、Pillow-PIL 0.1
驅動安裝與配置環境:
① 下載chromedriver:http://chromedriver.storage.googleapis.com/index.html(需代理)、http://npm.taobao.org/mirrors/chromedriver/(無需代理)
②具體瀏覽器與驅動版本映射表可參考 http://www.javashuo.com/article/p-wlhinbws-eo.html ,最新chrome 74版本---ChromeDriver v74.0.3729.6
③解壓後放置在/usr/local/bin/目錄下
④加入環境變量:export PATH=$PATH:/usr/local/bin/ChromeDriver
2、驗證碼爬取
對於驗證碼而言,目前各式網站出現的驗證碼類型基本有:圖形驗證碼(數字、計算題、中文、英文、問答題)、滑塊驗證碼、語音驗證碼、圖片驗證碼(正倒序、同類型)。自身項目的驗證碼爲數字+英文圖形驗證碼,針對這一塊的內容,首先咱們先來爬取一些驗證碼到指定文件夾中,來着重分析一下特色。代碼以下:
1 #-*- coding:utf-8 -*- 2 from selenium import webdriver 3 import time 4 import urllib 5 import os 6 import sys 7 8 9 req_url = "https://項目網址/#/" 10 11 12 def download_code(num): 13 for i in range(int(num)): 14 browser.refresh() 15 time.sleep(3) 16 # 尋找登陸按鈕,查找登陸classname 17 browser.find_elements_by_css_selector("[class='ant-btn logining-btn ant-btn-primary ant-btn-lg ant-btn-background-ghost']")[0].click() 18 time.sleep(3) 19 #獲取驗證碼url連接 20 src=browser.find_elements_by_css_selector("[class='picturecode-img']")[0].get_attribute("src") 21 time.sleep(1) 22 23 local = '/Users/funny/PycharmProjects/auto_cloud/code_pic/' + str(i) + '.png' 24 print local 25 urllib.urlretrieve(src,local) 26 time.sleep(1) 27 28 if __name__=="__main__": 29 browser = webdriver.Chrome() 30 browser.get(req_url) 31 download_code(sys.argv[1]) 32 browser.close()
大體講解一下上面出現的一些函數用法和實現過程當中存在的問題。
1.使用classname定位,運行時報錯
A:通常來講,使用classname來定位仍是比較精準的,可是此項目的classname包含了多個tag,如上述的登陸按鈕class='ant-btn logining-btn ant-btn-primary ant-btn-lg ant-btn-background-ghost',這時候使用 find_elements_by_class_name方法定位,會沒法定位並報錯。因此須要使用find_elements_by_css_selector,你們能夠根據各自項目來選擇方法。
2.urllib.urlretrieve(src,local)
urllib模塊提供的urlretrieve()函數,urlretrieve()方法直接將遠程數據下載到本地,傳入下載的連接。
3.命令行獲取參數
爲了指定咱們想要下載的驗證碼數量,要在源程序裏面修改嗎?不用。sys.argv[]是一個從程序外部獲取參數的橋樑,所得到的是一個列表(list),文中的sys.argv[1]則是表明獲取列表中的下標爲1的內容,在終端咱們運行的方法是:python catch_code.py 10 ,這樣sys.argv[1]取到的的值則爲10,num的值亦爲10,循環10次下載驗證碼。
3、識別方案選擇
上節中爬取下來了100張驗證碼,以下圖:
基本特性是:橫向排列、數字與英文字母組合、字母間粘連佔比約30%、背景干擾較少。閱讀已有的一些ocr識別技術,基本有如下三個方向:
① pytesser
② pytesseract
③ 百度文字識別 AipOcr
爲了對比這三者識別技術的識別率,對應實現來展現效果,因此樣本選擇爲0.png、4.png、11.png(字母粘連、純字母、字母+數字)
pytesser:谷歌OCR開源項目的一個模塊,在python中導入這個模塊便可將圖片中的文字轉換成文本。pytesser下載連接:http://code.google.com/p/pytesser/ ,實現代碼以下:
1 #-*- coding:utf-8 -*- 2 from PIL import Image 3 import pytesser.pytesser as pytesser 4 5 image = Image.open('code_pic/test_pic/0.png') 6 print pytesser.image_file_to_string('code_pic/test_pic/0.png') 7 print pytesser.image_to_string(image)
image_file_to_string()函數能夠實現簡單的英文字母識別,若是圖像是不相容的,會先轉換成兼容的格式,而後再提取圖片中的文本信息。
image_to_string()函數亦可實現英文字母識別,讀取圖片時,將內存中的圖像文件保存爲bmp,再使用tesseract處理。
執行結果以下:
順序識別0,4,11圖片後均沒法識別結果,識別機率爲0%
pytesseract:Google的Tesseract-OCR引擎包裝器
1 print pytesseract.image_to_string(Image.open('code_pic/test_pic/11.png'),lang="eng")
順序識別0,4,11圖片後均沒法識別結果,識別機率爲0%
AipOcr:一款百度提供的OCR識別服務,支持多種圖片格式,接口免費調用50000次/日,具體請參考官方文檔:https://ai.baidu.com/docs#/OCR-API/top ,在實現以前,咱們須要建立一款產品,來得到AppID、API Key、Secret Key的值。以下圖:
獲取到以上三個參數後,繼續上代碼:
1 from aip import AipOcr 2 3 # 你的 APPID AK SK 4 APP_ID = '1*****' 5 API_KEY = 'sHzo*******' 6 SECRET_KEY = 'V******' 7 8 client = AipOcr(APP_ID, API_KEY, SECRET_KEY) 9 # 讀取圖片 10 def get_file_content(filePath): 11 with open(filePath, 'rb') as fp: 12 return fp.read() 13 14 image = get_file_content('/Users/funny/PycharmProjects/auto_cloud/code_pic/test_pic/11.png') 15 # 調用通用文字識別, 圖片參數爲本地圖片 16 result = client.general(image) 17 18 # 定義參數變量 19 options = { 20 # 定義圖像方向 21 'detect_direction' : 'true', 22 # 識別語言類型,默認爲'CHN_ENG'中英文混合 23 'language_type' : 'CHN_ENG', 24 25 26 } 27 28 # 調用通用文字識別接口 29 result = client.general(image,options) 30 print(result) 31 for word in result['words_result']: 32 print(word['words'])
順序識別0,4,11圖片後,圖片11識別出了一半,提取到了"2F",機率爲16%
4、圖像處理和識別
在上節看來,未通過處理的圖片進行識別,識別機率都很是之低。因此咱們換一個角度來思考,經過對圖片進行一些處理,使得特徵更加明顯,再經過上述的三種識別庫來識別,提升識別的機率。步驟大體以下:1)灰度二值化 2)線降噪 3)開運算
1)灰度二值化
im = cv2.imread('/Users/funny/PycharmProjects/auto_cloud/code_pic/0.png') im = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY) # 二值化 th1 = cv2.adaptiveThreshold(im, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 21, 1) edges = cv2.Canny(th1, 30, 70) cv2.imshow('二值化',th1) cv2.waitKey(0) cv2.destroyAllWindows()
處理的圖像以下:
2)線降噪
#二值化圖片,而且線降噪 img = cv2.imread('/Users/funny/PycharmProjects/auto_cloud/code_pic/11.png') img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #灰值化 h, w = img.shape[:2] # opencv矩陣點是反的 # img[1,2] 1:圖片的高度,2:圖片的寬度 for y in range(1, w - 1): for x in range(1, h - 1): count = 0 if img[x, y - 1] > 245: count = count + 1 if img[x, y + 1] > 245: count = count + 1 if img[x - 1, y] > 245: count = count + 1 if img[x + 1, y] > 245: count = count + 1 if count > 2: img[x, y] = 255 cv2.imshow('線降噪',img) cv2.waitKey(0) cv2.destroyAllWindows()
處理的圖像以下:
3)閉運算
img = cv2.imread('/Users/funny/PycharmProjects/auto_cloud/code_pic/11.png') img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 3)) # 定義結構元素 opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel) # 開運算 closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel) cv2.imshow('閉運算',closing) cv2.waitKey(0) cv2.destroyAllWindows()
處理的圖像以下:
圖像處理到如今基本上咱們已經將已有的背景干擾及色彩去除完畢,接下來咱們針對這些處理的圖像進行三種識別方案的識別,識別結果以下表:
咱們來分析一下這個表,在最開始的二值化,AipOcr至少識別出來了一些內容。縱觀三種圖像處理後的識別效果,明顯閉運算已經能識別出大體的內容了,圖片4.png三種識別方式都是能夠識別出來,對於0.png這種粘連字母,識別效果基本爲0%,而11.png「j」的底部表現不出來,因此識別不出來,但後面的內容亦識別成功。因此咱們能夠總結三點:①識別方式精準度 :AipOcr>pytesser>pytesseract。 ②處理後效果:閉運算>線降噪>二值化。③粘連性、帶噪點圖片識別效果很是差(當前準確值是基於我選取的樣本集)。
5、自動化實現
從上節的處理和識別中的總結內容中,本項目咱們選擇將AipOcr做爲識別,若識別結果不正確(如粘連、噪點過多、部分裁剪圖片),將獲取新的驗證碼,以此類推。將上述部分代碼封裝,方便調用,最終完整代碼以下:
1 #-*- coding:utf-8 -*- 2 from selenium import webdriver 3 from time import sleep 4 import xlrd 5 import os 6 import time 7 import urllib 8 import cv2 9 from aip import AipOcr 10 #define 11 req_url = "網址" 12 local = '/Users/funny/PycharmProjects/auto_cloud/code_pic/code.png' 13 APP_ID = '1****2' 14 API_KEY = 's*****' 15 SECRET_KEY = 'V******Hw' 16 xlsname="user_tab.xlsx" 17 18 #excel讀取 19 def Load_excel(): 20 excel = xlrd.open_workbook(xlsname) 21 shxrange = range(excel.nsheets) 22 try: 23 sh = excel.sheet_by_name("Sheet1") 24 except: 25 print "no sheet in %s named Sheet1" % xlsname 26 nrows = sh.nrows 27 ncols = sh.ncols 28 #print "nrows %d, ncols %d" % (nrows, ncols) 29 # 獲取第一行第一列數據 30 cell_value = sh.cell_value(1, 1) 31 # print cell_value 32 row_list = [] 33 # 獲取各行數據 34 for i in range(1, nrows): 35 row_data = sh.row_values(i) 36 row_list.append(row_data) 37 return row_list 38 39 def change_catch(): 40 img = cv2.imread('/Users/funny/PycharmProjects/auto_cloud/code_pic/code.png') 41 img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 42 kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 3)) # 定義結構元素 43 closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel) 44 cv2.imwrite('/Users/funny/PycharmProjects/auto_cloud/code_pic/code-close.png',closing) 45 46 def code_detect(): 47 client = AipOcr(APP_ID, API_KEY, SECRET_KEY) 48 f=open('/Users/funny/PycharmProjects/auto_cloud/code_pic/code-close.png','rb') 49 image =f.read() 50 # 調用通用文字識別, 圖片參數爲本地圖片 51 result = client.general(image) 52 # 定義參數變量 53 options = { 54 # 定義圖像方向 55 'detect_direction': 'true', 56 # 識別語言類型,默認爲'CHN_ENG'中英文混合 57 'language_type': 'CHN_ENG', 58 } 59 # 調用通用文字識別接口 60 result = client.general(image, options) 61 print result 62 print str(result['words_result'][0]['words']) 63 return str(result['words_result'][0]['words']) 64 65 66 67 68 69 70 if __name__ == '__main__': 71 72 flag=False 73 row_list=Load_excel() 74 print row_list 75 browser = webdriver.Chrome() 76 browser.get(req_url) 77 time.sleep(4) 78 #尋找登陸按鈕,查找登陸classname 79 browser.find_elements_by_css_selector("[class='ant-btn logining-btn ant-btn-primary ant-btn-lg ant-btn-background-ghost']")[0].click() 80 time.sleep(2) 81 #獲取驗證碼url 82 src = browser.find_elements_by_css_selector("[class='picturecode-img']")[0].get_attribute("src") 83 urllib.urlretrieve(src, local) 84 print "下載驗證碼中。。。" 85 change_catch() 86 word=code_detect() 87 print word 88 time.sleep(1) 89 browser.find_element_by_id("loginName").send_keys(row_list[0][1]) 90 browser.find_element_by_id("password").send_keys(row_list[0][2]) 91 browser.find_element_by_id("imgValidCode").send_keys(word) 92 browser.find_elements_by_css_selector("[class='ant-btn ant-btn-primary ant-btn-lg ant-btn-block']")[0].click() 93 time.sleep(1) 94 95 while browser.current_url=="網址": 96 time.sleep(2) 97 src = browser.find_elements_by_css_selector("[class='picturecode-img']")[0].get_attribute("src") 98 urllib.urlretrieve(src, local) 99 print "下載驗證碼中。。。" 100 change_catch() 101 word = code_detect() 102 time.sleep(2) 103 browser.find_element_by_id("imgValidCode").send_keys(word) 104 browser.find_elements_by_css_selector("[class='ant-btn ant-btn-primary ant-btn-lg ant-btn-block']")[0].click() 105 106 print "登陸成功"
對於粘連性及部分被切割的驗證碼,還須要再研究一番~
另,由於驗證碼識別率還不能達到100%,且後期可能由於版本迭代的緣由,更換不一樣方式的驗證碼類型,因此這裏只是提供一個圖像預處理思路給到你們,實現登陸自動化還有其餘方式,如白名單控制、關閉驗證碼校驗等。