文章目錄
-
- selenium簡介
- 反爬蟲
- 反反爬蟲
-
- 配置 Selenium 選項
- 控制已打開的浏覽器
- mitmproxy中間人
- 待續...
selenium簡介
當我們使用 requests 抓取頁面的時候,得到的結果可能會和在浏覽器中看到的不一樣,正常顯示的頁面資料,使用 reuquests 卻沒有得到結果。這是因為 requests 擷取的都是原始 HTML 文檔,而浏覽器中的頁面則是經過 Javascript 資料處理後生成的結果,這些資料的來源有多種,可能是通過 AJax 加載的,也可能是經過 Javascript 和特定算法計算後生成的。
此時解決方法通常有兩種:
- 深挖 Ajax 的邏輯,把接口位址和其加密參數構造邏輯完全找出來,再用 Python 複現,構造 Ajax請求
- 通過模拟浏覽器的方式,繞過這個過程。
這裡我們主要介紹下第二種方式,模拟浏覽器爬取。
Selenium 是一個自動化測試工具,利用它可以驅動浏覽器執行特定的操作。比如點選,下拉等操作,同時還可以擷取浏覽器目前呈現的頁面源代碼,做到 所見即所得。對于一些使用 Javascript 動态渲染的頁面來說,此種抓取方式非常有效!
反爬蟲
但是,使用 Selenium 調用 ChromeSriver 來打開網頁,還是與正常打開網頁有一定的差別的。現在很多網站都加上了對 Selenium 的檢測,來防止一些爬蟲的惡意爬取。
大多數情況下,檢測的基本原理是檢測目前浏覽器視窗下的
window.navigator
對象是否包含
webdriver
這個屬性。在正常使用浏覽器的情況下,這個屬性是
undefined
,然後一旦我們使用了 selenium,這個屬性就被初始化為
true
,很多網站就通過 Javascript 判斷這個屬性實作簡單的反 selenium爬蟲。
這時候我們可能想到通過 Javascript 直接把這個 webdriver 屬性置空,比如通過調用
execute_script
方法來執行如下代碼:
這行 Javascript 的确可以把 webdriver 屬性置空,但是 execute_script 調用這行 Javascript 語句實際上是在頁面加載完畢之後才執行的,執行得太晚了,網站早在頁面渲染之前就已經對 webdriver 屬性進行了檢測,所有用上述方法并不能達到效果。
反反爬蟲
基于上邊舉例的反爬措施,我們主要可以使用如下方法解決:
配置 Selenium 選項
但是
ChromeDriver 79.0.3945.36
版本修改了非無頭模式下排除 “啟用自動化” 時
window.navigator.webdriver
是未定義的問題,要想正常使用,需要把 Chrome 復原 79 之前的版本,并找到對應的 ChromeDriver 版本,這樣才可以!
當然,大家也可以參考
CDP(Chrome Devtools-Protocol)
文檔,使用
driver.execute_cdp_cmd
在 selenium 中調用
CDP
的指令。下述代碼隻需要執行一次,之後隻要不關閉這個 driver 開啟的視窗,無論打開多少網址,它都會在網站自帶的所有 JS 之前執行這個語句,進而達到隐藏 webdriver 的目的。
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
# 隐藏 正在受到自動軟體的控制 這幾個字
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)
driver = webdriver.Chrome(executable_path=r"E:\chromedriver\chromedriver.exe", options=options)
# 修改 webdriver 值
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
})
driver.get('https://www.baidu.com')
另外如下配置也可以去除 webdriver 特征
options = Options()
options.add_argument("--disable-blink-features")
options.add_argument("--disable-blink-features=AutomationControlled")
控制已打開的浏覽器
既然使用 selenium 打開的浏覽器存在一些特定參數,那麼我們可以另辟蹊徑,直接手動打開一個真實的浏覽器,然後再使用 selenium 控制不就可以了嗎!
-
利用 Chrome DevTools 協定打開一個浏覽器,它允許客戶檢查和調試 Chrome 浏覽器
(1)關閉所有打開的 Chrome 視窗
(2)打開 CMD,在指令行中輸入指令:
# 此處 Chrome 的路徑需要修改為你本機的 Chrome 安裝位置 # --remote-debugging-port 指定任何打開的端口 "C:\Program Files(x86)\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222
若路徑正确,此時會打開一個新的 Chrome 視窗
- 使用 selenium 連接配接這個打開的 Chrome 視窗
from selenium import webdriver from selenium.webdriver.chrome.options import Options options = Options() # 此處端口需要與上一步中使用的端口保持一緻 # 其它大多部落格此處使用 127.0.0.1:9222, 經測試無法連接配接, 建議采用 localhost:9222 # 具體原因參見: https://www.codenong.com/6827310/ options.add_experimental_option("debuggerAddress", "localhost:9222") driver = webdriver.Chrome(executable_path=r"E:\chromedriver\chromedriver.exe", options=options) driver.get('https://www.baidu.com')
但是使用本方法也存在一些弊端:
浏覽器一旦啟動,selenium中對浏覽器的配置就不在生效了,例如
–-proxy-server
等,當然你也可以一開始啟動 Chrome 的時候就加上
mitmproxy中間人
mitmproxy
其實和
fiddler/charles
等抓包工具的原理有些類似,作為一個第三方,它會把自己僞裝成你的浏覽器向伺服器發起請求,伺服器傳回的 response 會經由它傳遞給你的浏覽器,你可以 通過編寫腳本來更改這些資料的傳遞,進而實作對伺服器的 “欺騙” 和對用戶端的 “欺騙”
部分網站采用單獨的 js 檔案來識别 webdriver 的結果,我們可以通過 mitmproxy 攔截識别 webdriver 辨別符的 js 檔案,并僞造正确的結果。
參考:使用 mitmproxy + python 做攔截代理
待續…
其實,不隻是 webdriver,selenium打開浏覽器後,還會有這些特征碼:
webdriver
__driver_evaluate
__webdriver_evaluate
__selenium_evaluate
__fxdriver_evaluate
__driver_unwrapped
__webdriver_unwrapped
__selenium_unwrapped
__fxdriver_unwrapped
_Selenium_IDE_Recorder
_selenium
calledSelenium
_WEBDRIVER_ELEM_CACHE
ChromeDriverw
driver-evaluate
webdriver-evaluate
selenium-evaluate
webdriverCommand
webdriver-evaluate-response
__webdriverFunc
__webdriver_script_fn
__$webdriverAsyncExecutor
__lastWatirAlert
__lastWatirConfirm
__lastWatirPrompt
...
如果你不相信,我們可以來做一個實驗,分别使用 正常浏覽器,
selenium+Chrome
,
selenium+Chrome headless
打開這個網址:https://bot.sannysoft.com/
當然,這些例子并不是為了打擊各位的自信,僅僅是希望大家不要學會了部分技術就開始沾沾自喜,時刻保持一顆赤子之心,懷着對技術的熱情繼續前進。爬蟲與反爬蟲這場沒有硝煙的戰争,還在繼續 …