近期在跟進(jìn)新項(xiàng)目的時(shí)候池颈,整體的業(yè)務(wù)線非常之長凡涩,會(huì)一直重復(fù)登錄退出不同賬號(hào)的這個(gè)流程搂鲫,所以想從登錄開始實(shí)現(xiàn)部分的自動(dòng)化。因?yàn)槭荁/S的架構(gòu)潜必,所以采用的是selenium的框架來實(shí)現(xiàn)靴姿。大致實(shí)現(xiàn)步驟如下:
1.環(huán)境準(zhǔn)備
2.驗(yàn)證碼爬取
3.識(shí)別方案選擇
4.圖像處理和識(shí)別
5.自動(dòng)化實(shí)現(xiàn)
一、環(huán)境準(zhǔn)備
系統(tǒng):macOS
軟件:Pycharm
語言:Python 2.7
瀏覽器:Chrome 70.0.35
依賴庫:selenium 3.141刮便、xlrd 1.1空猜、aip 1.0.0.5绽慈、pytesser恨旱、pytesseract 0.2.5、opencv-python 3.4.3坝疼、urllib3 1.24.1搜贤、Pillow-PIL 0.1
驅(qū)動(dòng)安裝與配置環(huán)境:
① 下載chromedriver:http://chromedriver.storage.googleapis.com/index.html(需代理)、http://npm.taobao.org/mirrors/chromedriver/(無需代理)
②具體瀏覽器與驅(qū)動(dòng)版本映射表可參考 https://blog.csdn.net/huilan_same/article/details/51896672 ,最新chrome 74版本---ChromeDriver v74.0.3729.6
③解壓后放置在/usr/local/bin/目錄下
④加入環(huán)境變量:export PATH=$PATH:/usr/local/bin/ChromeDriver
二钝凶、驗(yàn)證碼爬取
對(duì)于驗(yàn)證碼而言仪芒,目前各式網(wǎng)站出現(xiàn)的驗(yàn)證碼類型基本有:圖形驗(yàn)證碼(數(shù)字唁影、計(jì)算題、中文掂名、英文据沈、問答題)、滑塊驗(yàn)證碼饺蔑、語音驗(yàn)證碼锌介、圖片驗(yàn)證碼(正倒序、同類型)猾警。自身項(xiàng)目的驗(yàn)證碼為數(shù)字+英文圖形驗(yàn)證碼孔祸,針對(duì)這一塊的內(nèi)容,首先我們先來爬取一些驗(yàn)證碼到指定文件夾中发皿,來著重分析一下特點(diǎn)崔慧。代碼如下:
#-*- coding:utf-8 -*-
from selenium import webdriver
import time
import urllib
import os
import sys
req_url = "https://項(xiàng)目網(wǎng)址/#/"
def download_code(num):
for i in range(int(num)):
browser.refresh()
time.sleep(3)
# 尋找登錄按鈕,查找登錄classname
browser.find_elements_by_css_selector("[class='ant-btn logining-btn ant-btn-primary ant-btn-lg ant-btn-background-ghost']")[0].click()
time.sleep(3)
#獲取驗(yàn)證碼url鏈接
src=browser.find_elements_by_css_selector("[class='picturecode-img']")[0].get_attribute("src")
time.sleep(1)
local = '/Users/funny/PycharmProjects/auto_cloud/code_pic/' + str(i) + '.png'
print local
urllib.urlretrieve(src,local)
time.sleep(1)
if __name__=="__main__":
browser = webdriver.Chrome()
browser.get(req_url)
download_code(sys.argv[1])
browser.close()
大致講解一下上面出現(xiàn)的一些函數(shù)用法和實(shí)現(xiàn)過程中存在的問題穴墅。
1.使用classname定位惶室,運(yùn)行時(shí)報(bào)錯(cuò)
A:一般來說,使用classname來定位還是比較精準(zhǔn)的玄货,但是此項(xiàng)目的classname包含了多個(gè)tag拇涤,如上述的登錄按鈕class='ant-btn logining-btn ant-btn-primary ant-btn-lg ant-btn-background-ghost',這時(shí)候使用 find_elements_by_class_name方法定位誉结,會(huì)無法定位并報(bào)錯(cuò)鹅士。所以需要使用find_elements_by_css_selector,大家可以根據(jù)各自項(xiàng)目來選擇方法惩坑。
2.urllib.urlretrieve(src,local)
urllib模塊提供的urlretrieve()函數(shù)掉盅,urlretrieve()方法直接將遠(yuǎn)程數(shù)據(jù)下載到本地,傳入下載的鏈接以舒。
3.命令行獲取參數(shù)
為了指定我們想要下載的驗(yàn)證碼數(shù)量趾痘,要在源程序里面修改嗎?不用蔓钟。sys.argv[]是一個(gè)從程序外部獲取參數(shù)的橋梁永票,所獲得的是一個(gè)列表(list),文中的sys.argv[1]則是代表獲取列表中的下標(biāo)為1的內(nèi)容滥沫,在終端我們運(yùn)行的方法是:python catch_code.py 10 侣集,這樣sys.argv[1]取到的的值則為10,num的值亦為10,循環(huán)10次下載驗(yàn)證碼兰绣。
三世分、識(shí)別方案選擇
上節(jié)中爬取下來了100張驗(yàn)證碼,如下圖:
〕濉① pytesser
② pytesseract
∪倏帧③ 百度文字識(shí)別 AipOcr
為了對(duì)比這三者識(shí)別技術(shù)的識(shí)別率魁莉,對(duì)應(yīng)實(shí)現(xiàn)來展示效果,所以樣本選擇為0.png募胃、4.png旗唁、11.png(字母粘連、純字母痹束、字母+數(shù)字)
pytesser:谷歌OCR開源項(xiàng)目的一個(gè)模塊检疫,在python中導(dǎo)入這個(gè)模塊即可將圖片中的文字轉(zhuǎn)換成文本。pytesser下載鏈接:http://code.google.com/p/pytesser/ 祷嘶,實(shí)現(xiàn)代碼如下:
#-*- coding:utf-8 -*-
from PIL import Image
import pytesser.pytesser as pytesser
image = Image.open('code_pic/test_pic/0.png')
print pytesser.image_file_to_string('code_pic/test_pic/0.png')
print pytesser.image_to_string(image)
image_file_to_string()函數(shù)可以實(shí)現(xiàn)簡(jiǎn)單的英文字母識(shí)別屎媳,如果圖像是不相容的,會(huì)先轉(zhuǎn)換成兼容的格式论巍,然后再提取圖片中的文本信息烛谊。
image_to_string()函數(shù)亦可實(shí)現(xiàn)英文字母識(shí)別,讀取圖片時(shí)嘉汰,將內(nèi)存中的圖像文件保存為bmp丹禀,再使用tesseract處理。
pytesseract:Google的Tesseract-OCR引擎包裝器
print pytesseract.image_to_string(Image.open('code_pic/test_pic/11.png'),lang="eng")
順序識(shí)別0,4,11圖片后均無法識(shí)別結(jié)果双泪,識(shí)別概率為0%
AipOcr:一款百度提供的OCR識(shí)別服務(wù),支持多種圖片格式密似,接口免費(fèi)調(diào)用50000次/日焙矛,具體請(qǐng)參考官方文檔:https://ai.baidu.com/docs#/OCR-API/top ,在實(shí)現(xiàn)之前残腌,我們需要?jiǎng)?chuàng)建一款產(chǎn)品村斟,來獲得AppID、API Key抛猫、Secret Key的值蟆盹。如下圖:
from aip import AipOcr
# 你的 APPID AK SK
APP_ID = '1*****'
API_KEY = 'sHzo*******'
SECRET_KEY = 'V******'
client = AipOcr(APP_ID, API_KEY, SECRET_KEY)
# 讀取圖片
def get_file_content(filePath):
with open(filePath, 'rb') as fp:
return fp.read()
image = get_file_content('/Users/funny/PycharmProjects/auto_cloud/code_pic/test_pic/11.png')
# 調(diào)用通用文字識(shí)別, 圖片參數(shù)為本地圖片
result = client.general(image)
# 定義參數(shù)變量
options = {
# 定義圖像方向
'detect_direction' : 'true',
# 識(shí)別語言類型邑滨,默認(rèn)為'CHN_ENG'中英文混合
'language_type' : 'CHN_ENG',
}
# 調(diào)用通用文字識(shí)別接口
result = client.general(image,options)
print(result)
for word in result['words_result']:
print(word['words'])
順序識(shí)別0,4,11圖片后,圖片11識(shí)別出了一半,提取到了"2F"日缨,概率為16%
四、圖像處理和識(shí)別
在上節(jié)看來掖看,未經(jīng)過處理的圖片進(jìn)行識(shí)別匣距,識(shí)別概率都非常之低。所以我們換一個(gè)角度來思考哎壳,通過對(duì)圖片進(jìn)行一些處理毅待,使得特征更加明顯,再通過上述的三種識(shí)別庫來識(shí)別归榕,提高識(shí)別的概率尸红。步驟大致如下:1)灰度二值化 2)線降噪 3)開運(yùn)算
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矩陣點(diǎn)是反的
# 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)閉運(yùn)算
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)) # 定義結(jié)構(gòu)元素
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel) # 開運(yùn)算
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
cv2.imshow('閉運(yùn)算',closing)
cv2.waitKey(0)
cv2.destroyAllWindows()
處理的圖像如下:
五戴甩、自動(dòng)化實(shí)現(xiàn)
從上節(jié)的處理和識(shí)別中的總結(jié)內(nèi)容中,本項(xiàng)目我們選擇將AipOcr作為識(shí)別闪彼,若識(shí)別結(jié)果不正確(如粘連甜孤、噪點(diǎn)過多、部分裁剪圖片)畏腕,將獲取新的驗(yàn)證碼缴川,以此類推。將上述部分代碼封裝描馅,方便調(diào)用把夸,最終完整代碼如下:
#-*- coding:utf-8 -*-
from selenium import webdriver
from time import sleep
import xlrd
import os
import time
import urllib
import cv2
from aip import AipOcr
#define
req_url = "網(wǎng)址"
local = '/Users/funny/PycharmProjects/auto_cloud/code_pic/code.png'
APP_ID = '1****2'
API_KEY = 's*****'
SECRET_KEY = 'V******Hw'
xlsname="user_tab.xlsx"
#excel讀取
def Load_excel():
excel = xlrd.open_workbook(xlsname)
shxrange = range(excel.nsheets)
try:
sh = excel.sheet_by_name("Sheet1")
except:
print "no sheet in %s named Sheet1" % xlsname
nrows = sh.nrows
ncols = sh.ncols
#print "nrows %d, ncols %d" % (nrows, ncols)
# 獲取第一行第一列數(shù)據(jù)
cell_value = sh.cell_value(1, 1)
# print cell_value
row_list = []
# 獲取各行數(shù)據(jù)
for i in range(1, nrows):
row_data = sh.row_values(i)
row_list.append(row_data)
return row_list
def change_catch():
img = cv2.imread('/Users/funny/PycharmProjects/auto_cloud/code_pic/code.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 3)) # 定義結(jié)構(gòu)元素
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
cv2.imwrite('/Users/funny/PycharmProjects/auto_cloud/code_pic/code-close.png',closing)
def code_detect():
client = AipOcr(APP_ID, API_KEY, SECRET_KEY)
f=open('/Users/funny/PycharmProjects/auto_cloud/code_pic/code-close.png','rb')
image =f.read()
# 調(diào)用通用文字識(shí)別, 圖片參數(shù)為本地圖片
result = client.general(image)
# 定義參數(shù)變量
options = {
# 定義圖像方向
'detect_direction': 'true',
# 識(shí)別語言類型,默認(rèn)為'CHN_ENG'中英文混合
'language_type': 'CHN_ENG',
}
# 調(diào)用通用文字識(shí)別接口
result = client.general(image, options)
print result
print str(result['words_result'][0]['words'])
return str(result['words_result'][0]['words'])
if __name__ == '__main__':
flag=False
row_list=Load_excel()
print row_list
browser = webdriver.Chrome()
browser.get(req_url)
time.sleep(4)
#尋找登錄按鈕铭污,查找登錄classname
browser.find_elements_by_css_selector("[class='ant-btn logining-btn ant-btn-primary ant-btn-lg ant-btn-background-ghost']")[0].click()
time.sleep(2)
#獲取驗(yàn)證碼url
src = browser.find_elements_by_css_selector("[class='picturecode-img']")[0].get_attribute("src")
urllib.urlretrieve(src, local)
print "下載驗(yàn)證碼中恋日。膀篮。。"
change_catch()
word=code_detect()
print word
time.sleep(1)
browser.find_element_by_id("loginName").send_keys(row_list[0][1])
browser.find_element_by_id("password").send_keys(row_list[0][2])
browser.find_element_by_id("imgValidCode").send_keys(word)
browser.find_elements_by_css_selector("[class='ant-btn ant-btn-primary ant-btn-lg ant-btn-block']")[0].click()
time.sleep(1)
while browser.current_url=="網(wǎng)址":
time.sleep(2)
src = browser.find_elements_by_css_selector("[class='picturecode-img']")[0].get_attribute("src")
urllib.urlretrieve(src, local)
print "下載驗(yàn)證碼中岂膳。誓竿。。"
change_catch()
word = code_detect()
time.sleep(2)
browser.find_element_by_id("imgValidCode").send_keys(word)
browser.find_elements_by_css_selector("[class='ant-btn ant-btn-primary ant-btn-lg ant-btn-block']")[0].click()
print "登錄成功"
對(duì)于粘連性及部分被切割的驗(yàn)證碼谈截,還需要再研究一番~
另筷屡,因?yàn)轵?yàn)證碼識(shí)別率還不能達(dá)到100%,且后期可能因?yàn)榘姹镜脑螋の梗鼡Q不同方式的驗(yàn)證碼類型毙死,所以這里只是提供一個(gè)圖像預(yù)處理思路給到大家,實(shí)現(xiàn)登錄自動(dòng)化還有其他方式喻鳄,如白名單控制扼倘、關(guān)閉驗(yàn)證碼校驗(yàn)等。