WebDriver的工作原理及常用方法大全
WebDriver的工作原理
在我們new一個(gè)WebDriver的過(guò)程中,Selenium首先會(huì)確認(rèn)瀏覽器的native component是否存在可用而且版本匹配。接著就在目標(biāo)瀏覽器里啟動(dòng)一整套Web Service(實(shí)際上就是瀏覽器廠商提供的driver, 比如IEDriver, ChromeDriver,它們都實(shí)現(xiàn)了WebDriver's wire protocol.),這套Web Service使用了Selenium自己設(shè)計(jì)定義的協(xié)議,名字叫做The WebDriver Wire Protocol。
WebDriver Wire協(xié)議是通用的,也就是說(shuō)不管是FirefoxDriver還是ChromeDriver,啟動(dòng)之后都會(huì)在某一個(gè)端口啟動(dòng)基于這套協(xié)議的Web Service。例如FirefoxDriver初始化成功之后,默認(rèn)會(huì)從http://localhost:7055開(kāi)始,而ChromeDriver 則大概是http://localhost:46350之類(lèi)的。
接下來(lái),我們調(diào)用WebDriver的任何API,都需要借助一個(gè) ComandExecutor發(fā)送一個(gè)命令,實(shí)際上是一個(gè)HTTP request給監(jiān)聽(tīng)端口上的Web Service。在我們的HTTP request的body中,會(huì)以WebDriver Wire協(xié)議規(guī)定的JSON格式的字符串來(lái)告訴Selenium我們希望瀏覽器接下來(lái)做什么事情。
WebDriver 啟動(dòng)目標(biāo)瀏覽器,并綁定到指定端口。該啟動(dòng)的瀏覽器實(shí)例,做為web driver的remote server
Client 端通過(guò)CommandExcuter 發(fā)送HTTPRequest 給remote server 的偵聽(tīng)端口(通信協(xié)議:the webriver wire protocol)
Remote server 需要依賴原生的瀏覽器組件(如:IEDriver.dll,chromedriver.exe),來(lái)轉(zhuǎn)化轉(zhuǎn)化瀏覽器的native調(diào)用。
那么remoteserver端的這些功能是如何實(shí)現(xiàn)的呢?答案是瀏覽器實(shí)現(xiàn)了webdriver的統(tǒng)一接口,這樣client就可以通過(guò)統(tǒng)一的restful的接口去進(jìn)行瀏覽器的自動(dòng)化操作。目前webdriver支持ie, chrome, firefox, opera等主流瀏覽器,其主要原因是這些瀏覽器實(shí)現(xiàn)了webdriver約定的各種接口。
可以更通俗的理解:由于客戶端腳本(java,python,ruby)不能直接與瀏覽器通信,這時(shí)候可以把WebService當(dāng)做一個(gè)翻譯器,它可以把客戶端代碼翻譯成瀏覽器可以識(shí)別的代碼(比如js).客戶端 (也就是測(cè)試腳本)創(chuàng)建1個(gè)session,在該session中通過(guò)http請(qǐng)求向WebService發(fā)送restful的請(qǐng)求,WebService翻譯成瀏覽器懂得腳本傳給瀏覽器,瀏覽器把執(zhí)行的結(jié)果返回給WebService,WebService把返回的結(jié)果做了一些封 裝(一般都是json格式),然后返回給client,根據(jù)返回值就能判斷對(duì)瀏覽器的操作是不是執(zhí)行成功
摘自官網(wǎng)對(duì)于chrome driver的描述:
The ChromeDriver consists of three separate pieces. There is the browser itself ("chrome"), the language bindings provided by the Selenium project ("the driver") and an executable downloaded from the Chromium project which acts as a bridge between "chrome" and the "driver". This executable is called "chromedriver", but we'll try and refer to it as the "server" in this page to reduce confusion.
大概意思就是我們下載的chrome可執(zhí)行文件(.exe)是為作為瀏覽器與client(language binding)橋梁的作用,也更印證了對(duì)于Web Service(driver)的理解。
舉個(gè)實(shí)際的例子:
WebDriver diver = new FirefoxDriver();
driver.get("http://google.com");
在執(zhí)行 driver.get("http://google.com"); 這句代碼時(shí),client也就是我們的測(cè)試代碼向Web Service(remote server)發(fā)送了如下的請(qǐng)求:
POST session/285b12e4-2b8a-4fe6-90e1-c35cba245956/url
post_data {"url":"http://google.com"}
通過(guò)post的方式請(qǐng)求localhost:port/hub/session/session_id/url地址,請(qǐng)求瀏覽器完成跳轉(zhuǎn)url的操作。
如果上述請(qǐng)求是可接受的,或者說(shuō)Web Service是實(shí)現(xiàn)了這個(gè)接口,那么Web Service會(huì)跳轉(zhuǎn)到該post data包含的url,并返回如下的
response
{"name":"get","sessionId":"285b12e4-2b8a-4fe6-90e1-c35cba245956","status":0,"value":""}
該response中包含如下信息 name:Web Service端的實(shí)現(xiàn)的方法的名稱,這里是get,表示跳轉(zhuǎn)到指定url;sessionId:當(dāng)前session的id;status:請(qǐng)求執(zhí)行的狀態(tài)碼,非0表示未正確執(zhí)行,這里是0,表示一切ok不必?fù)?dān)心;value:請(qǐng)求的返回值,這里返回值為空,如果client調(diào)用title接口,則該值應(yīng)該是當(dāng)前頁(yè)面的title;
如果client發(fā)送的請(qǐng)求是定位某個(gè)特定的頁(yè)面元素,則response的返回值可能是這樣的:
{"name":"findElement","sessionId":"285b12e4-2b8a-4fe6-90e1-c35cba245956","status":0,"value":{"ELEMENT":"{2192893e-f260-44c4-bdf6-7aad3c919739}"}}
name,sessionId,status跟上面的例子是差不多的,區(qū)別是該請(qǐng)求的返回值是ELEMENT:{2192893e- f260-44c4-bdf6-7aad3c919739},表示定位到元素的id,通過(guò)該id,client可以發(fā)送如click之類(lèi)的請(qǐng)求與 server端進(jìn)行交互。
WebDriver的常用方法
日常操作
from selenium import webdriver
from time import sleep
# 創(chuàng)建谷歌瀏覽器對(duì)象
chrome_driver = webdriver.Chrome()
# 訪問(wèn)百度
chrome_driver.get("http://www.baidu.com")
# 最大化窗口
chrome_driver.maximize_window()
# 訪問(wèn)檸檬班
chrome_driver.get("https://lemon.ke.qq.com/")
# 后退
chrome_driver.back()
# 前進(jìn),要有歷史記錄
chrome_driver.forward()
# 刷新
chrome_driver.refresh()
# 等待10s,更明顯看出效果
sleep(10)
# 關(guān)閉窗口,關(guān)閉當(dāng)前窗口
chrome_driver.close()
# 等待10s,更明顯看出效果
sleep(10)
# 關(guān)閉會(huì)話,關(guān)閉瀏覽器,關(guān)閉driver
chrome_driver.quit()
鼠標(biāo)事件
from selenium import webdriver
# 引入 ActionChains 類(lèi)
from selenium.webdriver.common.action_chains import ActionChains
driver = webdriver.Chrome() driver.get("https://www.baidu.cn")
# 定位到要懸停的元素
above = driver.find_element_by_link_text("設(shè)置") # 對(duì)定位到的元素執(zhí)行鼠標(biāo)懸停操作
ActionChains(driver).move_to_element(above).perform()
'''
perform(): 執(zhí)行操作
context_click(): 右擊;
double_click(): 雙擊;
drag_and_drop(): 拖動(dòng);
move_to_element(): 鼠標(biāo)懸停
'''
鍵盤(pán)事件
from selenium import webdriver
# 引入 Keys 模塊
from selenium.webdriver.common.keys import Keys
driver = webdriver.Chrome ()
driver.get("http://www.baidu.com")
#輸入框輸入內(nèi)容
driver.find_element_by_id ("kw").send_keys("seleniumm")
#刪除多輸入的一個(gè)m
driver.find_element_by_id ("kw").send_keys(Keys.BACK_SPACE)
'''
刪除鍵(BackSpace)send_keys(Keys.BACK_SPACE)
空格鍵 (Space)send_keys(Keys.SPACE)
制表鍵(Tab) send_keys(Keys.TAB)
回退鍵(Esc)send_keys(Keys.ESCAPE)
回車(chē)鍵(Enter)send_keys(Keys.ENTER)
全選(Ctrl+A)send_keys(Keys.CONTROL,‘a(chǎn)’)
復(fù)制(Ctrl+C)send_keys(Keys.CONTROL,‘c’)
剪切(Ctrl+X)send_keys(Keys.CONTROL,‘x’)
粘貼(Ctrl+V)send_keys(Keys.CONTROL,‘v’)
鍵盤(pán) F1 send_keys(Keys.F1)
鍵盤(pán) F12send_keys(Keys.F12)
'''
上傳文件
driver.find_element_by_id("albumUpload").send_keys("文件路徑")
#通過(guò)模擬鍵盤(pán)敲擊上傳
from selenium.webdriver.common.action_chains import ActionChains
from selenium import webdriver
import win32com.client # python -m pip install pypiwin32
ActionChains(driver).click(driver.find_element_by_id("albumUpload")).perform()
sh = win32com.client.Dispatch("WScript.shell")
time.sleep(3)
# 1、代碼不聯(lián)想 2、輸入法要保持英文輸入狀態(tài) 3、無(wú)法處理中文
sh.Sendkeys("文件路徑\r\n")
'''
上傳多個(gè)文件
通過(guò)輸入文件路徑上傳
"文件1路徑\n文件2路徑"
通過(guò)模擬鍵盤(pán)上傳
'"文件1路徑" "文件2路徑\r\n"'
'''
內(nèi)嵌網(wǎng)頁(yè)iframe切換
# 定位到內(nèi)嵌網(wǎng)頁(yè)
ele = driver.find_element_by_css_selector("[class=\"ke-edit-iframe\"]")
# 切入內(nèi)嵌網(wǎng)頁(yè)中
driver.switch_to.frame(ele)
driver.find_element_by_css_selector("[class=\"ke-content\"]").send_keys("123")
# 切入內(nèi)嵌網(wǎng)頁(yè)后,若想再操作內(nèi)嵌網(wǎng)頁(yè)外的元素,需要再切出來(lái)
driver.switch_to.default_content()
瀏覽器標(biāo)簽頁(yè)切換
win_sli = driver.window_handles # 獲取當(dāng)前所有標(biāo)簽頁(yè)的句柄
# 循環(huán)所有標(biāo)簽,直到找到標(biāo)題、url、頁(yè)面元素相符的標(biāo)簽,就停止切換,停留在當(dāng)前標(biāo)簽頁(yè)
for win in win_sli:
driver.switch_to.window(win)
# 如果標(biāo)簽頁(yè)的標(biāo)題不固定,還可以用網(wǎng)址判斷
# 如果網(wǎng)址和標(biāo)題都不固定,可以找一個(gè)目標(biāo)標(biāo)簽頁(yè)獨(dú)有的元素,然后判斷元素是否存在
if driver.title == "OPMS-項(xiàng)目管理軟件+OA管理軟件+CRM管理軟件":
break
'''
current_window_handle:獲得當(dāng)前標(biāo)簽頁(yè)句柄
window_handles:返回所有便簽頁(yè)的句柄
switch_to.window(標(biāo)簽頁(yè)句柄):切換到對(duì)應(yīng)的標(biāo)簽頁(yè)
關(guān)閉標(biāo)簽頁(yè)使用 close 方法
'''
頁(yè)面滾動(dòng)
driver.execute_script(“window.scrollBy(0,500)”)
driver.execute_script(“arguments[0].scrollIntoView();”, ele)
'''
window.scrollBy(0,500) 向下滾動(dòng)500個(gè)像素
window.scrollBy(0,-500) 向上滾動(dòng)500個(gè)像素
window.scrollBy(500,0) 向右滾動(dòng)500個(gè)像素
window.scrollBy(-500,0) 向左滾動(dòng)500個(gè)像素
'''
截圖
# 截圖,截取全屏,參數(shù)為要保存圖片的文件路徑,官方建議用png格式
# 如果想截取登陸以后的界面:1、sleep 2、尋找一個(gè)登錄后才有的元素
driver.get_screenshot_as_file("./a.png")
# 對(duì)元素進(jìn)行截圖
ele = driver.find_element_by_css_selector("[class=\"nav nav-pills nav-stacked custom-nav js-left-nav\"]")
ele.screenshot("./b.png")
警告框處理
# 獲取對(duì)話框?qū)ο?br>al = driver.switch_to.alert
# 確認(rèn)對(duì)話框
al.accept()
'''
text:返回 alert/confirm/prompt 中的文字信息
accept():接受現(xiàn)有警告框
dismiss():取消現(xiàn)有警告框
send_keys(“haha”):發(fā)送文本至警告框
'''
作者: Python測(cè)試社區(qū)
歡迎關(guān)注微信公眾號(hào) :Python測(cè)試社區(qū)