https://github.com/mozilla/geckodriver/releasesjavascript
http://npm.taobao.org/mirrors/chromedrivercss
Selenium是一個瀏覽器自動化操做框架。Selenium主要由三種工具組成。第一個工具SeleniumIDE,是Firefox的擴展插件,支持用戶錄製和回訪測試。錄製/回訪模式存在侷限性,對許多用戶來講並不適合,所以第二個工具——Selenium WebDriver提供了各類語言環境的API來支持更多控制權和編寫符合標準軟件開發實踐的應用程序。最後一個工具——SeleniumGrid幫助工程師使用Selenium API控制分佈在一系列機器上的瀏覽器實例,支持併發運行更多測試。在項目內部,它們分別被稱爲「IDE」、「WebDriver」和「Grid」。html
這裏主要介紹它的第二個工具:WebDriver。前端
官網上是這麼介紹它的:WebDriver is a clean, fast framework for automated testing of webapps. 可是我以爲它並不侷限與進行自動化測試,徹底能夠用做其它用途。java
WebDriver針對各個瀏覽器而開發,取代了嵌入到被測Web應用中的JavaScript。與瀏覽器的緊密集成支持建立更高級的測試,避免了JavaScript安全模型致使的限制。除了來自瀏覽器廠商的支持,WebDriver還利用操做系統級的調用模擬用戶輸入。WebDriver支持Firefox(FirefoxDriver)、IE (InternetExplorerDriver)、Opera (OperaDriver)和Chrome (ChromeDriver)。 它還支持Android (AndroidDriver)和iPhone (IPhoneDriver)的移動應用測試。它還包括一個基於HtmlUnit的無界面實現,稱爲HtmlUnitDriver。WebDriver API能夠經過Python、Ruby、Java和C#訪問,支持開發人員使用他們偏心的編程語言來建立測試。python
from selenium import webdriver b = webdriver.Firefox() b.get("http://www.baidu.com") b.find_element_by_id("kw").send_keys("火影") b.find_element_by_id("su").click() b.close()
在不一樣的編程語言中會有語法的差別,咱們跑去這些差別,在不一樣的語言中實現百度搜索的自動化實例都完成了下面幾個操做。android
因此,webDriver支持多種編程語言,也能夠看做是多種語言都支持WebDriver,惟一的不一樣在於不一樣語言實現的類和方法名的命名差別性。固然,這樣作的好處不言而喻:每一個人均可以根據本身熟悉的語言來使用 WebDriver 編寫自動化測試腳本。git
find_element_by_id("id")
find_element_by_name("name")
find_element_by_class_name("class")
find_element_by_tag_name("div") # 標籤
link 定位與前面介紹的幾種定位方法有所不一樣,它專門用來定位文本連接。百度輸入框上面的幾個文本連接的代碼以下:github
<a class="mnav" name="tj_trnews" href="http://www.baidu.com">新聞</a> <a class="mnav" name="tj_trhao123" href="http://www.hao123.com">hao123</a> <a class="mnav" name="tj_trmap" href="http://map.baidu.com">地圖</a> <a class="mnav" name="tj_trvideo" href="http://v.baidu.com">視頻</a> <a class="mnav" name="tj_trtieba" href="http://tieba.baidu.com">貼吧</a>
其實可使用name屬性來定位。這裏演示link定位的使用web
find_element_by_link_text("新聞") find_element_by_link_text("hao123") find_element_by_link_text("地圖") find_element_by_link_text("視頻") find_element_by_link_text("貼吧")
find_element_by_link_text("文本") 方法經過元素標籤對之間的文本信息來定位元素。
partial link 定位是對 link 定位的一種補充,有些文本連接會比較長,這個時候咱們能夠取文本連接的一部分定位,只要這一部分信息能夠惟一地標識這個連接。
<a class="mnav" name="tj_lang" href="#">一個很長很長的文本連接</a>
能夠以下定位:
find_element_by_partial_link_text("一個很長的") find_element_by_partial_link_text("文本連接")
find_element_by_partial_link_text()方法經過元素標籤對之間的部分文本信息來定位元素。
前面介紹的幾種定位方法相對來講比較簡單,理想狀態下,在一個頁面當中每個元素都有一個惟一id和name屬性值,咱們能夠經過它們的屬性值來找到它們。但在實際項目中並不是想象得這般美好,有時候一個元素並無id、name屬性,或者頁面上多個元素的id和name屬性值相同,又或者每一次刷新頁面,id值都會隨機變化。怎麼辦?能夠用Xpath與CSS定位。
可參考:http://www.w3school.com.cn/xpath/index.asp
XPath是一種在XML文檔中定位元素的語言。由於HTML能夠看作XML的一種實現,因此selenium用戶可使用這種強大的語言在web應用中定位元素。
絕對路徑定位
XPath 有多種定位策略,最簡單直觀的就是寫出元素的絕對路徑。
參考baidu.html前端工具所展現的代碼,咱們能夠經過下面的方式找到百度輸入框和搜索按鈕。
find_element_by_xpath("/html/body/div/div[2]/div/div/div/from/span/input") find_element_by_xpath("/html/body/div/div[2]/div/div/div/from/span[2]/input")
find_element_by_xpath()方法使用XPath語言來定位元素。XPath主要用標籤名的層級關係來定位元素的絕對路徑,最外層爲html語言。在body文本內,一級一級往下查找,若是一個層級下有多個相同的標籤名,那麼就按上下順序肯定是第幾個,例如,div[2]表示當前層級下的第二個div標籤。
利用元素屬性定位
除了使用絕對路徑外,XPath 也可使用元素的屬性值來定位。一樣以百度輸入框和搜索按鈕爲例:
find_element_by_xpath("//*[@id='kw']") #注意外層 " 符號和內層 ' 符號 find_element_by_xpath("//*[@id='su']")
//表示當前頁面某個目錄下,input 表示定位元素的標籤名,[@id="kw"]表示這個元素的 id 屬性值等於 kw。下面經過name和class屬性值來定位。
瀏覽器開發者工具F12,複製,XPath
層級與屬性結合
若是一個元素自己沒有能夠惟一標識這個元素的屬性值,name咱們能夠找其上一級元素,若是它的上一級元素有能夠惟一標識屬性的值。也能夠拿來使用。
find_element_by_xpath('//[@id="cnblogs_post_body"]/p[1]/a[1]')
使用邏輯運算符
若是一個屬性不能惟一地區分一個元素,咱們還可使用邏輯運算符連接多個屬性來查找元素。
<input type="text" id="kw" class="su" name="ie"> <input type="text" id="kw" class="aa" name="ie"> <input type="text" id="bb" class="su" name="ie">
如上面的三行元素,假設咱們如今要定位第一行元素,若是使用 id 將會與第二行元素重名,若是使用 class 將會與第三行元素重名,若是同時使用 id 和 class 就會惟一地標識這個元素,這個時候就能夠經過邏輯運算符 「and」 來鏈接兩個條件。
固然,咱們也能夠用「and」鏈接更多的屬性來惟一地標識一個元素。
find_element_by_xpath('//input[@id="kw" and @class="su"]/span/input')
CSS 是一種語言,它用來描述HTML和XML文檔的表現。CSS使用選擇器來爲頁面元素綁定屬性。這些選擇器能夠被Selenium用做另外的定位策略。
CSS能夠較爲靈活地選擇空間的任意屬性,通常狀況下定位速度要比XPath快,但對於初學者來講學習起來稍微有點難度。
(1) 經過 class 屬性定位
find_element_by_css_selector(".s_ipt") find_element_by_css_selector(".bgs_btn")
find_element_by_css_selector()方法用於CSS語言定位元素,點號(.)表示經過class屬性來定位元素。
(2) 經過 id 屬性定位
find_element_by_css_selector("#kw") find_element_by_css_selector("#su")
(3) 井號(#)表示經過 id 屬性來定位元素
經過標籤名定位:
find_element_by_css_selector("input")
在 CSS 語言中,用標籤名定位元素不須要任何符號標識,直接使用標籤名便可。但咱們前面已經瞭解到,標籤名重複的機率很是大,因此經過這種方式很難找到想要的元素。
(3.1)經過父子關係定位
find_element_by_css_selector("span>input")
上面的寫法表示有父親元素,它的標籤名爲apan,查找它的全部標籤名叫input的子元素。
(3.2)經過屬性定位
find_element_by_css_selector("[autocomplete=off]") find_element_by_css_selector("[name='kw']") find_element_by_css_selector("[type='submit']")
在 CSS 當中也可使用元素的任意屬性,只要這些屬性能夠惟一標識這個元素,對於屬性值來講,可加引號,也能夠不加,但注意和整個字符串的引號進行區分。
(3.3)組合定位
咱們固然能夠把上面的定位策略組合起來使用,這就大大增強了定位元素的惟一性。
find_element_by_css_selenium("span.bgs_ipt_wr>input.s_inpt") find_element_by_css_selenium("span.bgs_btn_wr>input#su")
有一個父元素,它的標籤名叫 span;它有一個class屬性值叫 bgs_ipt_wr;它有一個子元素,標籤名叫 input,而且這個子元素的 class 屬性值叫 s_ipt。
瀏覽器開發者工具F12,複製,selector
針對前面介紹的 8 種定位方法,WebDriver 還提供了另一套寫法,即統一調用 find_element()方法,經過 By 來生命定位的方法,而且傳入對應定位方法的定位參數,具體以下:
find_element(By.ID,"kw") find_element(By.NAME,"wd") find_element(By.CLASS_NAME,"s_ipt") find_element(By.TAG_NAME,"input") find_element(By.LINK_TEXT,"新聞") find_element(By.PARTIAL_LINK_TEXT,"新") find_element(By.XPATH,"//*[@class='bgs_btn']") find_element(By.CSS_SELECTOR,"span.bgs_btn_wr>input#su")
find_element()方法只用於定位元素。它須要兩個參數,第一個參數是定位的類型,由By提供;第二個參數是定位的具體方法,在使用By以前須要將By類導入。
from selenium.webdriver.common.by import By
經過查看 WebDriver 的底層實現代碼發現它們實際上是一回事兒,例如,find_element_by_id()方法的實現。
def find_elements_by_id(self, id_): """ Finds multiple elements by id. :Args: - id\_ - The id of the elements to be found. :Returns: - list of WebElement - a list with elements if any was found. An empty list if not :Usage: elements = driver.find_elements_by_id('foo') """ return self.find_elements(by=By.ID, value=id_)
from selenium import webdriver driver = webdriver.Firefox() driver.get("http://m.mail.10086.cn") #參數數字爲像素點 print("設置瀏覽器寬480、高800顯示") driver.set_window_size(480,800) driver.quit()
在PC端執行自動化測試腳本大多的狀況下是但願瀏覽器在全屏模式下執行,那麼可使用maximize_window()方法使打開的瀏覽器全屏顯示,其用法與set-window_size() 相同,但它不須要參數。
在使用瀏覽器瀏覽網頁時,瀏覽器提供了後退和前進按鈕,能夠方便地在瀏覽過的網頁之間切換,WebDriver 也提供了對應的 back() 和 forward() 方法來模擬後退和前進按鈕,下面經過例子來演示這兩個方法的使用。
from selenium import webdriver driver = webdriver.Firefox() #訪問百度首頁 first_url="http://www.baidu.com" print("now access %s" %(first_url)) driver.get(first_url) #訪問新聞頁面 second_url="http://news.baidu.com" print("now access %s" %(second_url)) driver.get(second_url) #返回(後退)到百度首頁 print("back to %s" %(first_url)) driver.back() #前進到新聞頁 print("forward to %s" %(second_url)) driver.forward() driver.quit()
爲了看清腳本的執行過程,下面每操做一步都經過print() 來打印當前的 URL 地址,執行結果以下:
now access http://www.baidu.com now access http://news.baidu.com back to http://www.baidu.com forward to http://news.baidu.com
driver.refresh() #刷新當前頁面
clear() 方法用於清除文本輸入框中的內容。例如,登陸框內通常默認會有「帳號」、「密碼」等提示信息,用於引導用戶輸入正確的數據;但若是直接向輸入框中輸入數據,則可能會與輸入框中的提示信息拼接,從而形成輸入信息錯誤。這個時候能夠先使用clear()方法來清除輸入框中的默認提示信息。
send_keys() 方法模擬鍵盤向輸入框裏輸入內容。如上面的例子中,經過這個方法向用戶名和密碼框中輸入登陸信息。固然,它的做用不只於此,咱們還能夠用它發送鍵盤按鍵,甚至用它來模擬文件上傳。
click() 方法能夠用來單擊一個元素,前提是它是能夠被單擊的對象,它與 send_keys() 方法是Web頁面操做中最經常使用到的兩個方法。其實 click() 方法不只可用於單擊一個按鈕,它還能單擊任何能夠單擊的文字/圖片連接、複選框、單選框、下拉框等。
from selenium import webdriver b = webdriver.Chrome() b.get("https://mail.qq.com/") b.find_element_by_xpath('//*[@id="u"]').clear() b.find_element_by_xpath('//*[@id="u"]').send_keys("578389018@qq.com") b.find_element_by_xpath('//*[@id="p"]').send_keys("*******") b.find_element_by_xpath('//*[@id="login_button"]').click() b.quit()
會報錯,表示找不到元素。
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"xpath","selector":"//*[@id="u"]"}
元素找不到有好幾種可能:
這個是最多見的緣由,首先要理解下frame的實質,frame中其實是嵌入了另外一個頁面,而webdriver每次只能在一個頁面識別,所以須要先定位到相應的frame,對那個頁面裏的元素進行定位。
from selenium import webdriver b = webdriver.Chrome() b.get("https://mail.qq.com/") b.switch_to.frame('login_frame') #需先跳轉到iframe框架 b.find_element_by_xpath('//*[@id="u"]').clear() b.find_element_by_xpath('//*[@id="u"]').send_keys("578389018@qq.com") b.find_element_by_xpath('//*[@id="p"]').send_keys("********") b.find_element_by_xpath('//*[@id="login_button"]').click() b.quit()
from selenium import webdriver b = webdriver.Chrome() b.get("https://mail.qq.com/") elementi= b.find_element_by_xpath('//*[@id="login_frame"]') b.switch_to.frame(elementi) #需先跳轉到iframe框架 b.find_element_by_xpath('//*[@id="u"]').clear() b.find_element_by_xpath('//*[@id="u"]').send_keys("578389018@qq.com") b.find_element_by_xpath('//*[@id="p"]').send_keys("********") b.find_element_by_xpath('//*[@id="login_button"]').click() b.quit()
因爲Xpath層級太複雜,容易犯錯。
可使用Firefox的firePath,複製xpath路徑。該方式容易由於層級改變而須要從新編寫過xpath路徑,不建議使用,初學者能夠先複製路徑,而後嘗試去修改它。
如上百度登陸代碼,經過名稱爲tj_login查找的登陸元素,有些是不可見的,因此加一個循環判斷,找到可見元素(is_displayed())點擊登陸便可。
參考:http://www.javashuo.com/article/p-pjkfmibd-cn.html
submit()
submit() 方法用於提交表單。例如,在搜索框輸入關鍵字以後的「回車」操做,就能夠經過submit() 方法模擬。
from selenium import webdriver driver = webdriver.Firefox() driver.get("http://www.youdao.com") driver.find_element_by_id("translateContent").send_keys("hello") #提交輸入框的內容 driver.find_element_by_id("translateContent").submit()
上面的例子,咱們經過定位有道搜索框並經過submit()提交搜索框的內容,一樣達到單擊「搜索」按鈕的效果。有時候 submit() 能夠與 click() 方法互換來使用, submit() 一樣能夠提交一個按鈕,但 submit() 的應用範圍遠不及 click() 普遍。
from selenium import webdriver driver = webdriver.Firefox() driver.get("http://www.baidu.com") # 獲取輸入框的尺寸 size = driver.find_element_by_id("kw").size print(size) # 返回百度頁面底部備案信息 text = driver.find_element_by_id("cp").text print(text) # 返回元素的屬性值,能夠是id、name、type或其餘任意屬性 attribute = driver.find_element_by_id("kw").get_attribute("type") print(attribute) # 返回元素的結果是否可見,返回結果爲True或False result = driver.find_element_by_id("kw").is_displayed() # 注意,是不可見,不是不存在。不存在會直接報錯 print(result) driver.quit() >>> {'height': 22.0, 'width': 500.0} ©2018 Baidu 使用百度前必讀 意見反饋 京ICP證030173號 京公網安備11000002000001號 text True ***Repl Closed***
ActionChains 類提供了鼠標操做的經常使用方法:
from selenium import webdriver # 引入 ActionChains 類 from selenium.webdriver.common.action_chains import ActionChains driver = webdriver.Firefox() driver.get("http://www.baidu.com") # 定位到要右擊的元素 right_click = driver.find_element_by_id("kw") # 對定位到的元素執行鼠標右鍵操做 ActionChains(driver).context_click(right_click).perform()
from selenium import webdriver # 引入 ActionChains 類 from selenium.webdriver.common.action_chains import ActionChains driver = webdriver.Firefox() driver.get("http://www.baidu.com") # 定位到要懸停的元素 above = driver.find_element_by_id("su") # 對定位到的元素執行懸停操做 ActionChains(driver).move_to_element(above).perform()
from selenium import webdriver # 引入 ActionChains 類 from selenium.webdriver.common.action_chains import ActionChains driver = webdriver.Firefox() driver.get("http://www.baidu.com") # 定位到要雙擊的元素 double_click = driver.find_element_by_id("su") # 對定位到的元素執行雙擊操做 ActionChains(driver).double_click(double_click).perform()
drag_and_drop(source,target)在源元素上按住鼠標左鍵,而後移動到目標元素上釋放。
from selenium import webdriver # 引入 ActionChains 類 from selenium.webdriver.common.action_chains import ActionChains driver = webdriver.Firefox() driver.get("http://www.baidu.com") # 定位元素的原位置 element = driver.find_element_by_id("su") target = driver.find_element_by_id("su") # 執行元素的拖動操做 ActionChains(driver).drag_and_drop(element,target).perform()
keys() 類提供了鍵盤上幾乎全部按鍵的方法。前面瞭解到,send_keys()方法能夠用來模擬鍵盤輸入,除此以外,咱們還能夠用它來輸入鍵盤上的按鍵,甚至是組合鍵,如 Ctrl + A ,Ctrl + C等。
#! /usr/bin/env python # -*- coding: utf-8 -*- # __author__ = "Q1mi" # Date: 2018/7/2 from selenium import webdriver # 引入Keys模塊 from selenium.webdriver.common.keys import Keys driver = webdriver.Firefox() driver.get("http://www.baidu.com") # 輸入框輸入內容 driver.find_element_by_id("kw").send_keys("seleniumm") # 刪除多輸入的一個m driver.find_element_by_id("kw").send_keys(Keys.BACK_SPACE) # 輸入空格鍵 + 「教程」 driver.find_element_by_id("kw").send_keys(Keys.SPACE) driver.find_element_by_id("kw").send_keys("教程") # ctrl + a 全選輸入框內容 driver.find_element_by_id("kw").send_keys(Keys.CONTROL,"a") # ctrl + x 剪切輸入框內容 driver.find_element_by_id("kw").send_keys(Keys.CONTROL,"x") # ctrl + v 粘貼內容到輸入框 driver.find_element_by_id("kw").send_keys(Keys.CONTROL,"v") # 經過回車鍵來代替單擊操做 driver.find_element_by_id("su").send_keys(Keys.ENTER)
如下爲經常使用的鍵盤操做:
send_keys(Keys.BACK_SPACE) 刪除鍵(BackSpace) send_keys(Keys.SPACE) 空格鍵(Space) send_keys(Keys.TAB) 製表鍵(Tab) send_keys(Keys.ESCAPE) 回退鍵(Esc) send_keys(Keys.ENTER) 回車鍵(Enter) send_keys(Keys.CONTROL,"a") 全選(Ctrl+A) send_keys(Keys.CONTROL,"c") 複製(Ctrl+C) send_keys(Keys.CONTROL,"x") 剪切(Ctrl+X) send_keys(Keys.CONTROL,"v") 粘貼(Ctrl+V) send_keys(Keys.F1) 鍵盤F1 ...... . send_keys(Keys.F12) 鍵盤F12
在編寫功能測試用例時,會假定一個預期結果,在執行用例的過程當中把獲得的實際結果與預期結果進行比較,從而判斷用戶的經過或失敗。自動化測試用例是由機器去執行的,一般機器並不像人同樣有思惟和判斷能力,那麼是否是模擬各類操做頁面的動做沒有報錯就說明用例執行成功呢?並不是如此,假如咱們模擬百度搜索的用例,當新的迭代版本上線後,每一頁的搜索結果少一條,但用例的執行不會報錯,所以這個 bug 永遠不會被自動化測試發現。
那麼是否是在運行自動化測試用例時須要由測試人員盯着用例的執行來辨別執行結果呢?若是是這樣的話,自動化測試就失去了「自動化」的意義。在自動化用例執行完成以後,咱們能夠從頁面上獲取一些信息來「證實「用例執行是成功仍是失敗。
一般用得最多的幾種驗證信息分別是title、URL和text。text方法在前面已經講過,它用於獲取標籤對之間的文本信息。
from selenium import webdriver import time driver = webdriver.Chrome() driver.get("https://mail.qq.com/") time.sleep(2) frame_add = driver.find_element_by_xpath('//*[@id="login_frame"]') driver.switch_to_frame(frame_add) #需先跳轉到iframe框架 print("Before login==========") # 打印當前頁面title now_title = driver.title print(now_title) # 打印當前頁面URL now_url = driver.current_url print(now_url) # 執行郵箱登陸 driver.find_element_by_id("u").clear() driver.find_element_by_id("u").send_keys("578389018@qq.com") driver.find_element_by_id("p").clear() driver.find_element_by_id("p").send_keys("*******") driver.find_element_by_id("login_button").click() time.sleep(5) print("After login==========") # 再次打印當前頁面title now_title = driver.title print(now_title) # 再次打印當前頁面URL now_url = driver.current_url print(now_url) # 獲取登陸的用戶名 user = driver.find_element_by_id("useraddr").text print(user) driver.quit() >>> C:\Users\Administrator\AppData\Local\Programs\Python\Python37\python.exe C:/Users/Administrator/Desktop/py/test.py Before login========== 登陸QQ郵箱 https://mail.qq.com/ After login========== QQ郵箱 https://mail.qq.com/cgi-bin/frame_html?sid=IDp2I6d3Vi8WORtA&r=b6242c8e186a9f7c4b80aab437e760f8 dongye95@foxmail.com Process finished with exit code 0
經過打印結果,咱們發現登陸先後的title和URL明顯不一樣。咱們能夠把登陸以後的這些信息存放起來,做爲登陸是否成功的驗證信息。固然,這裏URL每次登陸都會有所變化,是不能拿來作驗證信息的。title能夠拿來作驗證信息,但它並不能明確地標識是哪一個用戶登陸成功了,所以經過text獲取用戶文本(dongye95@foxmail.com)是很好的驗證信息。
現在大多數Web應用程序使用AJAX技術。當瀏覽器在加載頁面時,頁面上的元素可能並非同時被加載完成的,這給元素的定位增長了困難。若是由於在加載某個元素時延遲而形成ElementNotVisibleException的狀況出現,那麼就會下降自動化腳本的穩定性,咱們能夠經過設置元素等待改善這種問題形成的不穩定。
WebDriver提供了兩種類型的等待:顯式待和隱式等待
顯式等待使 WebDriver 等待某個條件成立時繼續執行,不然在達到最大時長時拋棄超時等待。(TimeoutException)
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC driver = webdriver.Firefox() driver.get("http://www.baidu.com") element = WebDriverWait(driver, 5, 0.5).until( EC.presence_of_element_located((By.ID,"kw")) ) element.send_keys('selenium') driver.quit()
WebDriverWait 類是由 WebDriver 提供的的等待方法。在設置時間內,默認每隔一段時間檢測一次當前頁面元素是否存在,若是超過設置時間檢測不到則拋出異常。具體格式以下
WebDriverWait(driver,timeout,poll_frequency=0.5,ignored_exceptions=None)
WebDriverWait() 通常由 until() 或者 until_not() 方法配合使用,下面是 until() 和 until_not() 方法的說明。
調用該方法提供的驅動程序做爲一個參數,直到返回值爲True
調用該方法提供的驅動程序做爲一個參數,直到返回值爲False
在本例中,經過 as 關鍵字將 expected_conditions 重命名爲 EC,並調用 presence_of_element_located() 方法判斷元素是否存在。
expected_conditions 類所提供的預期條件判斷的方法以下表所示。
除expected_conditions 所提供的豐富的預期條件判斷方法外,還可使用前面學過的is_displayed() 方法來判斷元素是否可見。
from selenium import webdriver from time import sleep, ctime driver = webdriver.Firefox() driver.get("http://www.baidu.com") print(ctime()) for i in range(10): try: el = driver.find_element_by_id("kw22") if el.is_displayed(): break except Exception as e: pass sleep(1) else: print("time out") driver.close() print(ctime()) >>> Tue Jul 3 09:41:42 2018 time out Tue Jul 3 09:41:54 2018 ***Repl Closed***
相對來講,這種方式更容易理解,經過for循環10次,每次循環判斷元素的is_displayed()狀態是否爲True;若是爲True,則break跳出循環;不然sleep(1) 後繼續循環判斷,直到10次循環結束後,打印「time out」信息。
隱式等待是經過必定的時長等待頁面上某元素加載完成。若是超出尚未被加載,則拋出 NoSuchElementException 異常。WebDriver提供方法來實現隱式等待,默認設置爲0.它的用法相對來講要簡單得多。
from selenium import webdriver from selenium.common.exceptions import NoSuchElementException from time import ctime driver = webdriver.Firefox() # 設置隱式等待10秒 driver.implicitly_wait(10) driver.get("http://www.baidu.com") try: print(ctime()) driver.find_element_by_id("kw22").send_keys('selenium') except NoSuchElementException as e: print("查找元素異常 %s" %(e)) else: pass finally: print(ctime()) driver.quit() >>> Wed Jul 4 12:51:30 2018 查找元素異常 Message: Unable to locate element: [id="kw22"] Wed Jul 4 12:51:40 2018 Process finished with exit code 0
implicitly_wait() 默認參數的單位爲秒,本例中設置等待時長爲10秒。首先這10秒並不是一個固定的等待時間,它並不影響腳本的執行速度。其次,它並不針對頁面上的某一元素進行等待。當腳本執行到某個元素定位時,若是元素能夠定位,則繼續執行;若是元素定位不到,則它將以輪詢的方式不斷地判斷元素是否被定位到。假設在第6秒定位到了元素則繼續執行,若直到超出設置時間(10秒)尚未定位到元素,則拋出異常。
在上面的例子中,顯然百度輸入框的定位 id=kw22 是有誤的,經過打印的兩個時間能夠看出,當執行度一百度輸入框的操做時,超過了10秒的等待。
有時候咱們但願腳本在執行到某一位置時作固定時間的休眠,尤爲是在腳本調試過程當中。這時可使用sleep()方法,須要說明的是,sleep()方法由Python的time模塊提供。
from selenium import webdriver from time import sleep driver = webdriver.Firefox() driver.get("http://www.baidu.com") sleep(2) driver.find_element_by_id("kw").send_keys("webdriver") driver.find_element_by_id("su").click() sleep(3) driver.quit()
當執行到sleep()方法時會固定休眠必定的時長,而後再繼續執行。sleep()方法默認參數以秒爲單位,若是設置時長小於1秒,則能夠用小數表示,如sleep(0.5)表示休眠0.5秒。
在2.4中咱們已經學了8中定位方法,這8種定位方法時針對單個元素定位的,WebDriver還提供了與之對應的8種用於定位一組元素的方法。
find_elements_by_id()
find_elements_by_name()
find_elements_by_class_name()
find_elements_by_tag_name()
find_elements_by_link_text()
find_elements_by_partial_link_text()
find_elements_by_xpath
find_elements_by_css_selector()
定位一組元素的方法與定位單個元素的方法相似,惟一的區別是在單詞element後面多了一個s表示複數。定位一組元素通常用於如下場景:
from selenium import webdriver import os,time driver = webdriver.Firefox() file_path = "file:///" + os.path.abspath("checkbox.html") # file:/// 換成 file: 也能夠,可是這個是必須的前綴 driver.get(file_path) # 選擇頁面上全部的tag name 爲input的元素 inputs = driver.find_elements_by_tag_name('input') # 而後從中過濾出type爲checkbox的元素,單擊勾選 for i in inputs: if i.get_attribute("type") == "checkbox": i.click() time.sleep(1) driver.quit()
前面提到,經過tag name的定位方式很難定位到單個元素,由於元素標籤名重名的機率很高,於是在定位一組元素時,這種方式就派上用場了。在上面的例子中先經過find_elements_by_tag_name()找到一組標籤名爲input的元素。而後經過for循環進行遍歷,在遍歷過程當中,經過get_attribute()方法獲取元素的type屬性是否爲「checkbox」,若是爲「checkbox」,就認爲這個元素是一個複選框,對其進行勾選操做。
須要注意的是,在上面的例子中,經過瀏覽器打開的是一個本地的html文件,因此須要用到Python的os模塊,path.abspath()方法用於獲取當前路徑下的文件。
除此以外,咱們還可使用XPath或CSS來直接判斷屬性值,從而進行單擊操做。
from selenium import webdriver import os,time driver = webdriver.Firefox() file_path = "file:///" + os.path.abspath("checkbox.html") driver.get(file_path) # 經過XPath找到type=checkbox的元素 # checkboxes = driver.find_elements_by_xpath("//input[@type='checkbox']") # 經過CSS找到type=checkbox的元素 checkboxes = driver.find_elements_by_css_selector("//input[type=checkbox]") for checkbox in checkboxes: if i.get_attribute("type") == "checkbox": checkbox.click() time.sleep(1) # 打印當前頁面上的type爲checkbox的個數 print(len(checkboxes)) # 把頁面上最後1個checkbox的鉤給去掉 driver.find_element_by_css_selector("input[type=checkbox]").pop().click() driver.quit()
經過XPath或CSS來查找一組元素時,省去了判斷步驟。由於定位方法已經作了判斷,只需循環對這一組元素進行勾選便可。
在Web應用中常常會遇到frame/iframe表單嵌套頁面的應用,WebDriver只能在一個頁面上對元素識別與定位,對於frame/iframe表單內嵌頁面上的元素沒法直接定位。這時就須要經過switch_to.frame() 方法將當前定位的主體切換爲frame/iframe表單的內嵌頁面中。
from selenium import webdriver b = webdriver.Chrome() b.get("https://mail.qq.com/") # 切換到 iframe(id="login_frame") b.switch_to.frame('login_frame') b.find_element_by_xpath('//*[@id="u"]').clear() b.find_element_by_xpath('//*[@id="u"]').send_keys("578389018@qq.com") b.find_element_by_xpath('//*[@id="p"]').send_keys("********") b.find_element_by_xpath('//*[@id="login_button"]').click() b.quit()
switch_to.frame() 默承認以直接取表單的 id 或 name 屬性。若是 iframe 沒有可用的 id 和 name 屬性,則能夠經過下面的方式進行定位。
from selenium import webdriver b = webdriver.Chrome() b.get("https://mail.qq.com/")
# 先經過xpath定位到iframe elementi= b.find_element_by_xpath('//*[@id="login_frame"]')
#再將定位對象傳給switch_to.frame()方法 b.switch_to.frame(elementi) b.find_element_by_xpath('//*[@id="u"]').clear() b.find_element_by_xpath('//*[@id="u"]').send_keys("578389018@qq.com") b.find_element_by_xpath('//*[@id="p"]').send_keys("********") b.find_element_by_xpath('//*[@id="login_button"]').click() b.switch_to.parent_frame() b.quit()
switch_to包的方法詳解
在switch_to的基礎上,有這麼幾個方法,鑑於基本上都是以前曾經講過的,此次把等價的方法也列出來,供你們參考
1. driver.switch_to.active_element() ---------------替換-------------- driver.switch_to_active_element()
定位到當前聚焦的元素上 聚焦這部分參考:https://blog.csdn.net/huilan_same/article/details/52338073
2. driver.switch_to.alert() ---------------替換-------------- 等同於 driver.switch_to_alert()
切換到alert彈窗
3. driver.switch_to.default_content() ---------------替換-------------- driver.switch_to_default_content()
切換到最上層頁面
4. driver.switch_to.frame(frame_reference) ---------------替換--------------driver.switch_to_frame(frame_reference)
經過id、name、element(定位的某個元素)、索引來切換到某個frame
5. driver.switch_to.parent_frame()
這是switch_to中獨有的方法,能夠切換到上一層的frame,對於層層嵌套的frame頗有用
6. driver.switch_to.window(window_name)等同於 driver.switch_to_window(window_name)
切換到制定的window_name頁面
注: 官方把selenium.webdriver包中的switch方法所有封裝成了一個包,這樣可以比較明瞭和方便,也符合軟件編程中的高內聚低耦合的思想。
在頁面操做過程當中有時候點擊某個連接會彈出新的窗口,這時就須要主機切換到新打開的窗口上進行操做。WebDriver提供了switch_to.window() 方法,能夠實如今不一樣的窗口之間切換。
以百度首頁和百度註冊頁爲例。
from selenium import webdriver import time driver = webdriver.Chrome() driver.implicitly_wait(10) driver.get("http://www.baidu.com") # 得到百度搜索窗口句柄 sreach_windows = driver.current_window_handle driver.find_element_by_link_text("登陸").click() driver.find_element_by_link_text("當即註冊").click() # 得到當前全部打開的窗口的句柄 all_handles = driver.window_handles # 進入註冊窗口 for handle in all_handles: if handle != sreach_windows: driver.switch_to.window(handle) print("now register window!") driver.find_element_by_name("userName").send_keys("username") driver.find_element_by_id("TANGRAM__PSP_3__password").send_keys("password") time.sleep(2) # 回到搜索窗口 for handle in all_handles: if handle == sreach_windows: driver.switch_to.window(handle) print("now sreach window!") # 會跳出來一個讓你下載APP的彈窗,把這個彈窗給點掉 try: driver.find_element_by_id("TANGRAM__PSP_4__closeBtn") except: break else: driver.find_element_by_id("TANGRAM__PSP_4__closeBtn").click() # 彈窗消失後,再去查找 driver.find_element_by_id("kw").send_keys("selenium") driver.find_element_by_id("su").click() time.sleep(2) driver.quit()
腳本的執行過程:首先打開百度首頁,經過current_window_handle 得到當前窗口的句柄,並賦值給變量sreach_handle。接着打開登陸彈窗,在登陸彈窗上單擊「當即註冊」,從而打開新的註冊窗口。經過window_handles得到當前打開的全部窗口的句柄,並賦值給變量all_handles。
第一個循環遍歷all_handles,若是handle不等於sreach_handle,那麼必定是註冊窗口,由於腳本執行過程當中只打開了兩個窗口。因此,經過switch_to.window()切換到註冊頁進行註冊操做。第二個循環相似,不過這一次判斷若是handle等於sreach_handle,那麼切換到百度搜索頁,而後進行搜索操做。
在本例中所涉及的新方法以下:
current_window_handle:得到當前窗口句柄。
window_handles:返回全部窗口的句柄到當前會話。
switch_to.window():用於切換到相應的窗口,與上一節的switch_to.frame()相似,前者用於不一樣窗口的切換,後者用於不一樣表單之間的切換。
在WebDriver 中處理JavaScript所生成的alert、confirm以及prompt十分簡單,具體作法是使用switch_to.alert() 方法定位到 alert/confirm/prompt,而後使用text/accept/dismiss/send_keys等方法進行操做。
百度搜索設置彈出的窗口是不能經過前端工具對其進行定位的,這個時候就能夠經過switch_to.alert()方法接受這個彈窗
from selenium import webdriver from selenium.webdriver.common.action_chains import ActionChains import time driver = webdriver.Chrome() driver.implicitly_wait(10) driver.get("http://www.baidu.com") # 鼠標懸停至「設置」連接 link = driver.find_element_by_link_text("設置") ActionChains(driver).move_to_element(link).perform() # 打開搜索設置 driver.find_element_by_link_text("搜索設置").click() time.sleep(0.3) # 沒有這個延遲會出現問題,表如今點不到保存設置上,具體還不清楚咋回事情 # 保存設置 driver.find_element_by_link_text('保存設置').click() # 接受警告框 driver.switch_to.alert.accept() driver.quit()
上傳文件是比較常見的Web功能之一,但WebDriver並無提供專門用於上傳的方法,如何實現上傳操做關鍵在於上傳文件的思路。
通常Web頁面的上傳功能的操做須要單擊「上傳」按鈕後打開本地的Window窗口,從窗口中選擇本地文件進行上傳。而webDriver是沒法操做Windows控件,因此,對於初學者來講,通常思路會卡在如何識別Window空間這個問題上。
對於Web頁面的上傳功能實現通常由如下兩種方式。
對於經過input標籤實現的上傳功能,能夠將其看作是一個輸入框,即經過send_keys()指定本地文件路徑的方式實現文件上傳。
from selenium import webdriver import os,time driver = webdriver.Chrome() file_path = "file:///" + os.path.abspath("test.html") driver.get(file_path) # 定位上傳按鈕,添加本地文件 driver.find_element_by_name("file").send_keys("C:\\Users\\Administrator\\Desktop\\py\\geckodriver.log") driver.quit()
autoit 目前最新版本是v3,它是一個使用相似BASIC腳本語言的免費軟件,它被設計用來進行Windows GUI(圖形用戶界面)的自動化測試。它利用模擬鍵盤按鍵,鼠標移動和窗口/控件的組合來實現自動化任務。
具體參考《自動化測試實戰 基於PYTHON語言》一書4.12.2章節。並不推薦,由於經過Python調用的exe程序並不在Python的可控範圍內。換句話說,exe執行多長時間,執行是否出錯,Python程序都沒法得知。
https://blog.csdn.net/huilan_same/article/details/52789954
WebDriver 容許咱們設置默認的文件下載路徑,也就是說,文件會自動下載而且存放到設置的目錄中。下面以Firefox瀏覽器爲例,執行文件的下載。
from selenium import webdriver import os fp = webdriver.FirefoxProfile() fp.set_preference("browser.download.folderList",2) fp.set_preference("browser.download.manager.showWhenStarting",False) fp.set_preference("browser.download.manager.dir",os.getcwd()) fp.set_preference("browser.helperApps.neverAsk.saveToDisk","application/octet-stream") # 下載文件的類型 driver = webdriver.Firefox(firefox_profile=fp) driver.get("http://pypi.Python.org/pypi/selenium") driver.find_element_by_partial_link_text("selenium-2").click()
爲了讓Firefox瀏覽器能實現文件下載,咱們須要經過FirefoxProfile()對其作一些設置。
browser.download.folderList
設置成0表明下載到瀏覽器默認下載路徑,設置成2則能夠保存到制定目錄
browser.download.manager.showWhenStarting
是否顯示開始:True爲顯示,Flase爲不顯示
browser.download.dir
用於指定所下載文件的目錄。os.getcwd()函數不須要傳遞參數,用於返回當前的目錄。
browser.helperApps.neverAsk.saveToDisk
指定要下載頁面的Content_type值,「application/octet-stream」爲文件的類型
HTTP Content_type經常使用對照表:http://tool.oschina.net/commons
這些參數的設置能夠經過在Firefox瀏覽器地址欄輸入:about:config進行設置。
將全部設置信息在調用WebDriver的Firefox()方法時做爲參數傳遞給瀏覽器。Firefox瀏覽器在下載時就根據這些設置信息將文件下載在當前腳本的目錄下。
上面例子中的設置只針對Firefox瀏覽器,不一樣瀏覽器設置方法不該。通用的藉助 AutoIt來操做Windows空間進行下載。
有時候咱們須要驗證瀏覽器中cookie是否正確,由於基於真實cookie的測試是沒法經過白盒和集成測試的。WebDriver提供了操做Cookie的相關方法,能夠獲取、添加和刪除cookie信息。
WebDriver 操做 cookie的方法:
下面經過get-cookies()來獲取當前瀏覽器的cookie信息
from selenium import webdriver driver = webdriver.Firefox() driver.get("http://www.youdao.com") # 獲取cookie信息 cookie = driver.get_cookies() # 將得到cookie的信息打印 print(cookie) driver.quit() >>> [{'name': 'YOUDAO_MOBILE_ACCESS_TYPE', 'value': '1', 'path': '/', 'domain': '.youdao.com', 'expiry': 1562137504, 'secure': False, 'httpOnly': False}, {'name': 'DICT_UGC', 'value': 'be3af0da19b5c5e6aa4e17bd8d90b28a|', 'path': '/', 'domain': '.youdao.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'OUTFOX_SEARCH_USER_ID', 'value': '-1243283229@60.12.215.75', 'path': '/', 'domain': '.youdao.com', 'expiry': 2476681504, 'secure': False, 'httpOnly': False}, {'name': 'JSESSIONID', 'value': 'abcXbMI_bHumq7K2x9Erw', 'path': '/', 'domain': '.youdao.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '___rl__test__cookies', 'value': '1530601505713', 'path': '/', 'domain': 'www.youdao.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'OUTFOX_SEARCH_USER_ID_NCOO', 'value': '1523083508.1908145', 'path': '/', 'domain': '.youdao.com', 'expiry': 1593673505, 'secure': False, 'httpOnly': False}]
從執行結果能夠看出,cookie數據是以字典的形式進行存放的。知道了cookie的存放形式,接下來咱們就能夠按照這種形式向瀏覽器中寫入cookie信息。
from selenium import webdriver driver = webdriver.Firefox() driver.get("http://www.youdao.com") # 向cookie的name和value中添加會話信息 driver.add_cookie({"name":"key-aaaa","value":"value-bbbb"}) # 遍歷cookies中的name和value信息並打印,固然還有上面添加的信息。 for cookie in driver.get_cookies(): print("%s -> %s" %(cookie["name"],cookie["value"])) driver.quit() >>> YOUDAO_MOBILE_ACCESS_TYPE -> 1 DICT_UGC -> be3af0da19b5c5e6aa4e17bd8d90b28a| OUTFOX_SEARCH_USER_ID -> 871140950@60.12.215.75 JSESSIONID -> abcq80IlD53H4bj3V_Erw ___rl__test__cookies -> 1530601866047 OUTFOX_SEARCH_USER_ID_NCOO -> 2091021681.247173 key-aaaa -> value-bbbb
從執行結果能夠看到,最後一條cookie信息是在腳本執行過程當中經過add_cookie()方法添加的。經過遍歷獲得全部的cookie信息,從而找到key爲「name」和「value」的特定cookie的value。
那麼在什麼狀況下會用到cookie的操做呢?例如開發人員開發一個功能,當用戶登陸後,會將用戶的用戶名寫入瀏覽器cookie,指定的key爲「username」,那麼咱們就能夠經過 get_cookies()找到username,打印value。若是找不到username或對應的value爲空,那麼說明cookie沒有成功地保存到瀏覽器中。
delete_cookie()和delete_all_cookies() 的使用也很簡單,前者經過name刪除一個特定的cookie信息,後者直接刪除瀏覽器中的全部cookies()信息。
雖然WebDriver提供了操做瀏覽器的前進和後退方法,但對於瀏覽器滾動條並無提供相應的操做方法。在這種狀況下,就能夠藉助JavaScript來控制瀏覽器的滾動條。WebDriver提供了execute_script()方法來執行JavaScript代碼。
通常咱們想到的必須使用滾動條的場景是:註冊時的法律條文的閱讀。判斷用戶是否閱讀完的標準是,滾動條是否拉到頁面底部。固然,有時候爲了使操做更接近用戶行爲也會使用滾動條,例如用戶要操做的元素在頁面的第二屏,通常用戶不會對看不到的元素進行操做,那麼就須要先將滾動條拖到頁面的第二屏再進行操做。
用於調整瀏覽器滾動條位置的JavaScript代碼以下:
...... <!-- window.scrollTo(左邊距,上邊距);--> window.scrollTo(0,450) ......
window.scrollTo()方法用於設置瀏覽器窗口滾動條的水平和垂直位置。方法的第一個參數表示水平的左間距,第二個參數表示垂直的上邊距。其代碼以下:
from selenium import webdriver from time import sleep # 訪問百度 driver = webdriver.Firefox() driver.get("http://www.baidu.com") # 設置瀏覽器窗口大小 driver.set_window_size(600,600) # 搜索 driver.find_element_by_id("kw").send_keys("selenium") driver.find_element_by_id("su").click() sleep(2) # 經過javascript設置瀏覽器窗口的滾動條位置 js="window.scrollTo(100,450);" driver.execute_script(js) sleep(3) driver.quit()
經過瀏覽器打開百度進行搜索,而且提早經過 set_window_size()方法將瀏覽器窗口設置爲固定寬高顯示,目的是讓窗口出現水平和垂直滾動條。而後經過execute_script()方法執行JavaScript代碼來移動滾動條的位置。
固然,JavaScript的做用不只僅體如今瀏覽器滾動條的操做上,還能夠用它向頁面中textarea文本框輸入內容。
...... <textarea id="id" style="width: 98%" cols="50" rows="5" class="textarea"> </textarea> ......
雖然咱們能夠經過id的方式將其進行定位,但卻不能經過send_keys()向文本框中輸入文本信息。這種狀況下,就須要藉助JavaScript代碼完成輸入。
...... text = "input text" js = "var sum=document.getElementById("id"); sum.value = '" + text + "';" driver.execute_script(js) ......
首先定義了要輸入的內容text,而後將text與JavaScript代碼經過 「+」 進行拼接。這樣作的目的是爲了使輸入內容變得可自定義。最後,經過execute_script()執行JavaScript代碼。
目前HTML5技術已漸漸成爲主流,主流的瀏覽器都已支持HTML5。愈來愈多的應用使用了HTML5的元素,如canvas、video等,另外網頁存儲功能更增長了用戶的網絡體驗,使得愈來愈多的開發者在使用這樣的標準,因此咱們也須要學習如何使用自動化技術來測試它們。
WebDriver支持在指定的瀏覽器上測試HTML5,另外,咱們還可使用JavaScript來測試這些功能,這樣就能夠在任何瀏覽器上測試HTML5了。
大多數瀏覽器使用控件(如Flash)來播放視頻,可是,不一樣的瀏覽器須要使用不一樣的插件。HTML5定義了一個新的元素<video>,指定了一個標準的方式來嵌入電影片斷。
from selenium import webdriver from time import sleep driver = webdriver.Firefox() driver.get("http://videojs.com/") video = driver.find_element_by_xpath("body/Setion[1]/div/video") # 返回播放文件地址 url = driver.execute_script("return arguments[0].currentSrc;",video) print(url) # 播放視頻 print("start") driver.execute_script("return arguments[0].play()",video) # 播放15秒鐘 sleep(15) # 暫停視頻 print("stop") driver.execute_script("arguments[0].pause()",video) driver.quit()
JavaScript 函數有個內置的對象叫作 arguments。arguments對象包含了函數調用的參數數組,[0]表示取對象的第1個值。
currentSrc 熟悉返回當前音頻 / 視頻的URL。若是未設置音頻 / 視頻,則返回空字符串。
load() 、play() 、pause() 、等控制着視頻的加載、播放和暫停。
自動化用例是由程序去執行的,所以有時候打印的錯誤信息並不十分明確。若是在腳本執行出錯的時候能對當前窗口截圖保存,那麼經過圖片就能夠很是直觀地看出出錯的緣由。WebDriver提供了截圖函數 get_screenshot_as_file()來截取當前窗口。
driver = webdriver.Firefox() driver.get("http://www.baidu.com") driver.find_element_by_id("kw").send_keys("selenium") driver.find_element_by_id("su").click() sleep(2) # 截圖當前窗口,並制定截圖圖片的保存位置 driver.get_screenshot_as_file("D:\\pyse\\baidu_img.png") # png格式 driver.quit()
在前面的例子中咱們一直使用quit()方法,其含義爲退出相關的驅動程序和關閉全部窗口。除此以外,WebDriver還提供了close()方法,用來關閉當前窗口。在用例執行的過程當中打開了多個窗口,咱們想要關閉其中的某個窗口,這時就要用到close()方法進行關閉了。
對於Web應用來講,大部分的系統在用戶登陸時都要求用戶輸入驗證碼。驗證碼的類型不少,有字母數字的,有漢字的,甚至還有須要用戶輸入一道算術題的答案的。對於系統來講,使用驗證碼能夠有效地防止採用機器猜想方法對口令的刺探,在必定程度上增長了安全性。
但對測試人員來講,無論是進行性能測試仍是自動化測試,都是一個比較棘手的問題。在WebDriver中並無提供相應的方法來處理驗證碼,這裏有幾種處理驗證碼的常見方法。
這是最簡單的方法,對於見解人員來講,只是把驗證碼的相關代碼註釋掉便可。若是是在測試環境,這樣作可省去測試人員很多的麻煩;但若是自動化腳本是在正是環境測試,那麼這種作法就給系統帶來了必定的風險。
去掉驗證碼的主要問題是安全,爲了應對在線系統的安全威脅,能夠在修改程序時不取消驗證碼,而是在程序中留一個「後門」,即設置一個「萬能驗證碼」。只要用戶輸入這個「萬能驗證碼」,程序就認爲驗證經過,不然就判斷用戶輸入的驗證碼是否正確。
設計萬能驗證碼的方式很是簡單,只需對用戶的輸入信息多加一個邏輯判斷。
例如,能夠經過Python-tesseract 來識別圖片驗證碼。Python-tesseract是光學字符識別 Tesseract OCR 引擎的Python封裝類,可以讀取任何常規的圖片文件(JPG/GIF/PNG/TIFF等)。不過,目前市面上的驗證碼形式繁多,大多驗證碼識別技術,識別率都很難達到100%。
經過向瀏覽器中添加cookie能夠繞過登陸的驗證碼,這是比較有意思的一種解決方案。例如咱們在第一次登陸某網站時勾選「記住密碼」的選項,當下次再訪問該網站時自動就處於登錄狀態了。這樣天然就繞過了驗證碼問題。這個「記住密碼」功能其實就記錄在了瀏覽器的cookie中。前面已經學了經過WebDriver來操做瀏覽器的cookie,能夠經過add_cookie() 方法將用戶名密碼寫入瀏覽器cookie,當再次訪問網站時,服務器將直接讀取瀏覽器的cookie進行登陸。
...... # 訪問 xx 網站 driver.get("http://www.xx.cn") # 將用戶名密碼寫入瀏覽器 cookie driver.add_cookie({"name":"Login_UserNumber","value":"username"}) driver.add_cookie({"name":"Login_Passwd","value":"password"}) # 再次訪問xx網站,將會自動登陸 driver.get("http://www.xx.cn/") #...... driver.quit()
這種方式最大的問題是如何從瀏覽器的 cookie 中找到用戶名和密碼對應的 key 值,並傳入對應的登陸信息。能夠用 get_cookies()方法來獲取登陸的全部的cookie信息,從中找到用戶名和密碼的key。固然,更直接的方式是詢問開發人員。
WebDriver 是按照 Server - Client 的經典設計模式設計的。
Server 端就是 Remote Server,能夠是任意的瀏覽器。當咱們的腳本啓動瀏覽器後,該瀏覽器就是Remote Server,它的職業就是等待 Client 發送請求並做出響應。
Client 端簡單說來就是咱們的測試代碼。咱們測試代碼中的一些行爲,例如打開瀏覽器,轉跳到特定的 URL 等操做是以 http 請求的方式發送給被測試瀏覽器的,也就是 Remote Server。Remote Server。Remote Server 接受請求,執行相應操做,並在 Response 中返回執行狀態,返回值等信息。
WebDriver 的工做流程:
Python 提供了logging 模塊給運行中的應用提供了一個標準的信息輸出接口。它提供了basicConfig() 方法用於基本信息的定義。開啓 debug 模塊,就能夠捕捉到客戶端向服務端發送的請求。
from selenium import webdriver import logging logging.basicConfig(level = logging.DEBUG) driver = webdriver.Firefox() driver.get("http://www.baidu.com") driver.find_element_by_id("kw").send_keys("selenium") driver.find_element_by_id("su").click() driver.quit() >>> C:\Users\Administrator\AppData\Local\Programs\Python\Python37\python.exe C:/Users/Administrator/Desktop/py/test.py DEBUG:selenium.webdriver.remote.remote_connection:POST http://127.0.0.1:3633/session {"capabilities": {"firstMatch": [{}], "alwaysMatch": {"browserName": "firefox", "acceptInsecureCerts": true}}, "desiredCapabilities": {"browserName": "firefox", "acceptInsecureCerts": true, "marionette": true}} DEBUG:selenium.webdriver.remote.remote_connection:b'{"value": {"sessionId":"84ddc3fe-d7af-4d2e-a5bb-a7408100b3aa","capabilities":{"acceptInsecureCerts":true,"browserName":"firefox","browserVersion":"61.0","moz:accessibilityChecks":false,"moz:headless":false,"moz:processID":18244,"moz:profile":"C:\\\\Users\\\\Administrator\\\\AppData\\\\Local\\\\Temp\\\\rust_mozprofile.OvsTh3aeSztY","moz:useNonSpecCompliantPointerOrigin":false,"moz:webdriverClick":true,"pageLoadStrategy":"normal","platformName":"windows_nt","platformVersion":"10.0","rotatable":false,"timeouts":{"implicit":0,"pageLoad":300000,"script":30000}}}}' DEBUG:selenium.webdriver.remote.remote_connection:Finished Request DEBUG:selenium.webdriver.remote.remote_connection:POST http://127.0.0.1:3633/session/84ddc3fe-d7af-4d2e-a5bb-a7408100b3aa/url {"url": "http://www.baidu.com"} DEBUG:selenium.webdriver.remote.remote_connection:b'{"value": null}' DEBUG:selenium.webdriver.remote.remote_connection:Finished Request DEBUG:selenium.webdriver.remote.remote_connection:POST http://127.0.0.1:3633/session/84ddc3fe-d7af-4d2e-a5bb-a7408100b3aa/element {"using": "css selector", "value": "[id=\"kw\"]"} DEBUG:selenium.webdriver.remote.remote_connection:b'{"value":{"element-6066-11e4-a52e-4f735466cecf":"2f5d6f4b-81aa-425f-9a12-9b6b1b81c602"}}' DEBUG:selenium.webdriver.remote.remote_connection:Finished Request DEBUG:selenium.webdriver.remote.remote_connection:POST http://127.0.0.1:3633/session/84ddc3fe-d7af-4d2e-a5bb-a7408100b3aa/element/2f5d6f4b-81aa-425f-9a12-9b6b1b81c602/value {"text": "selenium", "value": ["s", "e", "l", "e", "n", "i", "u", "m"], "id": "2f5d6f4b-81aa-425f-9a12-9b6b1b81c602"} DEBUG:selenium.webdriver.remote.remote_connection:b'{"value": null}' DEBUG:selenium.webdriver.remote.remote_connection:Finished Request DEBUG:selenium.webdriver.remote.remote_connection:POST http://127.0.0.1:3633/session/84ddc3fe-d7af-4d2e-a5bb-a7408100b3aa/element {"using": "css selector", "value": "[id=\"su\"]"} DEBUG:selenium.webdriver.remote.remote_connection:b'{"value":{"element-6066-11e4-a52e-4f735466cecf":"d1ec924a-2bfa-4739-b3a0-e3f721319bee"}}' DEBUG:selenium.webdriver.remote.remote_connection:Finished Request DEBUG:selenium.webdriver.remote.remote_connection:POST http://127.0.0.1:3633/session/84ddc3fe-d7af-4d2e-a5bb-a7408100b3aa/element/d1ec924a-2bfa-4739-b3a0-e3f721319bee/click {"id": "d1ec924a-2bfa-4739-b3a0-e3f721319bee"} DEBUG:selenium.webdriver.remote.remote_connection:b'{"value": null}' DEBUG:selenium.webdriver.remote.remote_connection:Finished Request DEBUG:selenium.webdriver.remote.remote_connection:DELETE http://127.0.0.1:3633/session/84ddc3fe-d7af-4d2e-a5bb-a7408100b3aa {} DEBUG:selenium.webdriver.remote.remote_connection:b'{"value": null}' DEBUG:selenium.webdriver.remote.remote_connection:Finished Request Process finished with exit code 0
basicConfig 所捕捉的log信息。不過basicConfig()開啓的debug模式只能捕捉到客戶端向服務器發送的 POST 請求,而沒法獲取服務器所返回的應答信息。Selenium Server 能夠獲取到更詳細的請求與應答信息。