【Python+selenium】帶圖片驗(yàn)證碼的登錄自動(dòng)化實(shí)戰(zhàn)

近期在跟進(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)證碼,如下圖:

image.png
基本特性是:橫向排列缀辩、數(shù)字與英文字母組合臭埋、字母間粘連占比約30%踪央、背景干擾較少。閱讀已有的一些ocr識(shí)別技術(shù)瓢阴,基本有以下三個(gè)方向:
   〕濉① 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處理。

  執(zhí)行結(jié)果如下:
image.png
順序識(shí)別0,4,11圖片后均無法識(shí)別結(jié)果鞋怀,識(shí)別概率為0%
  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的值蟆盹。如下圖:

image.png
獲取到以上三個(gè)參數(shù)后,繼續(xù)上代碼:

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()

處理的圖像如下:
image.png

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()

處理的圖像如下:
image.png

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()

處理的圖像如下:

image.png
圖像處理到現(xiàn)在基本上我們已經(jīng)將已有的背景干擾及色彩去除完畢外里,接下來我們針對(duì)這些處理的圖像進(jìn)行三種識(shí)別方案的識(shí)別,識(shí)別結(jié)果如下表:
image.png
我們來分析一下這個(gè)表特石,在最開始的二值化盅蝗,AipOcr至少識(shí)別出來了一些內(nèi)容∧氛海縱觀三種圖像處理后的識(shí)別效果墩莫,明顯閉運(yùn)算已經(jīng)能識(shí)別出大致的內(nèi)容了,圖片4.png三種識(shí)別方式都是可以識(shí)別出來逞敷,對(duì)于0.png這種粘連字母狂秦,識(shí)別效果基本為0%,而11.png“j”的底部表現(xiàn)不出來推捐,所以識(shí)別不出來裂问,但后面的內(nèi)容亦識(shí)別成功。所以我們可以總結(jié)三點(diǎn):①識(shí)別方式精準(zhǔn)度 :AipOcr>pytesser>pytesseract牛柒。 ②處理后效果:閉運(yùn)算>線降噪>二值化愕秫。③粘連性、帶噪點(diǎn)圖片識(shí)別效果非常差(當(dāng)前準(zhǔn)確值是基于我選取的樣本集)焰络。
五戴甩、自動(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)等。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末诽表,一起剝皮案震驚了整個(gè)濱河市唉锌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌竿奏,老刑警劉巖袄简,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異泛啸,居然都是意外死亡绿语,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門候址,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吕粹,“玉大人,你說我怎么就攤上這事岗仑∑ジ” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵荠雕,是天一觀的道長稳其。 經(jīng)常有香客問我,道長炸卑,這世上最難降的妖魔是什么既鞠? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮盖文,結(jié)果婚禮上嘱蛋,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好洒敏,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布龄恋。 她就那樣靜靜地躺著,像睡著了一般桐玻。 火紅的嫁衣襯著肌膚如雪篙挽。 梳的紋絲不亂的頭發(fā)上荆萤,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天镊靴,我揣著相機(jī)與錄音,去河邊找鬼链韭。 笑死偏竟,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的敞峭。 我是一名探鬼主播踊谋,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼旋讹!你這毒婦竟也來了殖蚕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤沉迹,失蹤者是張志新(化名)和其女友劉穎睦疫,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鞭呕,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蛤育,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了葫松。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瓦糕。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖腋么,靈堂內(nèi)的尸體忽然破棺而出咕娄,到底是詐尸還是另有隱情,我是刑警寧澤珊擂,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布圣勒,位于F島的核電站,受9級(jí)特大地震影響未玻,放射性物質(zhì)發(fā)生泄漏灾而。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一扳剿、第九天 我趴在偏房一處隱蔽的房頂上張望旁趟。 院中可真熱鬧,春花似錦、人聲如沸锡搜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽耕餐。三九已至凡傅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間肠缔,已是汗流浹背夏跷。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留明未,地道東北人槽华。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像趟妥,于是被迫代替她去往敵國和親猫态。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容