Python之selenium,使用webdriver模擬登錄網站(含驗證碼)

語言: CN / TW / HK

theme: smartblue highlight: a11y-dark


持續創作,加速成長!這是我參與「掘金日新計劃 · 10 月更文挑戰」的第1天,點擊查看活動詳情

一、前言

前段時間做了一個小項目,其中有一段需要自動獲取網站後台的數據,但是這個網站沒有任何提供給開發者的API,所以只能靠自己去探索。

起初想着用發送請求的方式去模擬登陸,獲取cookies,從而再獲取網站後台數據,但是因為~~自己太菜了~~一些原因,放棄了這個方法。

後來想到使用webdriver調用瀏覽器來模擬登陸,發現操作起來簡單很多,而且可以達到同樣的效果,於是便有了這篇文章。

二、準備工作

需要下載和安裝一些東西,這裏直接丟一篇Python Selenium庫的使用過來,裏面有詳細的安裝方法,也有selenium庫的一些基本介紹和用例。 當然也可以去官方教程中進行了解。 學習本篇文章的內容只需要安裝好selenium庫以及下載好瀏覽器對應版本的驅動即可。

三、模擬登陸網站

1. 加載驅動和配置

這裏我使用的是默認配置,如果有特殊需求,可以參照官方文檔進行學習 python browser = webdriver.Chrome(executable_path='你驅動的本地路徑', options=webdriver.ChromeOptions())

2. 打開網站

python browser.get(要打開的網站) 不出意外,此時會自動拉起你的瀏覽器並進入到你想進入的網站了,如果你的瀏覽器同時還多出來了一個像下圖這樣的頁面:

拉起瀏覽器時多出來的頁面

不用管,因為這對你將要進行的操作基本沒有影響。

3. 尋找元素並填入賬號密碼

常規寫法如下 python browser.find_element_by_name('username').send_keys('你的賬號') # 填入用户名 browser.find_element_by_name('password').send_keys('你的密碼') # 填入密碼 或者你也可以這樣,效果和上面一樣 python username = browser.find_element_by_name('username') # 尋找賬號輸入框 username.send_keys('你的賬號') # 填入用户名 password = browser.find_element_by_name('password') # 尋找密碼輸入框 password.send_keys('你的密碼') # 填入密碼

上面尋找元素的方法中填入的username和password是根據頁面中的元素name進行尋找的,需要根據實際情況更改,如果輸入框沒有name屬性,那麼也可以使用id或者xpath進行尋找。

python browser.find_element_by_id() browser.find_element_by_xpath()

當然前提是你要先手動去網站中獲取輸入框的這些屬性,比如説百度,按F12進行元素審查,然後按Ctrl+shift+C選擇你要找的元素,如下圖:

image.png

然後去查看該元素有沒有屬性id或者name,如果有的話可以直接用,如果沒有的話就單擊鼠標右鍵,然後選擇cope,選擇xPath,然後把你複製到的xPath放到代碼裏就好了。

image.png

4. 填寫驗證碼

一般的網站登錄時都會有驗證碼,而驗證碼的識別一般是比較麻煩的一件事情(對於新手來説),這裏分享一下我的經驗。

首先需要自己去找一個打碼平台(直接百度搜打碼平台,然後挑一個合適的就好了),然後去閲讀它的接入文檔,使用它的API進行識別網站的驗證碼,一般需要給它傳入一個以base64編碼的字符串,代表待識別圖片,然後它會給你返回一些信息,裏面會包括識別結果。

回到我們的程序,找到驗證碼圖片的元素,有些驗證碼可以直接獲取到base64形式的圖片鏈接,這時候直接傳給打碼平台進行識別就可以了,代碼如下: python image = browser.find_element_by_xpath('//*[@id="root"]/div/div/div/div/div[2]/div[3]/div[1]/div[2]/img') image = image.get_attribute('src') # 獲取當前驗證碼圖片鏈接 imageCaptchaValue = readImage(image) # 這裏的readImage是用來給打碼平台發送請求的方法,需要根據自己的實際情況去實現這個方法 captchaValue = browser.find_element_by_xpath('//*[@id="root"]/div/div/div/div/div[2]/div[3]/div[1]/input') # 驗證碼輸入框 captchaValue.send_keys(imageCaptchaValue) # 填入驗證碼 如果你要登錄的網站的驗證碼不是base64形式的鏈接,甚至你單擊右鍵在新窗口中打開圖片時顯示的又是另一張圖片,那麼很可能就需要用到下面的方法了。

  1. 調用screenshot()方法對驗證碼進行截圖操作,並對圖片進行保存
  2. 將保存的圖片以二進制的方式打開,並轉為base64編碼
  3. 將得到的base64編碼字符串解碼並傳給打碼平台進行識別

代碼如下: python browser.find_element_by_name('img').screenshot("img.png") # 找到驗證碼並將驗證碼以截圖的方式保存 f = open('img.png', 'rb') # 二進制方式打開圖文件 image = base64.b64encode(f.read()) # 讀取文件內容,轉換為base64編碼 f.close() imageCaptchaValue = readImage(image.decode()) # base64編碼解碼後進行識別 browser.find_element_by_name('rand').send_keys(imageCaptchaValue) # 填入驗證碼 這裏需要注意的是以base64編碼進行發送的時候需要先解碼,否則會報錯: TypeError: Object of type bytes is not JSON serializable 進行到這一步,如果不出意外的話,只需要點擊登錄按鈕就可以成功登錄了: python browser.find_element_by_id('login_btn1').click() # 點擊登錄按鈕 登錄網址後就可以進行自己想要做的事情啦! 這裏再放一些登錄後可能需要的代碼: python browser.switch_to.alert.accept() # 接受彈窗警告 cookies = browser.get_cookies() # 獲取當前狀態下的cookies dataUrl = browser.find_element_by_xpath('/html/body/a[1]').get_attribute('href') # 獲取數據文件下載路徑

四、遇到的一些坑

  1. 有些網站的元素並不能直接通過find尋找到,比如有些網站頁面用了大量的frame或iframe,那麼此時需要先進入到該元素所在的frame,然後才能找到需要的元素: python browser.switch_to.frame('left') # 進入到frame下 doSomething...... browser.switch_to.default_content() # 重新回到主頁面上操作元素 browser.switch_to.frame('main') # 進入到新的frame中進行操作 doSomething......
  2. 一般的打碼平台都需要充值後才能使用,建議先進行在線免費測試,確認它可以識別我們的驗證碼了之後再進行充值使用。
  3. 操作完畢後要記得關閉瀏覽器browser.quit()
  4. 如果在調用selenium的一些方法的時候發現pycharm給代碼打了中劃線,那麼可能是這樣方法已經不被推薦使用了,這時候稍微修改一下即可: python switch_to_window 改為 switch_to.window switch_to_default_content 改為 switch_to.default_content switch_to_frame 改為 switch_to.frame
  5. 有時候瀏覽器的加載速度跟不上你代碼的運行速度,或者請求驗證碼識別速度比較慢,建議使用暫停的方法來進行緩衝: python browser.implicitly_wait(3) time.sleep(2)

五、後記

其實整個流程操作下來,也就幾十行代碼,但是對於~~像我這樣的菜雞~~新手來説,可能需要七找八找,花上好幾個小時,才能解決其中遇到的一些問題。學會selenium的一些方法之後,你會發現你還可以做很多其他的事情,然後掛在服務器上……哈哈哈,這裏就不多説了。

如果大家在操作過程中還遇到了其他的困難,多多百度,百度能解決你大部分問題。等一個個解決之後,你還可以寫一篇博客記錄一下,把自己的經驗分享給大家,讓自己學到的東西更有意義,這也是一種樂趣吧!