python爬蟲 - 配置強有力的js繞過神器-selenium grid分布式集群


一,前期說明

 

什么是selenium grid,它就是selenium的三大控件之一,三大控件就是selenium WebDriver,selenium Grid,selenium IDE

 

selenium IDE:負責錄制,回放腳本,模擬用戶對頁面的真是操作

selenium WebDriver:提供強大的瀏覽器APi操作,覆蓋瀏覽器有:chrome,firefox, microsoft edage,safari,ie等

selenium Grid:用於分布式自動化測試,通過控制多台機器,多個瀏覽器並行執行測試用例的

 

說白了就是selenium grid就是用來做selenium 分布式集群的,方便控制,不用每個項目或者每個瀏覽器操作腳本就重寫一套selenium代碼,然后各自執行,到后面把項目部署到服務器上,一個一個部署,那確實有點累

 

 

 

二,環境部署准備

沒什么好准備的,直接用docker鏡像,當然你要想在本地搭建一個或者不喜歡用docker,也可以,去selenium官網下載工具:點我

 

 

 

 

 

 

我這里就還是用docker了,安裝docker過程就省略了

 

拉取鏡像:

 

1.拉取hub鏡像

docker pull selenium/hub

這里的hub你可以理解為master或者server端,它自己不會作為執行端,而是調度端,用於調度各個node節點

2.拉取node鏡像

docker pull selenium/node-chrome

這里的node你可以理解為salve或者client端,作為執行端

 

然后,因為目前的話,node鏡像有很多種:

 

 

 

 

 

我這里就選用chrome版了,看你們個人喜好選用,另外,由於我喜歡實時的看到操作界面,所以我選用了selenium/node-chrome-debug,帶有debug字眼的

docker pull selenium/node-chrome-debug

 

3.查看開啟可用端口

查看當前開啟的端口:

firewall-cmd --list-ports

 

 

 

涉及安全隱私,我就不給圖了

 

再看dokcer里有多少已經用過的端口:

docker ps

 

涉及安全隱私,也不給圖了

 

看到都用到35005上了,那么我們就從35006開始用,先開啟端口:

 

firewall-cmd --add-port=35000-35010/tcp --permanent

 

ok,后面就開始部署了

 

開始部署

1.啟動hub節點

 

如果你選用的不是docker而是之前在官網下載的jar包的話,要這么啟動,前提需要安裝好java的jdk包才行:

 

java -jar selenium-server-standalone-3.141.59.jar -role hub -port 5566

 

docker啟動hub節點:

 

docker run -p 35006:4444 -p 35007:5900 -d --name hub selenium/hub

 

2.啟動node節點:

 

用jar包文件啟動在本地

java -jar selenium-server-standalone-3.141.59.jar -role node -hub http://localhost:5566/grid/register/ -port 5577

 

在docker里啟動:

 

docker run -d -p 35008:5900 --link hub:hub --name selenium-node1 -e "NODE_APPLICATION_NAME=test1"  selenium/node-chrome-debug

 

說明下:

  • -d意思就是在后台啟動,
  • -p就是指定端口映射,
  • –link就是連接到hub,如果連接遠程的話,就是 --link hub:遠程地址
  • --name 為啟動的容器取名
  • -e 就是指為這個鏡像設置的參數(每個鏡像所需參數不一樣),然后這里設置下node節點的名字為test1
  • 最后的selenium/node-chrome-debug就是指我要指定這個鏡像來啟動

 

然后再啟動一個節點,容器名為selenium-node2,節點名為test2

docker run -d -p 35009:5900 --link hub:hub --name selenium-node2 -e "NODE_APPLICATION_NAME=test2"  selenium/node-chrome-debug

 

 

當然,你如果不指定以上的參數就是用的默認的設置

 

 

以上啟動完了之后,查看:docker ps:

 

 

 

 

三:訪問測試

 

打開web地址: <你的服務器ip>:<port>/grid/console  用瀏覽器打開看看:

 

 

 

 

 

 

查看配置信息:

 

 

 

 

上面的test1和test2就是我們剛才啟動node節點時設置的名字了,說明是設置上了的,然后看到默認的額timeout是1800秒,即30分鍾

 

 

同時可以看看docker的日志:

docker logs hub:(這里的hub是容器名)

 

 

 

 

 

 

 

 

同時,你也可以用vnc查看,但是我用的vnc viewer老是連不上,最后用了novnc連接成功:

這個,打開的時候會讓你輸入密碼,這個密碼是docker的鏡像默認的,也就是【secret】,輸入之后就會展示下面的界面

 

 

 

 

 

然后node2也是一樣的,就不展示了,現在我們要用代碼控制了:

 

# coding=utf-8
import time from selenium import webdriver chrome_capabilities = { "browserName": "chrome", "version": "", "platform": "ANY", "javascriptEnabled": True, 'applicationName': 'test1' # 這里指定節點名啟動,如果不指定hub則隨機選擇空閑的node啟動
} browser = webdriver.Remote("http://<你的服務端ip>:<你剛才映射的端口>/wd/hub", desired_capabilities=chrome_capabilities) print(123123123, browser) browser.get("http://www.baidu.com") print(browser.title) time.sleep(5) browser.get_screenshot_as_file("8.png") browser.quit()

 

 
        

 

注意后面的/wd/hub這個是固定的啊,然后desired_capabilities可以設置的東西不多,也就上面那幾個比較主流點

 

執行,

 

 

同時看到vnc端已經自動展示了:

 

 

 

然后也按照設定的5秒自動關閉退出

 

截圖的圖片也確實是有的:

 

 

然后啟動test2的node也相同的,就把desired_capabilities里的applicationName的值改成test2就可以指定名為test2的node執行了,就不展示了

 

四,指定node重開,關閉

以上的操作,並不能很滿足我們的需求,假如代碼中途報錯意外終止,而瀏覽器窗口沒有正常關閉咋辦,此時到默認設置的timeout時間段內,該進程一直是在占用資源的,假如后面實際運用起來有很多個node,很多套代碼各自執行的話,是很不好管理的,所以就需要指定手動關閉一個窗口,這種怎么處理呢

 

我查閱大量的資料,都沒有直接說怎么關閉,基本都是說的stop掉那個docker容器然后再開,那假如node節點很多,我都不知道哪些正常執行,哪些意外終止呢?怎么處理?

 

所以,這里需要指定關閉,那怎么關呢,用session id來操作,因為剛才細心的朋友應該看到了,我剛才代碼執行的時候,直接打印了下browser對象,它把session顯示出來了:

 

 

 

我拿到這個我就知道是哪個窗口了,說明下,這里的session可能不是你理解的類似cookie那種的session會話對象,這個是selenium的一個屬性,你可以這么理解,我啟動一個selenium出來的瀏覽器窗口,那么它就是一個session,即使這個窗口可能開了多個標簽,那它也是一個session。

 

那么,我們可以把這個session重新復制給啟動的webdriver對象,不就可以關閉了嗎?

 

 

 

 

但是運行你就發現,之前的異常導致沒有正常關閉的窗口還在,它去另開了一個窗口

 

這個思路不對嗎,對了一半,距離實現真的就差一點,為啥呢,看webdriver.Remote的源碼:

 

當初始化webdriver對象時,session_id是空的:

 

 

進到start_session方法里:

發現里面其實調用了execute方法,同時新創建了一個session對象,

 

 

 

 

 

什么意思呢,意思就是,我直接賦值覆蓋調session_id是沒有用的,因為對象一初始化的時候就已經創建好了一個新的session,也就是一個新的窗口了,根本沒法利用之前的session id,那我拿到session id之后可以直接關閉嗎,

 

 

不行,session_id之后沒有方法或屬性可用了

 

怎么辦,改寫start_session方法:

 

 

from selenium import webdriver from selenium.common.exceptions import (InvalidArgumentException, WebDriverException, NoSuchCookieException) from selenium.webdriver.chrome.options import Options import time class ReuseChrome(webdriver.Remote): def __init__(self, command_executor, session_id): self.rewrite_session_id = session_id webdriver.Remote.__init__(self, command_executor=command_executor, desired_capabilities={}) def start_session(self, capabilities, browser_profile=None): """ 重寫start_session方法 """
if not isinstance(capabilities, dict): raise InvalidArgumentException("Capabilities must be a dictionary") if browser_profile: if "moz:firefoxOptions" in capabilities: capabilities["moz:firefoxOptions"]["profile"] = browser_profile.encoded else: capabilities.update({'firefox_profile': browser_profile.encoded}) self.capabilities = Options().to_capabilities() self.session_id = self.rewrite_session_id self.w3c = False def get(session_id): driver = ReuseChrome(command_executor='http://<你的服務端ip>:<你剛才映射的端口>/wd/hub', session_id=session_id) driver.session_id = session_id driver.get("http://www.baidu.com") # 打印current_url為百度的地址,說明復用成功
print(driver.title) time.sleep(3) driver.quit() get('7696c3eb43f97438229f6763242fd8b9')  # 請用實際的session id,如果一個不存在的也會報錯

 

 

 

以上,定義了一個類,繼承了webdriver.Remote,然后改寫了start_session方法,讓它不要去重新創建對象

 

好的,現在執行測試:

 

還是剛才的執行node的代碼,然后停頓300秒模擬程序異常導致沒法立即關閉

 

 

 

 

 

 

 

查看管理界面:

左邊這個有點灰色,說明是在執行中

 

 

 

鼠標放上去還會有提示:

 

 

 

 

現在我們執行關閉的腳本,手動傳入剛才的session id   3fc7ff8a159fdb4246210d6c29cf39d7

 

 

vnc看到打開了163:

 

 

 

過了3秒,關閉了:

 

 

 

 

同時這邊的管理界面也沒有在執行中的狀態:

 

 

 

 

同時這邊剛才模擬執行異常這邊還在執行

 

 

過段時間執行完后會報錯,這個不打緊,意思就是沒有正常關閉的意思,這個是我模擬sleep的,如果是真是的異常,估計都看不到報錯是啥了,得去docker里看日志了

 

 

 

五.為node設置maxsession和超時時間

以上的步驟已經能解決大部分的問題了,但是還是有兩個場景沒有實現,就是,以上的,一個node只能開一個session,比如下面:

 

我啟動了多次腳本,然后因為可用的session不夠,已經超額量的執行,導致阻塞等待了:

 

 

 

 

管理界面也顯示有5個請求還在等待處理

 

 

那怎么處理呢?

開多個node?這個是可以,但是這是典型的以磁盤空間來換的,那后期有上百個咋搞呢?而且回到上面說的時間問題,此時,我已經不知道這些session id分別是哪些了,我就算手動的關閉,也是一個一個關閉,它默認的超時時間又是30分鍾,我得等這么久才能操作,咋辦呢?

這個,目前的話,真的就只能停止docker容器再執行了,因為上面的兩個問題只有在創建docker容器,注冊到hub時才能設置的,現在已經到這后面了,沒法改了

 

所以,停止docker重來吧:

 

1.重設置docker容器

 

鏡像不用再拉了,只是啟動hub和node重新搞下就行:

 

docker run -p 35006:4444 -p 35007:5900 -d --name hub  -e "GRID_TIMEOUT=30"  selenium/hub

docker run -d -p 35008:5900 --link hub:hub --name selenium-node1 -e "NODE_APPLICATION_NAME=test1" -e "NODE_TIMEOUT=30" -e "NODE_MAX_SESSION=5" selenium/node-chrome-debug

 

說明下,GRID_TIMEOUT是指對hub設置超時時間30秒的處理

NODE_TIMEOUT是指對node節點設置超時時間

NODE_MAX_SESSION是指設置最大的session數量,也就是我們可以在一個節點里開多個窗口而不會被阻塞住了

 

進入管理界面查看:

 

 

 

 

目前來看,應該是設置成功了的

 

2.測試超時時間

現在再看剛才哪個腳本,就是模擬異常停頓了300秒那個:

 

先看,目前是空閑狀態:

 

 

 

 

 

 

 

 

 

 

 

 

 

等待30秒,看它會不會自動變成空閑狀態

 

這個管理界面有時候會如此抽風,不用管,多刷幾次即可

 

 

 

 

出來了,果真變成空閑狀態了

 

 

 

 

去docker鏡像里看看日志,注意左邊我框出來的時間,從9點52分的28秒到53分的04秒的樣子自己刪除了這個session,為什么不是定死的30秒呢?selenium調起瀏覽器,再關閉,肯定還是需要一點時間准備資源和釋放資源的,這幾秒的時間誤差其實對我們的項目影響並不大的

 

 

 

 

 

而且這個session id跟上面腳本執行打印出來的id對得上,說明就是它了,沒問題,實現了自動關閉

 

3.測試最大的sessoin

 

這次還是用的節點test2,因為之前test1執行了一次,為了方便查看日志,這里用test2

現在節點test2還是空的,結尾提示的才入冊到hub上

 

 

 

 

然后使用多線程開了多個來模擬后期被多個項目調用時的場景

 

 

 

運行

 

 

 

管理界面就立即有了5個請求,為什么是5個啊,因為我們設置的maxSession=5就是同時最多有5個執行的意思了

 

 

這樣看不太出來,看看日志:

現在執行有3個了,

 

 

 

管理界面顯示還有4個:

 

 

為啥4個,我上面的range是1,8,也就是1-7,上面執行了3,還剩4個

 

再看管理界面:

 

 

 

還有一個,那日志里肯定已經執行了6個,打開看:

 

 

 

 

 

 

 

 

 

 

 

那么最后,也就是說我們設置的maxSession也確實生效了

 

 

 

補充:六.設置maxinstance和比較超時時間

 

 

 

由於容器已經啟動起來了,又只有停止刪除容器重建了

 

 

 

重建容器:

 

 

 

創建hub:

 

docker run -p 35006:4444 -p 35007:5900 -d --name hub  -e "GRID_TIMEOUT=60"  selenium/hub

 

 

 

這次我對hub設置60秒

 

 

 

創建node:

 

docker run -d -p 35008:5900 --link hub:hub --name selenium-node1 -e "NODE_APPLICATION_NAME=test1" -e "NODE_TIMEOUT=30" -e "NODE_MAX_SESSION=5" -e "NODE_MAX_INSTANCES=5" selenium/node-chrome-debug

 

docker run -d -p 35009:5900 --link hub:hub --name selenium-node2 -e "NODE_APPLICATION_NAME=test2" -e "NODE_TIMEOUT=30" -e "NODE_MAX_SESSION=5" -e "NODE_MAX_INSTANCES=5" selenium/node-chrome-debug

 

對node設置30秒

 

然后NODE_MAX_INSTANCES=5表示設置允許最多有5個實例

 

 

 

instance和session的區別:

 

參考這個答案:https://stackoverflow.com/questions/13723349/selenium-grid-maxsessions-vs-maxinstances

 

 

 

 

 

 

 

 

 

 

精簡后的意思就是說,

 

maxsession=5表示限制你的一個node節點下只能最多有5個瀏覽器實例同時執行,並且不管這5個瀏覽器是否是相同瀏覽器(如果有指定版本,相同版本)

 

maxinstances=5表示限制你的這個node節點下最多有5個相同瀏覽器(如果有指定版本,相同版本)同時執行

 

也就是說,我只設置 maxinstances=5時,我可以開5個firefox12版,5個chrome相同版,5個IE,5個Opera,此時已經就有20個瀏覽器了

 

而我設置了maxsession=5和maxinstances=5時,我就只能開5個firefox或者5個chrome或者5個opera或者2個firefox+2個chrome+1opera,還有其他的組合,這里就不每個可能都例舉出來了,這里不是數學課里的排列組合,懂意思就行了

 

 

 

對於相關的設置,如果遇到問題,可以去這里查看原因:https://github.com/SeleniumHQ/docker-selenium/issues/370

 

 

 

查看管理頁面

 

 

 

 

 

注意看node和hub的超時時間

 

 

 

 

 

 

 

 

我上面明明設置的hub才是60秒,node就是30秒啊,怎么node也變成了60秒了,這就很騷了,運行腳本測試,也果然在60秒的樣子就自動關閉了,查看日志得:started和removing的時間就在60秒左右,為什么一定不是60秒呢,前面也說過了,就不贅述了

 

 

 

 

 

 

 

 

感覺有點像是node繼承的hub的設置,我再來測試下,我這次把node設置為60秒,hub設置為30秒,如果設置出來的時間顯示是hub30秒,node30秒,那基本就是繼承的沒跑了

 

 

 

為了驗證這個是不是真的繼承了,配置如下:

 

 

 

 

 

 

 

 

 

 

管理頁面查看,確實如此,那么這個超時時間確實是node繼承hub節點的

 

 

 

 

 

 

 

那肯定是繼承的啊,官方都是說的是繼承的

 

 

 

對了,補充下兩點:

 

1.如果你把timeout設置為0,那它永遠都不會因超時而釋放了

 

2.如果你用的selenium grid的版本是3.12的話,這個超時時間timout設置是有問題的,設置的沒法生效,而且也無法繼承,看開發者回復:

 

 

 

https://github.com/SeleniumHQ/selenium/issues/5908

 

 

 

 

另外對於時間設置除了timeout還有下面兩個設置:

 

browserTimeout,這個的意思是在hub節點上,可以設置節點在瀏覽器內掛起的最長時間,因為timeout是節點在清理一個視為過期或者作廢的資源,而這個browserTimeout就是一個緩沖,比如我這里設置browserTimeout=20,那么一個會話在20秒之后還沒有正常結束且沒有再有什么操作,那就會把其置為【過期或者作廢的資源】的行列里,這時,timeout到了就會清理掉,也就是說,我設置timeout=60,browserTimeout=20,那么一個異常會話會在60+20=80秒左右才會被關閉清理掉

 

 

 

cleanUpCycle,清理機制輪詢超時時間,這里的單位時微妙,默認是5000,即5秒,也就是多久會檢查一次超時的機制,也因為又這個,我們上面的設置30秒和60秒時,加上開啟瀏覽器釋放瀏覽器資源也需要點時間,並不會一定是30或者60秒才關閉清理資源

 

因為,假如我設置的超時tmeout是30秒,沒有設置browsertimeout,不考慮瀏覽器開啟和釋放資源的造成誤差的情況下,輪詢是5秒,此時我的程序因為意外終止了,輪詢清理機制在2秒以前就執行過了,那么這個瀏覽器資源會在30秒以后並不會被清理,而會再等3秒以后,被輪詢清理機制查到然后清理掉,總共用時30+3,因為這個程序在意外終止時的前2秒輪詢清理機制已經執行過了。

 

 

 

其他的參數感興趣的可以自己研究了

 

 

 

附言:

 

另外,hub節點也可以設置GRID_MAX_SESSION相關的,這個設置之后會有什么結果,感興趣自己研究了

 

相關文檔:

 

 

 

https://www.selenium.dev/documentation/en/remote_webdriver/remote_webdriver_client/

 

https://www.selenium.dev/documentation/en/grid/grid_4/grid_endpoints/

 

https://seleniumhq.github.io/docs/grid.html

 

http://www.seleniumhq.org/docs/07_selenium_grid.jsp

 

https://github.com/SeleniumHQ/selenium/wiki/Grid2

 

https://github.com/SeleniumHQ/selenium/blob/master/java/server/src/org/openqa/grid/common/defaults/DefaultNodeWebDriver.json

 

https://github.com/SeleniumHQ/selenium/blob/master/java/server/src/org/openqa/grid/common/defaults/DefaultHub.json

 

https://github.com/SeleniumHQ/docker-selenium

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM