巧用 selenium 解決驗(yàn)證碼,模擬登陸某流行網(wǎng)站

一、前言
1. 介紹
驗(yàn)證碼多種多樣,有圖形文字的、有模擬點(diǎn)選的、有拖動滑動的,但其實(shí)歸根結(jié)底都需要人來對某種情形做一些判斷,然后把結(jié)果返回并提交。
如果此時提交的驗(yàn)證碼結(jié)果是正確的,并且通過了一些驗(yàn)證碼的檢測,就能成功突破這個驗(yàn)證碼了。既然驗(yàn)證碼就是讓人來識別的,那么機(jī)器怎么辦呢?
如果我們也不會什么算法,怎么去解這些驗(yàn)證碼呢?此時我們需要利用可以幫助我們來識別驗(yàn)證碼的工具或平臺就,讓工具或平臺把驗(yàn)證碼識別的結(jié)果返回給我們,我們拿著結(jié)果提交,那不就好了嗎?
有專門的打碼平臺幫助我們來識別各種各樣的驗(yàn)證碼,平臺內(nèi)部對算法和人力做了集成,可以 7x24 小時來識別各種驗(yàn)證碼,包括識別圖形、坐標(biāo)點(diǎn)、缺口等各種驗(yàn)證碼,返回對應(yīng)的結(jié)果或坐標(biāo),正好可以解決我們的問題,比如超級鷹。


B站最新登錄驗(yàn)證為點(diǎn)選驗(yàn)證碼,以模擬登錄 B 站來熟悉 selenium 庫的使用和打碼平臺的使用方法。

這個驗(yàn)證碼上面顯示了幾個漢字,同時在圖中也顯示了幾個漢字,我們需要按照順序依次點(diǎn)擊漢字在圖中的位置,點(diǎn)擊完成之后確認(rèn)提交,即可完成驗(yàn)證。這種驗(yàn)證碼如果我們沒有任何圖像識別算法基礎(chǔ)的話,是很難去識別的,所以這里我們可以借助打碼平臺來幫助我們識別漢字的位置。
2. 準(zhǔn)備工作
本文用到的 Python 庫是 Selenium,使用的瀏覽器為 Chrome,確保已經(jīng)正確安裝好 Selenium 庫、Chrome 瀏覽器,并配置好 ChromeDriver,
使用的打碼平臺是超級鷹,鏈接為:https://www.chaojiying.com/,在使用之前請讀者自行注冊賬號并獲取一些題分供測試,另外還可以了解平臺可識別的驗(yàn)證碼的類別。
打碼平臺能提供的服務(wù)種類一般都非常廣泛,可識別的驗(yàn)證碼類型也非常多,其中就包括點(diǎn)觸驗(yàn)證碼。
超級鷹平臺同樣支持簡單的圖形驗(yàn)證碼識別,超級鷹平臺提供了如下一些服務(wù):
英文數(shù)字:提供最多 20 位英文數(shù)字的混合識別; 中文漢字:提供最多 7 個漢字的識別; 純英文:提供最多 12 位的英文識別; 純數(shù)字:提供最多 11 位的數(shù)字識別; 任意特殊字符:提供不定長漢字英文數(shù)字、拼音首字母、計算題、成語混合、集裝箱號等字符的識別; 坐標(biāo)選擇識別:如復(fù)雜計算題、選擇題四選一、問答題、點(diǎn)擊相同的字、物品、動物等返回多個坐標(biāo)的識別;
本例需要處理的就是坐標(biāo)多選識別的情況。我們先將驗(yàn)證碼圖片提交給平臺,平臺會返回識別結(jié)果在圖片中的坐標(biāo)位置,然后我們再解析坐標(biāo)模擬點(diǎn)擊,下面我們就用程序來實(shí)現(xiàn)。
二、實(shí)現(xiàn)思路

三、Python代碼實(shí)現(xiàn)
1. 獲取API
通過官方網(wǎng)站下載對應(yīng)的 Python API,鏈接為:https://www.chaojiying.com/api-14.html。API 是 Python 2 版本的,用 requests 庫來實(shí)現(xiàn)的。我們可以簡單更改幾個地方,即可將其修改為 Python 3 版本。
修改之后的 API 如下所示:
#?-*-?coding:?UTF-8?-*-
#?Chaojiying_Client類??用于提交要識別的圖片??返回json結(jié)果
class?Chaojiying_Client(object):
????def?__init__(self,?username,?password,?soft_id):
????????self.username?=?username
????????password?=?password.encode('utf-8')
????????self.password?=?md5(password).hexdigest()
????????self.soft_id?=?soft_id
????????self.base_params?=?{
????????????'user':?self.username,
????????????'pass2':?self.password,
????????????'softid':?self.soft_id,
????????}
????????self.headers?=?{
????????????'Connection':?'Keep-Alive',
????????????'User-Agent':?"Mozilla/5.0?(Windows?NT?6.1;?WOW64)?AppleWebKit/537.1?(KHTML,?like?Gecko)?Chrome/22.0.1207.1?Safari/537.1",
????????}
????def?PostPic(self,?im,?codetype):
????????"""
????????im:?圖片字節(jié)
????????codetype:?題目類型?參考?http://www.chaojiying.com/price.html
????????"""
????????params?=?{
????????????'codetype':?codetype,
????????}
????????params.update(self.base_params)
????????files?=?{'userfile':?('ccc.jpg',?im)}
????????r?=?requests.post('http://upload.chaojiying.net/Upload/Processing.php',?data=params,?files=files,
??????????????????????????headers=self.headers)
????????logging.info(r.json())
????????return?r.json()
????def?ReportError(self,?im_id):
????????"""
????????im_id:報錯題目的圖片ID
????????"""
????????params?=?{
????????????'id':?im_id,
????????}
????????params.update(self.base_params)
????????r?=?requests.post('http://upload.chaojiying.net/Upload/ReportError.php',?data=params,?headers=self.headers)
????????logging.info(r.json())
????????return?r.json()
這里定義了一個 Chaojiying 類,其構(gòu)造函數(shù)接收三個參數(shù),分別是超級鷹的用戶名、密碼以及軟件 ID,保存以備使用。
最重要的一個方法叫作 post_pic,它需要傳入圖片對象和驗(yàn)證碼類型的代號。該方法會將圖片對象和相關(guān)信息發(fā)給超級鷹的后臺進(jìn)行識別,然后將識別成功的 json 返回。
另一個方法叫作 report_error,它是發(fā)生錯誤時的回調(diào)。如果驗(yàn)證碼識別錯誤,調(diào)用此方法會返回相應(yīng)的題分。
2. 分析B站登錄界面

進(jìn)入登陸界面,定位用戶名和密碼對應(yīng)的標(biāo)簽,模擬輸入我們的賬號和密碼,點(diǎn)擊登錄,此時頁面會彈出驗(yàn)證碼。

對進(jìn)行驗(yàn)證碼的獲取,然后提交給「超級鷹」進(jìn)行識別,接收到漢字的坐標(biāo)后,處理數(shù)據(jù),然后用動作鏈進(jìn)行模擬點(diǎn)擊操作,最后定位點(diǎn)擊確認(rèn),實(shí)現(xiàn)成功登錄 B 站。
3. 具體實(shí)現(xiàn)
導(dǎo)入用的庫
from?selenium?import?webdriver
from?time?import?sleep
from?PIL?import?Image
from?selenium.webdriver?import?ActionChains
import?random
import?requests
from?hashlib?import?md5
import?logging
日志輸出和 webdriver.Chrome() 配置
#?日志輸出配置
logging.basicConfig(level=logging.INFO,?format='%(asctime)s?-?%(levelname)s:?%(message)s')
#?初始化一個webdriver.Chrome()對象
chrome_driver?=?r'D:\python\pycharm2020\chromedriver.exe'
options?=?webdriver.ChromeOptions()
#?關(guān)閉左上方?Chrome?正受到自動測試軟件的控制的提示
options.add_experimental_option('useAutomationExtension',?False)
options.add_experimental_option("excludeSwitches",?['enable-automation'])
browser?=?webdriver.Chrome(options=options,?executable_path=chrome_driver)
用 selenium 打開登錄頁面,進(jìn)行模擬登錄
#?登錄函數(shù)???訪問頁面->輸出賬號、密碼->點(diǎn)擊登錄
def?login():
????browser.get('https://passport.bilibili.com/login')
????browser.maximize_window()
????#?ID定位用戶名,密碼輸入框
????username?=?browser.find_element_by_id('login-username')
????password?=?browser.find_element_by_id('login-passwd')
????username.send_keys('your?username')
????password.send_keys('your?password')
????#?Xpath定位登錄按鈕并點(diǎn)擊
????browser.find_element_by_xpath('//*[@id="geetest-wrap"]/div/div[5]/a[1]').click()
????sleep(random.random()*3)
將當(dāng)前頁面進(jìn)行截圖并保存下來,裁剪出驗(yàn)證碼區(qū)域
def?save_img():
????# save_screenshot:將當(dāng)前頁面進(jìn)行截圖并保存下來
????browser.save_screenshot('page.png')
????#?Xpath定位驗(yàn)證碼圖片的位置
????code_img_ele?=?browser.find_element_by_xpath('/html/body/div[2]/div[2]/div[6]/div/div')
????location?=?code_img_ele.location??#?驗(yàn)證碼左上角的坐標(biāo)x,y
????size?=?code_img_ele.size??#?驗(yàn)證碼圖片對應(yīng)的長和寬
????#?得到左上角和右下角的坐標(biāo)
????rangle?=?(
????????int(location['x']?*?1.25),?int(location['y']?*?1.25),?int((location['x']?+?size['width'])?*?1.25),
????????int((location['y']?+?size['height'])?*?1.25)
????)
????image1?=?Image.open('./page.png')
????#?code_img_name?=?'./code.png'
????#?crop根據(jù)rangle元組內(nèi)的坐標(biāo)進(jìn)行裁剪?裁剪出驗(yàn)證碼區(qū)域
????frame?=?image1.crop(rangle)
????frame.save('./code.png')
????return?code_img_ele
將點(diǎn)擊登錄后的頁面進(jìn)行截圖,然后定位到驗(yàn)證碼的位置,通過location()方法獲取驗(yàn)證碼左上角的坐標(biāo), size() 獲取驗(yàn)證碼的寬和高,左上角坐標(biāo)加上寬和高就是驗(yàn)證碼右下角的坐標(biāo)。獲取坐標(biāo)后就可以用 crop() 方法來進(jìn)行裁剪,然后將裁剪到的驗(yàn)證碼圖片保存。
縮小圖片
def?narrow_img():
????#?縮小圖片
????code?=?Image.open('./code.png')
????small_img?=?code.resize((169,?216))
????small_img.save('./small_img.png')
????print(code.size,?small_img.size)
此時雖然獲取了驗(yàn)證碼圖片,但是還不能直接提交給超級鷹。因?yàn)槌夲椬R別的驗(yàn)證碼圖片的寬和高有限制,最好不超過460px*310px。但是截取到的驗(yàn)證碼圖片寬高為338px*432px,這時就要先將圖片縮小一倍再提交即可,等到收到坐標(biāo)數(shù)據(jù)再將坐標(biāo)乘2。
將驗(yàn)證碼提交給超級鷹進(jìn)行識別
def?submit_img():
????#?將驗(yàn)證碼提交給超級鷹進(jìn)行識別
????#?用戶中心->軟件ID?生成你的軟件ID->替換掉96001??綁定微信可以得到1000積分?免費(fèi)使用
????chaojiying?=?Chaojiying_Client('你的用戶名',?'密碼',?'生成的軟件ID')
????with?open('./small_img.png',?'rb')?as?f:
????????im?=?f.read()
????#?本地圖片文件路徑?來替換?a.jpg?有時WIN系統(tǒng)須要//
????result?=?chaojiying.PostPic(im,?9004)['pic_str']
????logging.info(result)
????return?result
調(diào)用已獲取的API,傳入?yún)?shù):'你的用戶名', '密碼','生成的軟件ID'
解析返回的漢字的坐標(biāo)點(diǎn)的結(jié)果
def?parse_data(result):
????node_list?=?[]??#?存儲即將被點(diǎn)擊的點(diǎn)的坐標(biāo)??[[x1,y1],[x2,y2]]
????print(result)
????if?'|'?in?result:
????????nums?=?result.split('|')
????????for?i?in?range(len(nums)):
????????????x?=?int(nums[i].split(',')[0])
????????????y?=?int(nums[i].split(',')[1])
????????????xy_list?=?[x,?y]
????????????node_list.append(xy_list)
????else:
????????print(result.split(',')[0])
????????print(result.split(',')[1])
????????x?=?int(result.split(',')[0])
????????y?=?int(result.split(',')[1])
????????xy_list?=?[x,y]
????????node_list.append(xy_list)
????return?node_list
超級鷹識別返回的結(jié)果的數(shù)據(jù)格式為:98,136|87,77 我們可以將數(shù)據(jù)以' | '進(jìn)行分割,保存到列表中,再以逗號分割將x,y的坐標(biāo)保存,得到[ [123,12],[234,21] ]這一格式,然后遍歷這一列表,使用動作鏈對每一個列表元素對應(yīng)的x,y指定的位置進(jìn)行模擬點(diǎn)擊操作,最后定位并點(diǎn)擊確認(rèn),可成功實(shí)現(xiàn)登錄 B 站。
按順序模擬點(diǎn)擊每個坐標(biāo)點(diǎn)
def?click_codeImg(all_list,?code_img_ele):
????#?遍歷列表,使用動作鏈對每一個列表元素對應(yīng)的x,y指定的位置進(jìn)行點(diǎn)擊操作
????for?item?in?all_list:
????????x?=?item[0]?*?1.6
????????y?=?item[1]?*?1.6
????????#?move_to_element_with_offset移動到距某個元素(左上角坐標(biāo))多少距離的位置
????????ActionChains(browser).move_to_element_with_offset(code_img_ele,?x,?y).click().perform()
????????sleep(random.random())
????????logging.info('點(diǎn)擊成功!')
????sleep(random.random()*2)
????#?完成動作鏈點(diǎn)擊操作后,定位確認(rèn)按鈕并點(diǎn)擊
????browser.find_element_by_xpath('/html/body/div[2]/div[2]/div[6]/div/div/div[3]/a').click()
主函數(shù)調(diào)用
#?-*-?coding:?UTF-8?-*-
def?main():
????#?進(jìn)入登錄界面,輸入賬號密碼
????login()
????#?保存頁面截圖,并根據(jù)坐標(biāo)裁剪獲取驗(yàn)證碼圖片
????code_img_ele?=?save_img()
????#?縮小圖片
????narrow_img()
????#?將圖片提交給超級鷹,獲取返回的識別結(jié)果
????result?=?submit_img()
????#?解析返回結(jié)果,將數(shù)據(jù)格式化
????all_list?=?parse_data(result)
????#?在頁面驗(yàn)證碼上完成點(diǎn)擊操作并登錄
????click_codeImg(all_list,?code_img_ele)
main()
運(yùn)行效果如下:


鼠標(biāo)是放在左邊的,沒有動過,不是人工悄悄點(diǎn)的哦,這樣我們就成功實(shí)現(xiàn)了 selenium 模擬登錄 B 站。
作者:葉庭云?
CSDN:https://yetingyun.blog.csdn.net/
本文來自公眾號讀者投稿,歡迎各位童鞋向公號投稿,點(diǎn)擊下面圖片了解詳情!
-END-

