又到年末了,羣裏不少朋友說要開始備戰2020金三銀四,其實,我建議是,若是你不是技術大牛,就不要去湊熱鬧。javascript
其實,如今(11,12月份)就是最佳換工做的時候,由於不少人想等着拿了年終再走,雖然招聘的公司很少,可是找工做的更少,因此,能夠去試試有沒有好機會。java
不少人說要去面試自動化,問我有沒有面試題,而好點的公司都喜歡問底層原理。python
想必用過selenium的測試朋友都知道18種定位方式吧?web
若是你去面試自動化,面試官估計會問你webdriver client的原理,由於這樣才能看出你自動化的深度,這須要去研究下源碼。面試
懵逼?別急,下面我就結合源碼給你們簡單介紹下(看主要源碼便可,原本想畫個調用流程圖,可是着實精力有限)chrome
我用的selenium版本是__version__ = '3.0.1'瀏覽器
寫一個簡單的樣例服務器
from selenium import webdriver driver = webdriver.Chrome() driver.get('https://www.cnblogs.com/uncleyong/') driver.close() # 關閉當前窗口,可是沒結束進程 driver.quit()
運行示例,成功打開目標頁面session
打開selenium的目錄結構,webdriver下有各類客戶端瀏覽器,好比chrome,firefox,ie等源碼分析
那webdriver client的原理是什麼呢?回到下面的示例,點擊下圖第2行Chrome
下面第20行能夠看到,Chrome是一個別名
點擊上圖第20行WebDriver
下圖能夠看到,WebDriver類繼承了RemoteWebDriver類,RemoteWebDriver類是as的別名,是另一個py文件中WebDriver類的別名
下面這個WebDriver類是在seleniumwebdriver.chrome下的webdriver.py中
這個類的註釋:Controls the ChromeDriver and allows you to drive the browser,含義是:控制ChromeDriver並容許驅動瀏覽器。
實例化WebDriver(也就是執行webdriver.Chrome())的時候,過程當中會先啓動chromedriver,而後調用父類RemoteWebDriver的__init方法
上圖57行實例化Service類,點擊這個類
點擊上圖35行free_port
下面是獲取到一個可用的端口
執行下圖62行self.service.start()後,啓動了chromedriver
點擊上圖62行start
可用看到,subprocess調用命令行打開webdriver.exe
也就是selenium啓動了chromedriver,能夠看到對應的進程
咱們把獲取到的端口打印出來
要執行了self.service.start()後,啓動了chromedriver,下面命令纔有結果
咱們來看下RemoteWebDriver這個父類,點擊下圖65行RemoteWebDriver
名稱仍是WebDriver,注意,只要不在同一個py文件,類名是能夠相同的,可是若是其中一個要引用另一個,另一個要as取別名,如上圖中from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver
這個類的註釋:Controls a browser by sending commands to a remote server,含義是:經過向遠程服務器發送命令來控制瀏覽器。
來看其__init__方法,
下圖58行__init__中參數command_executor的默認值是http://127.0.0.1:4444/wd/hubremote,
下圖86行建立RemoteConnection類對象也須要參數command_executor,說明是鏈接remote server的URL,RemoteConnection類是負責與Remote WebDriver server鏈接的類
點擊上圖92行start_session,
能夠看到,下圖180行生成sessionId
點擊上圖179行execute
點擊上圖234行execute
command表示執行的命令
點擊上圖407行command_info
點擊上圖402行_commands,打開self._commands這個dict,下圖199行就是咱們前面的發送請求的命令Command.NEW_SESSION,發送一個路徑爲/session的post請求,生成sessionId,返回響應
回到下圖,點擊407行_request
在_request方法中,try下面第一行將打開目標瀏覽器,並綁定到指定端口,綁定完成後,該啓動的瀏覽器實例就作爲WebDriver的remote server
resp是一個返回的對象
後面執行resp.read(),獲取到對象內的data數據,debug模式下,可用看到,data中包括已經生成的sessionId
另外,WebDriver類是在selenium.webdriver.remote下的webdriver.py文件中
回到咱們的示例腳本
點擊上方第3行get
上圖248行url是咱們傳的參數,點擊上圖248行GET,經過下圖可知Command是一個類,GET是類屬性
點擊上上圖248行execute
咱們直接看response後面的代碼,self對象後面是一個名爲command_executor的對象,這個對象執行了execute方法。
點擊上圖234行command_executor
下圖86行能夠看到,對象command_executor是RemoteConnection類的實例,
這個實例是在__init__方法中,說明是在建立selenium.webdriver.remote.webdriver.WebDriver類對象的時候也完成了command_executor對象的建立
點擊上圖86行RemoteConnection類
RemoteConnection類就是負責與Remote WebDriver server鏈接的類
再回到下圖,來看RemoteConnection類的方法execute
點擊上圖234行execute
command表示執行的命令
點擊上圖407行command_info
點擊上圖402行_commands,打開self._commands這個dict,
下圖206行就是咱們前面的發送請求的命令Command.GET,經過sessionId肯定請求的瀏覽器,也就是說sessionId表示了remote server和對應瀏覽器的一個會話,指令經過這個會話變成對瀏覽器的一個操做,
remote server在執行完對瀏覽器的操做後獲得的數據將做爲響應的body返回給測試代碼。
另外,216行是經常使用的
爲了更加清晰查看請求過程當中的path、params、method、url、body值,咱們先本身寫一個mylogger文件
修改源文件remote_connection.py
點擊上圖411行_request,添加以下內容
運行示例,控制檯打印出日誌
完整版日誌:
[2019-11-12 22:49:05,821] [DEBUG] [C:\Python36\lib\site-packages\selenium\webdriver\remote\remote_connection.py : execute:408 , 【path】:/session ----- 【params】:{'desiredCapabilities': {'browserName': 'chrome', 'version': '', 'platform': 'ANY', 'javascriptEnabled': True, 'chromeOptions': {'extensions': [], 'args': []}}, 'requiredCapabilities': {}}
[2019-11-12 22:49:05,821] [DEBUG] [C:\Python36\lib\site-packages\selenium\webdriver\remote\remote_connection.py : _request:428 , 【method】:POST +++++ 【url】:http://127.0.0.1:21753/session +++++ 【body】:{"desiredCapabilities": {"browserName": "chrome", "version": "", "platform": "ANY", "javascriptEnabled": true, "chromeOptions": {"extensions": [], "args": []}}, "requiredCapabilities": {}}
[2019-11-12 22:49:07,882] [DEBUG] [C:\Python36\lib\site-packages\selenium\webdriver\remote\remote_connection.py : execute:408 , 【path】:/session/80c0d4efaeea62ae67c11a9ae3c656fd/url ----- 【params】:{'url': 'https://www.cnblogs.com/uncleyong/', 'sessionId': '80c0d4efaeea62ae67c11a9ae3c656fd'}
[2019-11-12 22:49:07,882] [DEBUG] [C:\Python36\lib\site-packages\selenium\webdriver\remote\remote_connection.py : _request:428 , 【method】:POST +++++ 【url】:http://127.0.0.1:21753/session/80c0d4efaeea62ae67c11a9ae3c656fd/url +++++ 【body】:{"url": "https://www.cnblogs.com/uncleyong/", "sessionId": "80c0d4efaeea62ae67c11a9ae3c656fd"}
經過debug調試可知, 執行2-5行代碼時,driver_command的值分別爲:newSession、get、close、quit
上面日誌中,
前兩行是執行response = self.execute(Command.NEW_SESSION, capabilities)產生的日誌
後兩行是執行self.execute(Command.GET, {'url': url})產生的日誌
還原上面第2-3行過程
手動執行chromedriver.exe,模擬selenium先啓動了chromedriver(不要手動關閉)
模擬上面日誌中第一個post請求,browserName是chrome,表示告訴remote driver打開chrome瀏覽器
打開了瀏覽器(不要手動關閉)
響應中,返回了sessionId,後面和瀏覽器的交互都是基於這個sessionId進行的
模擬上面日誌第二個post請求,sessionId替換爲上面返回的值:8832915b0bc0151aab6573ab33b4be93
瀏覽器展現了咱們的目標url頁面內容
響應status等於0,即表示命令執行成功
看懂上面的分析,你應該知道結論了吧?
若是仍是迷糊,那請關注公衆號「全棧測試筆記」,回覆「webdriver」獲取結論。