想必大家在開發網頁爬蟲的過程中,都會有遇到各種反爬蟲機制的經驗,而Captcha就是其中一種透過圖片驗證的方式,來增加Python網頁爬蟲爬取資料的困難度。
所以,本文就以博客來網路書店的登入頁面為例,來和大家分享如何利用2Captcha服務,破解登入時的一般驗證碼(Normal Captcha),順利登入會員。其中實作的重點包含:
- Python Selenium下載Captcha圖片
- 提交一般驗證碼(Normal Captcha)
- 取得一般驗證碼(Normal Captcha)結果
一、Python Selenium下載Captcha圖片
由於本文開發的Python網頁爬蟲要能夠自動化輸入帳號、密碼與一般驗證碼(Normal Captcha),所以就需要安裝Python的Selenium套件及Webdriver-Manager瀏覽器管理套件,如下指令:
$ pip install selenium $ pip install webdriver-manager
接著,本文使用Visual Studio Code,建立app.py檔案,引用相關的模組(Module),如下範例:
from selenium import webdriver from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.common.by import By import time import base64 import requests
其中base64模組(Module)是等一下要用來解碼(Decode)下載下來的一般驗證碼(Normal Captcha)圖片。
前往博客來網路書店,並且點擊「登入」按鈕,如下圖:
截取自博客來網路書店官網https://www.books.com.tw/
接著,就可以看到登入畫面,如下圖:
開啟app.py檔案,利用Selenium與Webdriver-Manager模組(Module)開啟Chrome瀏覽器,並且前往博客來網路書店的登入頁面,如下範例:
from selenium import webdriver from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.common.by import By import time import base64 import requests browser = webdriver.Chrome(ChromeDriverManager().install()) browser.get('https://cart.books.com.tw/member/login')
登入頁面網址的後面參數可以無需理會,只需要到login就可以了。
接下來,利用time模組(Module)等待10秒的時間,讓網頁載入完成後,就可以透過Selenium套件的execute_script()方法(Method),執行一段JavaScript程式碼,如下範例:
from selenium import webdriver from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.common.by import By import time import base64 import requests browser = webdriver.Chrome(ChromeDriverManager().install()) browser.get('https://cart.books.com.tw/member/login') time.sleep(10) img_base64 = browser.execute_script(""" var ele = arguments[0]; var cnv = document.createElement('canvas'); cnv.width = ele.width; cnv.height = ele.height; cnv.getContext('2d').drawImage(ele, 0, 0); return cnv.toDataURL('image/jpeg').substring(22); """, browser.find_element(By.XPATH, "//*[@id='captcha_img']/img"))
第14~20行的JavaScript程式碼,就是先定位登入畫面上的一般驗證碼(Normal Captcha)圖片,接著,建立一個畫布(canvas),將一般驗證碼(Normal Captcha)繪製上去後,並且進行base64編碼。
而以上範例的一般驗證碼(Normal Captcha)圖片xpath路徑取得方式,就是在網頁上的一般驗證碼(Normal Captcha)圖片點擊右鍵,選擇「檢查」,在原始碼的地方再次點擊右鍵,選擇「Copy/Copy XPath」,如下圖:
from selenium import webdriver from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.common.by import By import time import base64 import requests browser = webdriver.Chrome(ChromeDriverManager().install()) browser.get('https://cart.books.com.tw/member/login') time.sleep(10) img_base64 = browser.execute_script(""" var ele = arguments[0]; var cnv = document.createElement('canvas'); cnv.width = ele.width; cnv.height = ele.height; cnv.getContext('2d').drawImage(ele, 0, 0); return cnv.toDataURL('image/jpeg').substring(22); """, browser.find_element(By.XPATH, "//*[@id='captcha_img']/img")) with open("captcha_login.png", 'wb') as image: image.write(base64.b64decode(img_base64))
到這邊可能有讀者會疑惑,為什麼下載圖片不使用一般驗證碼(Normal Captcha)圖片的src屬性網址來進行下載呢?這是因為在取得Captcha圖片網址後,使用requests套件再次打開圖片連結下載時,會產生不一樣的Captcha圖片驗證碼,使得網頁上與下載下來的不一致,所以才會使用JavaScript程式碼來進行繪製與編碼,確保驗證碼的一致性。
二、提交一般驗證碼(Normal Captcha)
2Captcha是一個驗證碼辨識服務,提供各種不同的驗證碼辨識,包含Text Captcha、Rotate Captcha、Click Captcha及reCaptcha等,透過它所提供的API,即可輕鬆破解網頁驗證碼,官網如下圖:
點擊右上角的Register(註冊),並且登入後,就可以看到如下圖的畫面:
其中包含了三個重要的部分:- Add funds(增加資金):存入金額才可使用2Captcha服務的API。
- API KEY:存取2Captcha服務的API KEY。
- API:2Captcha服務的API文件。
這邊帶大家來看一下2Captcha服務的API文件,點擊上圖的API按鈕,可以看到如下圖的畫面:
所以,接下來我們就要將下載下來的一般驗證碼(Normal Captcha)圖片,提交到2Captcha服務的API,如下範例:
from selenium import webdriver from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.common.by import By import time import base64 import requests browser = webdriver.Chrome(ChromeDriverManager().install()) browser.get('https://cart.books.com.tw/member/login') time.sleep(10) img_base64 = browser.execute_script(""" var ele = arguments[0]; var cnv = document.createElement('canvas'); cnv.width = ele.width; cnv.height = ele.height; cnv.getContext('2d').drawImage(ele, 0, 0); return cnv.toDataURL('image/jpeg').substring(22); """, browser.find_element(By.XPATH, "//*[@id='captcha_img']/img")) with open("captcha_login.png", 'wb') as image: image.write(base64.b64decode(img_base64)) # 下載下來的一般驗證碼(Normal Captcha)圖片 file = {'file': open('captcha_login.png', 'rb')} api_key = '你的2captcha API KEY' data = { 'key': api_key, 'method': 'post' } response = requests.post('http://2captcha.com/in.php', files=file, data=data) print(f'response:{response.text}')
執行結果
response:OK|67645583547
當回應為OK,代表一般驗證碼(Normal Captcha)提交成功,後面的數字則是驗證碼ID,用來存取另一個2Captcha服務的API,得到辨識的結果,截取的方式如下範例第39行:
from selenium import webdriver from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.common.by import By import time import base64 import requests browser = webdriver.Chrome(ChromeDriverManager().install()) browser.get('https://cart.books.com.tw/member/login') time.sleep(10) img_base64 = browser.execute_script(""" var ele = arguments[0]; var cnv = document.createElement('canvas'); cnv.width = ele.width; cnv.height = ele.height; cnv.getContext('2d').drawImage(ele, 0, 0); return cnv.toDataURL('image/jpeg').substring(22); """, browser.find_element(By.XPATH, "//*[@id='captcha_img']/img")) with open("captcha_login.png", 'wb') as image: image.write(base64.b64decode(img_base64)) # 下載下來的一般驗證碼(Normal Captcha)圖片 file = {'file': open('captcha_login.png', 'rb')} api_key = '你的2captcha API KEY' data = { 'key': api_key, 'method': 'post' } response = requests.post('http://2captcha.com/in.php', files=file, data=data) print(f'response:{response.text}') if response.ok and response.text.find('OK') > -1: captcha_id = response.text.split('|')[1] #擷取驗證碼ID else: print('提交驗證碼發生錯誤!')
三、取得一般驗證碼(Normal Captcha)結果
最後,就可以使用驗證碼ID,來取得一般驗證碼(Normal Captcha)的辨識結果,如下範例:
from selenium import webdriver from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.common.by import By import time import base64 import requests browser = webdriver.Chrome(ChromeDriverManager().install()) browser.get('https://cart.books.com.tw/member/login') time.sleep(10) img_base64 = browser.execute_script(""" var ele = arguments[0]; var cnv = document.createElement('canvas'); cnv.width = ele.width; cnv.height = ele.height; cnv.getContext('2d').drawImage(ele, 0, 0); return cnv.toDataURL('image/jpeg').substring(22); """, browser.find_element(By.XPATH, "//*[@id='captcha_img']/img")) with open("captcha_login.png", 'wb') as image: image.write(base64.b64decode(img_base64)) # 下載下來的一般驗證碼(Normal Captcha)圖片 file = {'file': open('captcha_login.png', 'rb')} api_key = '你的2captcha API KEY' data = { 'key': api_key, 'method': 'post' } response = requests.post('http://2captcha.com/in.php', files=file, data=data) print(f'response:{response.text}') if response.ok and response.text.find('OK') > -1: captcha_id = response.text.split('|')[1] #擷取驗證碼ID for i in range(10): response = requests.get( f'http://2captcha.com/res.php?key={api_key}&action=get&id={captcha_id}') print(f'response:{response.text}') if response.text.find('CAPCHA_NOT_READY') > -1: #尚未辨識完成 time.sleep(3) elif response.text.find('OK') > -1: captcha_text = response.text.split('|')[1] #擷取辨識結果 break else: print('取得驗證碼發生錯誤!') else: print('提交驗證碼發生錯誤!')
由於2Captcha服務有時無法即時辨識與回應結果,所以在第41~56行使用迴圈,當尚未辨識完成,則等待3秒後,再存取辨識結果API一次,直到回應OK完成,就擷取其中的辨識結果文字。
有了一般驗證碼(Normal Captcha)的辨識結果後,就可以利用Selenium模組(Module)自動化輸入博客來網路書店帳號、密碼及一般驗證碼(Normal Captcha)來進行登入,如下範例第53~61行:
from selenium import webdriver from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.common.by import By import time import base64 import requests browser = webdriver.Chrome(ChromeDriverManager().install()) browser.get('https://cart.books.com.tw/member/login') time.sleep(10) img_base64 = browser.execute_script(""" var ele = arguments[0]; var cnv = document.createElement('canvas'); cnv.width = ele.width; cnv.height = ele.height; cnv.getContext('2d').drawImage(ele, 0, 0); return cnv.toDataURL('image/jpeg').substring(22); """, browser.find_element(By.XPATH, "//*[@id='captcha_img']/img")) with open("captcha_login.png", 'wb') as image: image.write(base64.b64decode(img_base64)) # 下載下來的一般驗證碼(Normal Captcha)圖片 file = {'file': open('captcha_login.png', 'rb')} api_key = '你的2captcha API KEY' data = { 'key': api_key, 'method': 'post' } response = requests.post('http://2captcha.com/in.php', files=file, data=data) print(f'response:{response.text}') if response.ok and response.text.find('OK') > -1: captcha_id = response.text.split('|')[1] #擷取驗證碼ID for i in range(10): response = requests.get( f'http://2captcha.com/res.php?key={api_key}&action=get&id={captcha_id}') print(f'response:{response.text}') if response.text.find('CAPCHA_NOT_READY') > -1: #尚未辨識完成 time.sleep(3) elif response.text.find('OK') > -1: captcha_text = response.text.split('|')[1] #擷取辨識結果 username = browser.find_element_by_id('login_id') password = browser.find_element_by_id('login_pswd') captcha = browser.find_element_by_id('captcha') login = browser.find_element_by_id('books_login') username.send_keys('你的博客來帳號') #輸入帳號 password.send_keys('你的博客來密碼') #輸入密碼 captcha.send_keys(captcha_text) #輸入剛剛擷取的一般驗證碼辨識結果 login.click() #點擊登入按鈕 break else: print('取得驗證碼發生錯誤!') else: print('提交驗證碼發生錯誤!')
而其中的帳號、密碼、一般驗證碼(Normal Captcha)欄位及登入按鈕的定位方式,可以在網頁上的相應位置點擊右鍵,選擇「檢查」即可,如下圖:
四、小結
本文以一般驗證碼(Normal Captcha)為例,來和大家分享在開發Python動態網頁爬蟲時,如何利用2Captcha服務的API,來突破驗證碼(Captcha)的反爬蟲機制,順利登入或爬取所需的資料,如果大家在開發Python網頁爬蟲的過程中,有需要解決各種驗證碼(Captcha)的機制,可以前往2Captcha官網,參考所提供的API服務。
- [Pandas教學]有效使用Pandas Profiling套件實現探索式資料分析(EDA)
- [Pandas教學]利用Pandas套件的to_html方法在網頁快速顯示資料分析結果
- [Pandas教學]4個必學的Pandas套件處理遺漏值(Missing Value)資料方法
- [Pandas教學]有效利用Pandas套件的pipe方法打造資料處理流程管道
- [Pandas教學]3個優化Pandas套件讀取大型CSV檔案資料的技巧
- [Pandas教學]一定要學會的Pandas套件讀寫Google Sheets試算表資料秘訣
- [Pandas教學]客製化Pandas DataFrame樣式提升資料可讀性的實用方法
- [Pandas教學]3個Pandas套件合併多個CSV檔案資料的實用技巧
- Pandas教學]看完這篇就懂Pandas套件如何即時讀取API的回應資料
- [Pandas教學]快速掌握Pandas套件讀寫SQLite資料庫的重要方法
- [Pandas教學]輕鬆入門3個常見的Pandas套件排序資料方式
- [Pandas教學]有效利用Pandas套件篩選資料的應用技巧
- [Pandas教學]善用Pandas套件的Groupby與Aggregate方法提升資料解讀效率
- [Pandas教學]使用Pandas套件實作資料清理的必備觀念(上)
- [Pandas教學]使用Pandas套件實作資料清理的必備觀念(下)
- Visual Studio Code漂亮印出Pandas DataFrame資料的實用方法
- [Pandas教學]資料視覺化必懂的Pandas套件繪製Matplotlib分析圖表實戰
- [Pandas教學]5個實用的Pandas讀取Excel檔案資料技巧
- 解析Python網頁爬蟲如何有效整合Pandas套件提升資料處理效率
- [Pandas教學]掌握Pandas DataFrame讀取網頁表格的實作技巧
- [Pandas教學]資料分析必懂的Pandas DataFrame處理雙維度資料方法
- [Pandas教學]資料分析必懂的Pandas Series處理單維度資料方法
很棒的教學
回覆刪除不知道為什麼執行with open('captcha_login.png', 'wb') as image:會有invalid syntax的問題QQ
回覆刪除