【验证码的识别】—— 点触式验证码的识别
一、前言
大家好,不知不覺的我來csdn已經又一周年了,在這一年里,我收獲了很多東西,我是2022年2月22日入駐CSDN的,一開始只是為了方便瀏覽文章的,后來,我也有事沒事發發文章,創作了100多篇文章,有近三分之一是高質量文章,在這個不到一年里,我收獲了1066位粉絲,其實,我寫文章不是為了粉絲數量,只是在這個平臺把自己的知識分享給別人。在新的一年里,我可以繼續努力,日出萬物生,日落滿天星。新的一年依然記得仰望星空。2022年6月16日,那時候我才2個粉絲。雖然現在的粉絲不多,2000都沒有。但是,這些都是次要的,主要是我在這里學到了很多東西。
今天借這個機會表達一下,我在csdn一周年的紀念。為了回饋粉絲長久以來的支持,博主決定開始給大家送福利了。在爬蟲時,網上的免費代理IP不好用,怎么辦?不要慌,我給大家爭取到了一個福利,點擊下面鏈接即可免費領取七天測試
????????http://suo.nz/2zmKBG
????????白嫖不要不要的
二、引文
目前,許多網站采取各種各樣的措施來反爬蟲,其中一個措施便是使用驗證碼。隨著技術的發展,驗證碼的花樣越來越多。驗證碼最初是幾個數字組合的簡單的圖形驗證碼,后來加入了英文字母和混淆曲線。有的網站還可能看到中文字符的驗證碼,這使得識別愈發困難。
后來 12306 驗證碼的出現使得行為驗證碼開始發展起來,用過 12306 的用戶肯定多少為它的驗證碼頭疼過。我們需要識別文字,點擊與文字描述相符的圖片,驗證碼完全正確,驗證才能通過。現在這種交互式驗證碼越來越多,如極驗滑動驗證碼需要滑動拼合滑塊才可以完成驗證,點觸驗證碼需要完全點擊正確結果才可以完成驗證,另外還有滑動宮格驗證碼、計算題驗證碼等。
驗證碼變得越來越復雜,爬蟲的工作也變得愈發艱難。有時候我們必須通過驗證碼的驗證才可以訪問頁面。本章就專門針對驗證碼的識別做統一講解。
接下來會涉及的驗證碼有普通圖形驗證碼、極驗滑動驗證碼、點觸驗證碼、微博宮格驗證碼,這些驗證碼識別的方式和思路各有不同。了解這幾個驗證碼的識別方式之后,我們可以舉一反三,用類似的方法識別其他類型驗證碼。
三、點觸驗證碼識別
除了極驗驗證碼,還有另一種常見且應用廣泛的驗證碼,即點觸驗證碼。
可能你對這個名字比較陌生,但是肯定見過類似的驗證碼,比如 12306 就是典型的點觸驗證碼,直接點擊圖中符合要求的圖。所有答案均正確,驗證才會成功。如果有一個答案錯誤,驗證就會失敗。這種驗證碼就稱為點觸驗證碼。
3.1. 本節目標
我們的目標是用程序來識別并通過點觸驗證碼的驗證。
3.2. 準備工作
我們使用的 Python 庫是 Selenium,使用的瀏覽器為 Chrome。請確保已經正確安裝好 Selenium 庫、Chrome 瀏覽器,并配置好 ChromeDriver,相關流程可以參考我其他的博文。
3.3. 了解點觸驗證碼
與 12306 站點相似,不過這次是點擊圖片中的文字而非圖片。點觸驗證碼有很多種,它們的交互形式略有不同,但其基本原理都是類似的。
3.4. 識別思路
第一步:如果依靠圖像識別點觸驗證碼,則識別難度非常大。例如,某網站的識別難點有兩點,第一點是文字識別,第二步:圖像的識別。將圖像重新轉化文字,可以借助各種識圖接口,但識別的準確率非常低,經常會出現匹配不正確或無法匹配的情況。而且圖片清晰度不夠,識別難度也會更大。
3.5. 解決辦法
此類驗證碼該如何識別?互聯網上有很多驗證碼服務平臺,平臺 7×24 小時提供驗證碼識別服務,一張圖片幾秒就會獲得識別結果,準確率可達 90% 以上 ,例如超級鷹平臺:
超級鷹平臺提供了如下一些服務:
- 1.英文數字:提供最多 20 位英文數字的混合識別
- 2.中文漢字:提供最多 7 個漢字的識別
- 3.純英文:提供最多 12 位的英文的識別
- 4.純數字:提供最多 11 位的數字的識別
- 5.任意特殊字符:提供不定長漢字英文數字、拼音首字母、計算題、成語混合、 集裝箱號等字符的識別
- 6.坐標選擇識別:如復雜計算題、選擇題四選一、問答題、點擊相同的字、物品、動物等返回多個坐標的識別
這里我們需要處理的就是坐標多選識別的情況。我們先將驗證碼圖片提交給平臺,平臺會返回識別結果在圖片中的坐標位置,然后我們再解析坐標模擬點擊即可
3.6. 獲取 API
在官方網站下載對應的 Python API,鏈接為:https://www.chaojiying.com/api-14.html。此 API 是 Python 2 版本的,是用 requests 庫來實現的。我們可以簡單更改幾個地方,即可將其修改為 Python 3 版本。
修改之后的 API 如下所示:
import requests from hashlib import md5class 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(),它是發生錯誤的時候的回調。如果驗證碼識別錯誤,調用此方法會返回相應的題分。
3.7.模擬登陸
利用selenium模擬以賬號登陸方式模擬登陸某網站。
from chaojiying import Chaojiying from selenium import webdriver from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.support.wait import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By import time from io import BytesIO # from urllib import request from PIL import Image import logging logging.basicConfig(level=logging.DEBUG, format='%(asctime)s-%(levelname)s-%(message)s') logger = logging.getLogger('spider')class Jianshu():def __init__(self,cjy_username,cjy_password,cjy_softid,cjy_kind):self.url = 'https://www.jianshu.com/sign_in'# self.browser = webdriver.Chrome()self.browser = webdriver.PhantomJS()self.wait = WebDriverWait(self.browser,20)self.cjy_kind = cjy_kindself.chaojiying = Chaojiying(cjy_username,cjy_password,cjy_softid)def __del__(self):self.browser.close()def open(self,js_username,js_password):"""打開網頁輸入用戶名密碼:return: None"""self.browser.maximize_window()self.browser.get(self.url)self.browser.implicitly_wait(10)self.browser.find_element_by_link_text('登錄').click()login_name = self.wait.until(EC.presence_of_element_located((By.XPATH,"//input[@type='text' and @id='session_email_or_mobile_number']")))login_name.send_keys(js_username)login_password = self.browser.find_element_by_xpath('//input[@type="password" and @id="session_password"]')login_password.send_keys(js_password)def get_button(self):button = self.browser.find_element_by_class_name('sign-in-button')return buttondef get_element(self,name='captch.png'):element = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'.geetest_widget')))self.element = elementlocation = element.locationsize = element.size#獲取驗證碼左上角位置,以及驗證碼的寬和高(取它的位置和寬高,隨后返回其左上角和右下角的坐標)left_y, right_y, left_x, right_x = location['y'], location['y'] + size['height'], location['x'], location['x'] + size['width']# print(left_x,right_x,left_y,right_y)#獲取屏幕截圖,以二進制形式存入內存中scrsnap = self.browser.get_screenshot_as_png()scrsnap = Image.open(BytesIO(scrsnap))#裁剪圖片crop方法傳入參數依次為(圖片左上角x,圖片左上角y,圖片右下角x,圖片右下角y)captcha = scrsnap.crop((left_x,left_y,right_x,right_y))captcha.save(name)return captchadef get_point(self,result):# result = {'err_no': 0, 'err_str': 'OK', 'pic_id': '3108110014436000003', 'pic_str': '47,127|56,124', 'md5': '6e5164aa4f99e6f25dfd95fd12e30e1c'}#pic_str依次為需要識別的文字的坐標,是以字符串形式返回的,每個坐標都以|分隔pic_strs = result.get('pic_str').split('|') #['47,127', '56,124']# locations = [[int(number) for number in group.split(',')] for group in pic_str]locations = []for pic_str in pic_strs:location = [int(number) for number in pic_str.split(',')]# print(locations) [[47, 127], [56, 124]]locations.append(location)return(locations,len(locations))def click_action(self,locations):for location in locations:print(location)#調用動作鏈move_to_element_with_offset(o_element,xoffset,yoffset)方法,移動到某個元素的某個坐標上ActionChains(self.browser).move_to_element_with_offset(self.element,location[0],location[1]).click().perform()time.sleep(1)try:self.browser.find_element_by_class_name('geetest_commit').click()except Exception as error:print(error)def run_task(self,js_username,js_password):self.open(js_username,js_password)button = self.get_button()button.click()image = self.get_element()#創建操作二進制的內存流byte_flow = BytesIO() #<class '_io.BytesIO'>#將image對象以png格式存入byte_flow文件流中image.save(byte_flow,format='PNG')result = {'err_no': 0, 'err_str': 'OK', 'pic_id': '3108110014436000003', 'pic_str': '116,173|47,127|56,124','md5': '6e5164aa4f99e6f25dfd95fd12e30e1c'}# result = self.chaojiying.post_pic(byte_flow.getvalue(),self.cjy_kind)locations,length = self.get_point(result)self.click_action(locations)login_status = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,"#q")))if login_status:logger.info("登錄成功")else:logger.error("登錄失敗")if __name__ == '__main__':#超級鷹用戶名、密碼、軟件 ID、驗證碼類型cjy_username = ''cjy_password = ''cjy_softid = ''cjy_kind = js_username = ''js_password = ''Jianshu(cjy_username,cjy_password,cjy_softid,cjy_kind).run_task(js_username,js_password)總結
以上是生活随笔為你收集整理的【验证码的识别】—— 点触式验证码的识别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring Boot使用Simdito
- 下一篇: java字节流读取文件_字节流读取文件