使用selenium模擬瀏覽器進行數據抓取無疑是當下最通用的數據採集方案,它通吃各類數據加載方式,可以繞過客戶JS加密,繞過爬蟲檢測,繞過簽名機制。它的應用,使得許多網站的反採集策略形同虛設。因爲selenium不會在HTTP請求數據中留下指紋,所以沒法被網站直接識別和攔截。html
這是否是就意味着selenium真的就沒法被網站屏蔽了呢?非也。selenium在運行的時候會暴露出一些預約義的Javascript變量(特徵字符串),例如"window.navigator.webdriver",在非selenium環境下其值爲undefined,而在selenium環境下,其值爲true(以下圖所示爲selenium驅動下Chrome控制檯打印出的值)。web
![](http://static.javashuo.com/static/loading.gif)
除此以外,還有一些其它的標誌性字符串(不一樣的瀏覽器可能會有所不一樣),常見的特徵串以下所示:chrome
- 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
- $chrome_asyncScriptInfo
- $cdc_asdjflasutopfhvcZLmcfl_
瞭解了這個特色以後,就能夠在瀏覽器客戶端JS中經過檢測這些特徵串來判斷當前是否使用了selenium,並將檢測結果附加到後續請求之中,這樣服務端就能識別並攔截後續的請求。瀏覽器
下面講一個具體的例子。app
鯤之鵬的技術人員近期就發現了一個可以有效檢測並屏蔽selenium的網站應用:大衆點評網的驗證碼錶單頁,若是是正常的瀏覽器操做,可以有效的經過驗證,但若是是使用selenium就會被識別,即使驗證碼輸入正確,也會被提示「請求異常,拒絕操做」,沒法經過驗證(以下圖所示)。async
![](http://static.javashuo.com/static/loading.gif)
分析頁面源碼,能夠找到 https://static.meituan.net/bs/yoda-static/file:file/d/js/yoda.e6e7c3988817eb17.js 這個JS文件,將代碼格式化後,搜索webdriver能夠看到以下代碼:測試
![](http://static.javashuo.com/static/loading.gif)
能夠看到它檢測了"webdriver", "__driver_evaluate", "__webdriver_evaluate"等等這些selenium的特徵串。提交驗證碼的時候抓包能夠看到一個_token參數(很長),selenium檢測結果應該就包含在該參數裏,服務端藉以判斷「請求異常,拒絕操做」。網站
如今才進入正題,如何突破網站的這種屏蔽呢?加密
咱們已經知道了屏蔽的原理,只要咱們可以隱藏這些特徵串就能夠了。可是還不能直接刪除這些屬性,由於這樣可能會致使selenium不能正常工做了。咱們採用曲線救國的方法,使用中間人代理,好比fidder, proxy2.py或者mitmproxy,將JS文件(本例是yoda.*.js這個文件)中的特徵字符串給過濾掉(或者替換掉,好比替換成根本不存在的特徵串),讓它沒法正常工做,從而達到讓客戶端腳本檢測不到selenium的效果。lua
下面咱們驗證下這個思路。這裏咱們使用mitmproxy實現中間人代理),對JS文件(本例是yoda.*.js這個文件)內容進行過濾。啓動mitmproxy代理並加載response處理腳本:
- mitmdump.exe -S modify_response.py
其中modify_response.py腳本以下所示:
-
- import re
- from mitmproxy import ctx
-
- def response(flow):
-
- if '/js/yoda.' in flow.request.url:
-
- for webdriver_key in ['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', '$chrome_asyncScriptInfo', '$cdc_asdjflasutopfhvcZLmcfl_']:
- ctx.log.info('Remove "{}" from {}.'.format(webdriver_key, flow.request.url))
- flow.response.text = flow.response.text.replace('"{}"'.format(webdriver_key), '"NO-SUCH-ATTR"')
- flow.response.text = flow.response.text.replace('t.webdriver', 'false')
- flow.response.text = flow.response.text.replace('ChromeDriver', '')
在selnium中使用該代理(mitmproxy默認監聽127.0.0.1:8080)訪問目標網站,mitmproxy將過濾JS中的特徵符串,以下圖所示:
![](http://static.javashuo.com/static/loading.gif)
經屢次測試,該方法能夠有效的繞過大衆點評的selenium檢測,成功提交大衆點評網的驗證碼錶單。