整理:Lemon
巧用 解決驗(yàn)證碼,模擬登陸某流行網(wǎng)站一、前言1. 介紹
驗(yàn)證碼多種多樣,有圖形文字的、有模擬點(diǎn)選的、有拖動(dòng)滑動(dòng)的,但其實(shí)歸根結(jié)底都需要人來(lái)對(duì)某種情形做一些判斷,然后把結(jié)果返回并提交。
如果此時(shí)提交的驗(yàn)證碼結(jié)果是正確的,并且通過(guò)了一些驗(yàn)證碼的檢測(cè),就能成功突破這個(gè)驗(yàn)證碼了。既然驗(yàn)證碼就是讓人來(lái)識(shí)別的,那么機(jī)器怎么辦呢?
如果我們也不會(huì)什么算法,怎么去解這些驗(yàn)證碼呢?此時(shí)我們需要利用可以幫助我們來(lái)識(shí)別驗(yàn)證碼的工具或平臺(tái)就,讓工具或平臺(tái)把驗(yàn)證碼識(shí)別的結(jié)果返回給我們,我們拿著結(jié)果提交,那不就好了嗎?
有專門(mén)的打碼平臺(tái)幫助我們來(lái)識(shí)別各種各樣的驗(yàn)證碼,平臺(tái)內(nèi)部對(duì)算法和人力做了集成,可以 7x24 小時(shí)來(lái)識(shí)別各種驗(yàn)證碼,包括識(shí)別圖形、坐標(biāo)點(diǎn)、缺口等各種驗(yàn)證碼,返回對(duì)應(yīng)的結(jié)果或坐標(biāo),正好可以解決我們的問(wèn)題,比如超級(jí)鷹。
B站最新登錄驗(yàn)證為點(diǎn)選驗(yàn)證碼,以模擬登錄 B 站來(lái)熟悉 庫(kù)的使用和打碼平臺(tái)的使用方法。
這個(gè)驗(yàn)證碼上面顯示了幾個(gè)漢字,同時(shí)在圖中也顯示了幾個(gè)漢字,我們需要按照順序依次點(diǎn)擊漢字在圖中的位置,點(diǎn)擊完成之后確認(rèn)提交,即可完成驗(yàn)證。這種驗(yàn)證碼如果我們沒(méi)有任何圖像識(shí)別算法基礎(chǔ)的話,是很難去識(shí)別的,所以這里我們可以借助打碼平臺(tái)來(lái)幫助我們識(shí)別漢字的位置。
2. 準(zhǔn)備工作
本文用到的 庫(kù)是 ,使用的瀏覽器為 ,確保已經(jīng)正確安裝好 庫(kù)、 瀏覽器,并配置好 ,
使用的打碼平臺(tái)是超級(jí)鷹,鏈接為:,在使用之前請(qǐng)讀者自行注冊(cè)賬號(hào)并獲取一些題分供測(cè)試,另外還可以了解平臺(tái)可識(shí)別的驗(yàn)證碼的類別。
打碼平臺(tái)能提供的服務(wù)種類一般都非常廣泛,可識(shí)別的驗(yàn)證碼類型也非常多,其中就包括點(diǎn)觸驗(yàn)證碼。
超級(jí)鷹平臺(tái)同樣支持簡(jiǎn)單的圖形驗(yàn)證碼識(shí)別,超級(jí)鷹平臺(tái)提供了如下一些服務(wù):
本例需要處理的就是坐標(biāo)多選識(shí)別的情況。我們先將驗(yàn)證碼圖片提交給平臺(tái),平臺(tái)會(huì)返回識(shí)別結(jié)果在圖片中的坐標(biāo)位置,然后我們?cè)俳馕鲎鴺?biāo)模擬點(diǎn)擊,下面我們就用程序來(lái)實(shí)現(xiàn)。
二、實(shí)現(xiàn)思路
三、代碼實(shí)現(xiàn)1. 獲取API
通過(guò)官方網(wǎng)站下載對(duì)應(yīng)的 API,鏈接為:。API 是 2 版本的,用 庫(kù)來(lái)實(shí)現(xiàn)的。我們可以簡(jiǎn)單更改幾個(gè)地方,即可將其修改為 3 版本。
修改之后的 API 如下所示:
#?-*-?coding:?UTF-8?-*-
#?Chaojiying_Client類??用于提交要識(shí)別的圖片??返回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:報(bào)錯(cuò)題目的圖片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()
2. 分析B站登錄界面
進(jìn)入登陸界面,定位用戶名和密碼對(duì)應(yīng)的標(biāo)簽,模擬輸入我們的賬號(hào)和密碼,點(diǎn)擊登錄,此時(shí)頁(yè)面會(huì)彈出驗(yàn)證碼。
對(duì)進(jìn)行驗(yàn)證碼的獲取用html做一個(gè)登陸界面,然后提交給「超級(jí)鷹」進(jìn)行識(shí)別,接收到漢字的坐標(biāo)后,處理數(shù)據(jù),然后用動(dòng)作鏈進(jìn)行模擬點(diǎn)擊操作,最后定位點(diǎn)擊確認(rèn),實(shí)現(xiàn)成功登錄 B 站。
3. 具體實(shí)現(xiàn)
導(dǎo)入用的庫(kù)
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
日志輸出和 .() 配置
#?日志輸出配置
logging.basicConfig(level=logging.INFO,?format='%(asctime)s?-?%(levelname)s:?%(message)s')
#?初始化一個(gè)webdriver.Chrome()對(duì)象
chrome_driver?=?r'D:\python\pycharm2020\chromedriver.exe'
options?=?webdriver.ChromeOptions()
#?關(guān)閉左上方?Chrome?正受到自動(dòng)測(cè)試軟件的控制的提示
options.add_experimental_option('useAutomationExtension',?False)
options.add_experimental_option("excludeSwitches",?['enable-automation'])
browser?=?webdriver.Chrome(options=options,?executable_path=chrome_driver)
用 打開(kāi)登錄頁(yè)面,進(jìn)行模擬登錄
#?登錄函數(shù)???訪問(wèn)頁(yè)面->輸出賬號(hào)、密碼->點(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)前頁(yè)面進(jìn)行截圖并保存下來(lái),裁剪出驗(yàn)證碼區(qū)域
def?save_img():
????# save_screenshot:將當(dāng)前頁(yè)面進(jìn)行截圖并保存下來(lái)
????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)證碼圖片對(duì)應(yīng)的長(zhǎ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)擊登錄后的頁(yè)面進(jìn)行截圖,然后定位到驗(yàn)證碼的位置,通過(guò)()方法獲取驗(yàn)證碼左上角的坐標(biāo), size() 獲取驗(yàn)證碼的寬和高,左上角坐標(biāo)加上寬和高就是驗(yàn)證碼右下角的坐標(biāo)。獲取坐標(biāo)后就可以用 crop() 方法來(lái)進(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)
此時(shí)雖然獲取了驗(yàn)證碼圖片,但是還不能直接提交給超級(jí)鷹。因?yàn)槌?jí)鷹識(shí)別的驗(yàn)證碼圖片的寬和高有限制,最好不超過(guò)460px*310px。但是截取到的驗(yàn)證碼圖片寬高為338px*432px,這時(shí)就要先將圖片縮小一倍再提交即可,等到收到坐標(biāo)數(shù)據(jù)再將坐標(biāo)乘2。
將驗(yàn)證碼提交給超級(jí)鷹進(jìn)行識(shí)別
def?submit_img():
????#?將驗(yàn)證碼提交給超級(jí)鷹進(jìn)行識(shí)別
????#?用戶中心->軟件ID?生成你的軟件ID->替換掉96001??綁定微信可以得到1000積分?免費(fèi)使用
????chaojiying?=?Chaojiying_Client('你的用戶名',?'密碼',?'生成的軟件ID')
????with?open('./small_img.png',?'rb')?as?f:
????????im?=?f.read()
????#?本地圖片文件路徑?來(lái)替換?a.jpg?有時(shí)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?=?[]??#?存儲(chǔ)即將被點(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
超級(jí)鷹識(shí)別返回的結(jié)果的數(shù)據(jù)格式為:98,136|87,77我們可以將數(shù)據(jù)以' | '進(jìn)行分割,保存到列表中,再以逗號(hào)分割將x,y的坐標(biāo)保存,得到[ [123,12],[234,21] ]這一格式,然后遍歷這一列表,使用動(dòng)作鏈對(duì)每一個(gè)列表元素對(duì)應(yīng)的x,y指定的位置進(jìn)行模擬點(diǎn)擊操作,最后定位并點(diǎn)擊確認(rèn)用html做一個(gè)登陸界面,可成功實(shí)現(xiàn)登錄 B 站。
按順序模擬點(diǎn)擊每個(gè)坐標(biāo)點(diǎn)
def?click_codeImg(all_list,?code_img_ele):
????#?遍歷列表,使用動(dòng)作鏈對(duì)每一個(gè)列表元素對(duì)應(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移動(dòng)到距某個(gè)元素(左上角坐標(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)
????#?完成動(dòng)作鏈點(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)入登錄界面,輸入賬號(hào)密碼
????login()
????#?保存頁(yè)面截圖,并根據(jù)坐標(biāo)裁剪獲取驗(yàn)證碼圖片
????code_img_ele?=?save_img()
????#?縮小圖片
????narrow_img()
????#?將圖片提交給超級(jí)鷹,獲取返回的識(shí)別結(jié)果
????result?=?submit_img()
????#?解析返回結(jié)果,將數(shù)據(jù)格式化
????all_list?=?parse_data(result)
????#?在頁(yè)面驗(yàn)證碼上完成點(diǎn)擊操作并登錄
????click_codeImg(all_list,?code_img_ele)
main()
運(yùn)行效果如下:
鼠標(biāo)是放在左邊的,沒(méi)有動(dòng)過(guò),不是人工悄悄點(diǎn)的哦,這樣我們就成功實(shí)現(xiàn)了 模擬登錄 B 站。