Python3 爬虫实战 — 模拟登陆12306【点触验证码对抗】
- 登陸時間:2019-10-21
- 實現難度:★★★☆☆☆
- 請求鏈接:https://kyfw.12306.cn/otn/resources/login.html
- 實現目標:模擬登陸中國鐵路12306,攻克點觸驗證碼
- 涉及知識:點觸驗證碼的攻克、自動化測試工具 Selenium 的使用、對接在線打碼平臺
- 完整代碼:https://github.com/TRHX/Python3-Spider-Practice/tree/master/AutomationTool/12306-login
- 其他爬蟲實戰代碼合集(持續更新):https://github.com/TRHX/Python3-Spider-Practice
- 爬蟲實戰專欄(持續更新):https://itrhx.blog.csdn.net/article/category/9351278
文章目錄
- 【1x00】思維導圖
- 【2x00】打碼平臺選擇
- 【3x00】初始化模塊
- 【3x01】初始化函數
- 【3x02】賬號密碼輸入函數
- 【4x00】驗證碼處理模塊
- 【4x01】驗證碼圖片剪裁函數
- 【4x02】驗證碼坐標解析函數
- 【4x03】模擬點擊驗證碼函數
- 【5x00】登錄模塊
- 【6x00】完整代碼
- 【6x01】12306.py
- 【6x02】chaojiying.py
- 【7x00】效果實現動圖
【1x00】思維導圖
-
利用自動化測試工具 Selenium 直接模擬人的行為方式來完成驗證
-
發送請求,出現驗證碼后,剪裁并保存驗證碼圖片
-
選擇在線打碼平臺,獲取其API,以字節流格式發送圖片
-
打碼平臺人工識別驗證碼,返回驗證碼的坐標信息
-
解析返回的坐標信息,模擬點擊驗證碼,完成驗證后點擊登陸
【2x00】打碼平臺選擇
關于打碼平臺:在線打碼平臺全部都是人工在線識別,準確率非常高,原理就是先將驗證碼圖片提交給平臺,平臺會返回識別結果在圖片中的坐標位置,然后我們再解析坐標模擬點擊即可,常見的打碼平臺有超級鷹、云打碼等,打碼平臺是收費的,拿超級鷹來說,1元 = 1000題分,識別一次驗證碼將花費一定的題分,不同類型驗證碼需要的題分不同,驗證碼越復雜所需題分越高,比如 7 位中文漢字需要 70 題分,常見 4 ~ 6 位英文數字只要 10 題分,其他打碼平臺價格也都差不多,本次實戰使用超級鷹打碼平臺
使用打碼平臺:在超級鷹打碼平臺注冊賬號,官網:http://www.chaojiying.com/ ,充值一塊錢得到 1000 題分,在用戶中心里面申請一個軟件 ID ,在價格體系里面確定驗證碼的類型,先觀察 12306 官網,發現驗證碼是要我們點擊所有滿足條件的圖片,一般有 1 至 4 張圖片滿足要求,由此可確定在超級鷹打碼平臺的驗證碼類型為 9004(坐標多選,返回1~4個坐標,如:x1,y1|x2,y2|x3,y3), 然后在開發文檔里面獲取其 Python API,下載下來以備后用
【3x00】初始化模塊
【3x01】初始化函數
# 12306賬號密碼 USERNAME = '155********' PASSWORD = '***********'# 超級鷹打碼平臺賬號密碼 CHAOJIYING_USERNAME = '*******' CHAOJIYING_PASSWORD = '*******'# 超級鷹打碼平臺軟件ID CHAOJIYING_SOFT_ID = '********' # 驗證碼類型 CHAOJIYING_KIND = '9004'class CrackTouClick():def __init__(self):self.url = 'https://kyfw.12306.cn/otn/resources/login.html'# path是谷歌瀏覽器驅動的目錄,如果已經將目錄添加到系統變量,則不用設置此路徑path = r'F:\PycharmProjects\Python3爬蟲\chromedriver.exe'chrome_options = Options()chrome_options.add_argument('--start-maximized')self.browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options)self.wait = WebDriverWait(self.browser, 20)self.username = USERNAMEself.password = PASSWORDself.chaojiying = ChaojiyingClient(CHAOJIYING_USERNAME, CHAOJIYING_PASSWORD, CHAOJIYING_SOFT_ID)定義 12306 賬號(USERNAME)、密碼(PASSWORD)、超級鷹用戶名(CHAOJIYING_USERNAME)、超級鷹登錄密碼(CHAOJIYING_PASSWORD)、超級鷹軟件 ID(CHAOJIYING_SOFT_ID)、驗證碼類型(CHAOJIYING_KIND),登錄頁面 url ,谷歌瀏覽器驅動的目錄(path),瀏覽器啟動參數等,將超級鷹賬號密碼等相關參數傳遞給超級鷹 API
【3x02】賬號密碼輸入函數
def get_input_element(self):# 登錄頁面發送請求self.browser.get(self.url)# 登錄頁面默認是掃碼登錄,所以首先要點擊賬號登錄login = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-hd-account')))login.click()time.sleep(3)# 查找到賬號密碼輸入位置的元素username = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-userName')))password = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-password')))# 輸入賬號密碼username.send_keys(self.username)password.send_keys(self.password)分析頁面可知,登陸頁面默認出現的是掃描二維碼登陸,所以要先點擊賬號登錄,找到該 CSS 元素為 login-hd-account,調用 click() 方法實現模擬點擊,此時出現賬號密碼輸入框,同樣找到其 ID 分別為 J-userName 和 J-password,調用 send_keys() 方法輸入賬號密碼
【4x00】驗證碼處理模塊
def crack(self):# 調用賬號密碼輸入函數self.get_input_element()# 調用驗證碼圖片剪裁函數image = self.get_touclick_image()bytes_array = BytesIO()image.save(bytes_array, format='PNG')# 利用超級鷹打碼平臺的 API PostPic() 方法把圖片發送給超級鷹后臺,發送的圖像是字節流格式,返回的結果是一個JSONresult = self.chaojiying.PostPic(bytes_array.getvalue(), CHAOJIYING_KIND)print(result)# 調用驗證碼坐標解析函數locations = self.get_points(result)# 調用模擬點擊驗證碼函數self.touch_click_words(locations)# 調用模擬點擊登錄函數self.login()try:# 查找是否出現用戶的姓名,若出現表示登錄成功success = self.wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, '.welcome-name'), '譚先生'))print(success)cc = self.browser.find_element(By.CSS_SELECTOR, '.welcome-name')print('用戶' + cc.text + '登錄成功')# 若沒有出現表示登錄失敗,繼續重試,超級鷹會返回本次識別的分值except TimeoutException:self.chaojiying.ReportError(result['pic_id'])self.crack()crack() 為驗證碼處理模塊的主函數
調用賬號密碼輸入函數 get_input_element(),等待賬號密碼輸入完畢
調用驗證碼圖片剪裁函數 get_touclick_image(),得到驗證碼圖片
利用超級鷹打碼平臺的 API PostPic() 方法把圖片發送給超級鷹后臺,發送的圖像是字節流格式,返回的結果是一個JSON,如果識別成功,典型的返回結果類似于:
{'err_no': 0, 'err_str': 'OK', 'pic_id': '6002001380949200001', 'pic_str': '132,127|56,77', 'md5': '1f8e1d4bef8b11484cb1f1f34299865b'}其中,pic_str 就是識別的文字的坐標,是以字符串形式返回的,每個坐標都以 | 分隔
調用 get_points() 函數解析超級鷹識別結果
調用 touch_click_words() 函數對符合要求的圖片進行點擊
調用模擬點擊登錄函數 login(),點擊登陸按鈕模擬登陸
使用 try-except 語句判斷是否出現了用戶信息,判斷依據是是否有用戶姓名的出現,出現的姓名和實際姓名一致則登錄成功,如果失敗了就重試,超級鷹會返回該分值
【4x01】驗證碼圖片剪裁函數
def get_touclick_image(self, name='12306.png'):# 獲取驗證碼的位置element = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-pwd-code')))time.sleep(3)location = element.locationsize = element.sizetop, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size['width']# 先對整個頁面截圖screenshot = self.browser.get_screenshot_as_png()screenshot = Image.open(BytesIO(screenshot))# 根據驗證碼坐標信息,剪裁出驗證碼圖片captcha = screenshot.crop((left, top, right, bottom))captcha.save(name)return captcha首先查找到驗證碼的坐標信息,先對整個頁面截圖,然后根據驗證碼坐標信息,剪裁出驗證碼圖片
location 屬性可以返回該圖片對象在瀏覽器中的位置,坐標軸是以屏幕左上角為原點,x 軸向右遞增,y 軸向下遞增,size 屬性可以返回該圖片對象的高度和寬度,由此可以得到驗證碼的位置信息
【4x02】驗證碼坐標解析函數
def get_points(self, captcha_result):# 超級鷹識別結果以字符串形式返回,每個坐標都以|分隔groups = captcha_result.get('pic_str').split('|')# 將坐標信息變成列表的形式locations = [[int(number) for number in group.split(',')] for group in groups]return locationsget_points() 方法將超級鷹的驗證碼識別結果變成列表的形式
【4x03】模擬點擊驗證碼函數
def touch_click_words(self, locations):element = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-pwd-code')))# 循環點擊正確驗證碼的坐標for location in locations:print(location)ActionChains(self.browser).move_to_element_with_offset(element, location[0], location[1]).click().perform()循環提取正確的驗證碼坐標信息,依次點擊驗證碼
【5x00】登錄模塊
def login(self):submit = self.wait.until(EC.element_to_be_clickable((By.ID, 'J-login')))submit.click()分析頁面,找到登陸按鈕的 ID 為 J-login,調用 click() 方法模擬點擊按鈕實現登錄
【6x00】完整代碼
【6x01】12306.py
# ============================================= # --*-- coding: utf-8 --*-- # @Time : 2019-10-21 # @Author : TRHX # @Blog : www.itrhx.com # @CSDN : https://blog.csdn.net/qq_36759224 # @FileName: 12306.py # @Software: PyCharm # =============================================import time from io import BytesIO from PIL import Image from selenium import webdriver from selenium.webdriver.chrome.options import Options 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 ChaojiyingClient from selenium.common.exceptions import TimeoutException# 12306賬號密碼 USERNAME = '155********' PASSWORD = '***********'# 超級鷹打碼平臺賬號密碼 CHAOJIYING_USERNAME = '********' CHAOJIYING_PASSWORD = '********'# 超級鷹打碼平臺軟件ID CHAOJIYING_SOFT_ID = '******' # 驗證碼類型 CHAOJIYING_KIND = '9004'class CrackTouClick():def __init__(self):self.url = 'https://kyfw.12306.cn/otn/resources/login.html'# path是谷歌瀏覽器驅動的目錄,如果已經將目錄添加到系統變量,則不用設置此路徑path = r'F:\PycharmProjects\Python3爬蟲\chromedriver.exe'chrome_options = Options()chrome_options.add_argument('--start-maximized')self.browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options)self.wait = WebDriverWait(self.browser, 20)self.username = USERNAMEself.password = PASSWORDself.chaojiying = ChaojiyingClient(CHAOJIYING_USERNAME, CHAOJIYING_PASSWORD, CHAOJIYING_SOFT_ID)def crack(self):# 調用賬號密碼輸入函數self.get_input_element()# 調用驗證碼圖片剪裁函數image = self.get_touclick_image()bytes_array = BytesIO()image.save(bytes_array, format='PNG')# 利用超級鷹打碼平臺的 API PostPic() 方法把圖片發送給超級鷹后臺,發送的圖像是字節流格式,返回的結果是一個JSONresult = self.chaojiying.PostPic(bytes_array.getvalue(), CHAOJIYING_KIND)print(result)# 調用驗證碼坐標解析函數locations = self.get_points(result)# 調用模擬點擊驗證碼函數self.touch_click_words(locations)# 調用模擬點擊登錄函數self.login()try:# 查找是否出現用戶的姓名,若出現表示登錄成功success = self.wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, '.welcome-name'), '譚先生'))print(success)cc = self.browser.find_element(By.CSS_SELECTOR, '.welcome-name')print('用戶' + cc.text + '登錄成功')# 若沒有出現表示登錄失敗,繼續重試,超級鷹會返回本次識別的分值except TimeoutException:self.chaojiying.ReportError(result['pic_id'])self.crack()# 賬號密碼輸入函數def get_input_element(self):# 登錄頁面發送請求self.browser.get(self.url)# 登錄頁面默認是掃碼登錄,所以首先要點擊賬號登錄login = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-hd-account')))login.click()time.sleep(3)# 查找到賬號密碼輸入位置的元素username = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-userName')))password = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-password')))# 輸入賬號密碼username.send_keys(self.username)password.send_keys(self.password)# 驗證碼圖片剪裁函數def get_touclick_image(self, name='12306.png'):# 獲取驗證碼的位置element = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-pwd-code')))time.sleep(3)location = element.locationsize = element.sizetop, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size['width']# 先對整個頁面截圖screenshot = self.browser.get_screenshot_as_png()screenshot = Image.open(BytesIO(screenshot))# 根據驗證碼坐標信息,剪裁出驗證碼圖片captcha = screenshot.crop((left, top, right, bottom))captcha.save(name)return captcha# 驗證碼坐標解析函數,分析超級鷹返回的坐標def get_points(self, captcha_result):# 超級鷹識別結果以字符串形式返回,每個坐標都以|分隔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):element = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-pwd-code')))# 循環點擊正確驗證碼的坐標for location in locations:print(location)ActionChains(self.browser).move_to_element_with_offset(element, location[0], location[1]).click().perform()# 模擬點擊登錄函數def login(self):submit = self.wait.until(EC.element_to_be_clickable((By.ID, 'J-login')))submit.click()if __name__ == '__main__':crack = CrackTouClick()crack.crack()【6x02】chaojiying.py
import requests from hashlib import md5class ChaojiyingClient(object):def __init__(self, username, password, soft_id):self.username = usernamepassword = password.encode('utf8')self.password = md5(password).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 PostPic(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 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)return r.json()【7x00】效果實現動圖
最終實現效果圖:(關鍵信息已經過打碼處理)
總結
以上是生活随笔為你收集整理的Python3 爬虫实战 — 模拟登陆12306【点触验证码对抗】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浦发万用随借金好申请吗 浦发万用随借金怎
- 下一篇: 花5万买指数基金收益怎么样?一篇文章教你