第23讲:利用资源,学会用打码平台处理验证码
在前一課時我們介紹了多種多樣的驗證碼,有圖形文字的、有模擬點選的、有拖動滑動的,但其實歸根結底都需要人來對某種情形做一些判斷,然后把結果返回并提交。如果此時提交的驗證碼結果是正確的,并且通過了一些驗證碼的檢測,就能成功突破這個驗證碼了。
那么,既然驗證碼就是讓人來識別的,那么機器怎么辦呢?如果我們也不會什么算法,怎么去解這些驗證碼呢?此時如果有一個幫助我們來識別驗證碼的工具或平臺就好了,讓工具或平臺把驗證碼識別的結果返回給我們,我們拿著結果提交,那不就好了嗎?
有這種工具或平臺嗎?還真有專門的打碼平臺幫助我們來識別各種各樣的驗證碼,平臺內部對算法和人力做了集成,可以 7x24 小時來識別各種驗證碼,包括識別圖形、坐標點、缺口等各種驗證碼,返回對應的結果或坐標,正好可以解決我們的問題。
本課時我們就來介紹利用打碼平臺來識別驗證碼的流程。
學習目標
本課時我們以一種點選驗證碼為例來講解打碼平臺的使用方法,驗證碼的鏈接為:https://captcha3.scrape.cuiqingcai.com/,這個網站在每次登錄的時候都會彈出一個驗證碼,其驗證碼效果圖如下所示。
這個驗證碼上面顯示了幾個漢字,同時在圖中也顯示了幾個漢字,我們需要按照順序依次點擊漢字在圖中的位置,點擊完成之后確認提交,即可完成驗證。
這種驗證碼如果我們沒有任何圖像識別算法基礎的話,是很難去識別的,所以這里我們可以借助打碼平臺來幫助我們識別漢字的位置。
準備工作
我們使用的 Python 庫是 Selenium,使用的瀏覽器為 Chrome。
在本課時開始之前請確保已經正確安裝好 Selenium 庫、Chrome 瀏覽器,并配置好 ChromeDriver,相關流程可以參考 Selenium 那一課時的介紹。
另外本課時使用的打碼平臺是超級鷹,鏈接為:https://www.chaojiying.com/,在使用之前請你自己注冊賬號并獲取一些題分供測試,另外還可以了解平臺可識別的驗證碼的類別。
打碼平臺
打碼平臺能提供的服務種類一般都非常廣泛,可識別的驗證碼類型也非常多,其中就包括點觸驗證碼。
超級鷹平臺同樣支持簡單的圖形驗證碼識別。超級鷹平臺提供了如下一些服務。
打碼平臺能提供的服務種類一般都非常廣泛,可識別的驗證碼類型也非常多,其中就包括點觸驗證碼。
超級鷹平臺同樣支持簡單的圖形驗證碼識別。超級鷹平臺提供了如下一些服務。
- 英文數字:提供最多 20 位英文數字的混合識別;
- 中文漢字:提供最多 7 個漢字的識別;
- 純英文:提供最多 12 位的英文識別;
- 純數字:提供最多 11 位的數字識別;
- 任意特殊字符:提供不定長漢字英文數字、拼音首字母、計算題、成語混合、集裝箱號等字符的識別;
- 坐標選擇識別:如復雜計算題、選擇題四選一、問答題、點擊相同的字、物品、動物等返回多個坐標的識別。
具體如有變動以官網為準:https://www.chaojiying.com/price.html。
這里需要處理的就是坐標多選識別的情況。我們先將驗證碼圖片提交給平臺,平臺會返回識別結果在圖片中的坐標位置,然后我們再解析坐標模擬點擊。
下面我們就用程序來實現。
獲取 API
在官方網站下載對應的 Python API,鏈接為:https://www.chaojiying.com/api-14.html。API 是 Python 2 版本的,是用 requests 庫來實現的。我們可以簡單更改幾個地方,即可將其修改為 Python 3 版本。
修改之后的 API 如下所示:
import requests from hashlib import md5 class Chaojiying(object):def __init__(self, username, password, soft_id):self.username = usernameself.password = md5(password.encode('utf-8')).hexdigest()self.soft_id = soft_idself.base_params = {'user': self.username,'pass2': self.password,'softid': self.soft_id,}self.headers = {'Connection': 'Keep-Alive','User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',}def post_pic(self, im, codetype):"""im: 圖片字節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)return r.json()def report_error(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)return r.json()這里定義了一個 Chaojiying 類,其構造函數接收三個參數,分別是超級鷹的用戶名、密碼以及軟件 ID,保存以備使用。
最重要的一個方法叫作 post_pic,它需要傳入圖片對象和驗證碼類型的代號。該方法會將圖片對象和相關信息發給超級鷹的后臺進行識別,然后將識別成功的 JSON 返回。
另一個方法叫作 report_error,它是發生錯誤時的回調。如果驗證碼識別錯誤,調用此方法會返回相應的題分。
接下來,我們以 https://captcha3.scrape.cuiqingcai.com/ 為例來演示下識別的過程。
初始化
首先我們引入一些必要的包,然后初始化一些變量,如 WebDriver、Chaojiying 對象等,代碼實現如下所示:
import time from io import BytesIO from PIL import Image from selenium import webdriver from selenium.webdriver import ActionChains from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from chaojiying import Chaojiying USERNAME = 'admin' PASSWORD = 'admin' CHAOJIYING_USERNAME = '' CHAOJIYING_PASSWORD = '' CHAOJIYING_SOFT_ID = 893590 CHAOJIYING_KIND = 9102 if not CHAOJIYING_USERNAME or not CHAOJIYING_PASSWORD:print('請設置用戶名和密碼')exit(0) class CrackCaptcha():def __init__(self):self.url = 'https://captcha3.scrape.cuiqingcai.com/'self.browser = webdriver.Chrome()self.wait = WebDriverWait(self.browser, 20)self.username = USERNAMEself.password = PASSWORDself.chaojiying = Chaojiying(CHAOJIYING_USERNAME, CHAOJIYING_PASSWORD, CHAOJIYING_SOFT_ID)這里的 USERNAME、PASSWORD 是示例網站的用戶名和密碼,都設置為 admin 即可。另外 CHAOJIYING_USERNAME、CHAOJIYING_PASSWORD 就是超級鷹打碼平臺的用戶名和密碼,可以自行設置成自己的。
另外這里定義了一個 CrackCaptcha 類,初始化了瀏覽器對象和打碼平臺的操作對象。
接下來我們用 Selenium 模擬呼出驗證碼開始驗證就好啦。
獲取驗證碼
接下來的步驟就是完善相關表單,模擬點擊呼出驗證碼了,代碼實現如下所示:
def open(self):"""打開網頁輸入用戶名密碼:return: None"""self.browser.get(self.url)# 填入用戶名密碼username = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input[type="text"]')))password = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input[type="password"]')))username.send_keys(self.username)password.send_keys(self.password) def get_captcha_button(self):"""獲取初始驗證按鈕:return:"""button = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'button[type="button"]')))return button這里我們調用了 open 方法負責填寫表單,get_captcha_button 方法獲取驗證碼按鈕,之后觸發點擊,這時候就可以看到頁面已經把驗證碼呈現出來了。
有了驗證碼的圖片,我們下一步要做的就是把驗證碼的具體內容獲取下來,然后發送給打碼平臺識別。
那怎么獲取驗證碼的圖片呢?我們可以先獲取驗證碼圖片的位置和大小,從網頁截圖里截取相應的驗證碼圖片即可,代碼實現如下所示:
def get_captcha_element(self):"""獲取驗證圖片對象:return: 圖片對象"""# 驗證碼圖片加載出來self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'img.geetest_item_img')))# 驗證碼完整節點element = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_panel_box')))print('成功獲取驗證碼節點')return element def get_captcha_position(self):"""獲取驗證碼位置:return: 驗證碼位置元組"""element = self.get_captcha_element()time.sleep(2)location = element.locationsize = element.sizetop, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size['width']return (top, bottom, left, right) def get_screenshot(self):"""獲取網頁截圖:return: 截圖對象"""screenshot = self.browser.get_screenshot_as_png()screenshot = Image.open(BytesIO(screenshot))screenshot.save('screenshot.png')return screenshot def get_captcha_image(self, name='captcha.png'):"""獲取驗證碼圖片:return: 圖片對象"""top, bottom, left, right = self.get_captcha_position()print('驗證碼位置', top, bottom, left, right)screenshot = self.get_screenshot()captcha = screenshot.crop((left, top, right, bottom))captcha.save(name)return captcha這里 get_captcha_image 方法即為從網頁截圖中截取對應的驗證碼圖片,其中驗證碼圖片的相對位置坐標由 get_captcha_position 方法返回得到。所以就是利用了先截圖再裁切的方法獲取了驗證碼。
注意:如果你的屏幕是高清屏如 Mac 的 Retina 屏幕的話,可能需要適當調整下屏幕分辨率或者對獲取到的驗證碼位置做一些倍數偏移計算。
最后我們得到的驗證碼是 Image 對象,其結果樣例如圖所示。
識別驗證碼
現在我們有了驗證碼圖了,下一步就是把圖發送給打碼平臺了。
我們調用 Chaojiying 對象的 post_pic 方法,即可把圖片發送給超級鷹后臺,這里發送的圖像是字節流格式,代碼實現如下所示:
image = self.get_touclick_image() bytes_array = BytesIO() image.save(bytes_array, format='PNG') # 識別驗證碼 result = self.chaojiying.post_pic(bytes_array.getvalue(), CHAOJIYING_KIND) print(result)運行之后,result 變量就是超級鷹后臺的識別結果。可能運行需要等待幾秒,它會返回一個 JSON 格式的字符串。
如果識別成功,典型的返回結果如下所示:
{'err_no': 0, 'err_str': 'OK', 'pic_id': '6002001380949200001', 'pic_str': '132,127|56,77', 'md5': '1f8e1d4bef8b11484cb1f1f34299865b'}其中,pic_str 就是識別的文字的坐標,是以字符串形式返回的,每個坐標都以 | 分隔。接下來我們只需要將其解析,然后模擬點擊,代碼實現如下所示:
def get_points(self, captcha_result):"""解析識別結果:param captcha_result: 識別結果:return: 轉化后的結果"""groups = captcha_result.get('pic_str').split('|')locations = [[int(number) for number in group.split(',')] for group in groups]return locations def touch_click_words(self, locations):"""點擊驗證圖片:param locations: 點擊位置:return: None"""for location in locations:ActionChains(self.browser).move_to_element_with_offset(self.get_captcha_element(), location[0], location[1]).click().perform()time.sleep(1)這里用 get_points 方法將識別結果變成列表的形式。touch_click_words 方法則通過調用 move_to_element_with_offset 方法依次傳入解析后的坐標,點擊即可。
這樣我們就模擬完成坐標的點選了,運行效果如下所示。
最后再模擬點擊提交驗證的按鈕,等待驗證通過就會自動登錄啦,后續實現在此不再贅述。
如何判斷登錄是否成功呢?同樣可以使用 Selenium 的判定條件,比如判斷頁面里面出現了某個文字就代表登錄成功了,代碼如下:
# 判定是否成功 success = self.wait.until(EC.text_to_be_present_in_element((By.TAG_NAME, 'h2'), '登錄成功'))比如這里我們判定了點擊確認按鈕,頁面會不會跳轉到提示成功的頁面,成功的頁面包含一個 h2 節點,包含“登錄成功”四個字,就代表登錄成功啦。
這樣我們就借助在線驗證碼平臺完成了點觸驗證碼的識別。此方法是一種通用方法,我們也可以用此方法來識別圖文、數字、算術等各種各樣的驗證碼。
結語
本課時我們通過在線打碼平臺輔助完成了驗證碼的識別。這種識別方法非常強大,幾乎任意的驗證碼都可以識別。如果遇到難題,借助打碼平臺無疑是一個極佳的選擇。
總結
以上是生活随笔為你收集整理的第23讲:利用资源,学会用打码平台处理验证码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 第01讲:必知必会,掌握 HTTP 基本
- 下一篇: 第27讲:令人抓狂的 JavaScrip