Selenium Python

這篇文章在介紹官網的同時使用了比較多的腳本示例,示例里遇到的問題有部分在本篇文章進行了解釋,還有一篇文章專門記錄了問題。希望可以給予初學者更多的幫助。

1. Installation

1.1. Introduction

Selenium Python提供了一個簡單的API,可以使用Selenium WebDriver編寫功能/驗收(acceptance testing :驗收測試)測試。通過Selenium Python API,您可以以一種直觀的方式訪問Selenium WebDriver的所有功能。

Selenium Python提供了一個方便的API來訪問像Firefox、Ie、Chrome、Remote等的Selenium webdriver。當前支持的Python版本是2.7、3.5和以上。

該文檔解釋了Selenium 2 WebDriver API。這里不包括Selenium 1 / Selenium RC API。

1.2.下載 Python 內置的 Selenium

您可以從PyPI頁面下載Selenium。但是,更好的方法是使用pip來安裝selenium包。Python 3.6在標準庫中有pip。使用pip安裝selenium命令:
pip3 install selenium

You may consider using virtualenv to create isolated Python environments. Python 3.6 has pyvenv which is almost same as virtualenv.

1.3. 驅動(Drivers)

Selenium需要一個驅動程序與所選的瀏覽器進行交互。例如,Firefox需要geckodriver,只有安裝了驅動,腳本才能正常運行。確保它在正確的路徑(path),例如,把它放在:/usr/bin 或 /usr/local/bin。

如果你沒有安裝驅動或者驅動沒有在正確的路徑,執行腳本時會看到如下報錯:
WebDriverException: Message: ‘geckodriver’ executable needs to be in PATH.

常見瀏覽器驅動:

瀏覽器 驅動
Chrome: https://sites.google.com/a/chromium.org/chromedriver/downloads
Edge: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/
Firefox: https://github.com/mozilla/geckodriver/releases
Safari: https://webkit.org/blog/6900/webdriver-support-in-safari-10/

1.4.對于一些 Windows 用戶的說明

1、安裝 Python3.6

2、通過 cmd 安裝 selenium
C:\Python35\Scripts\pip.exe install selenium

現在,您可以使用Python運行您的測試腳本。例如,如果您創建了一個基于Selenium的腳本并將其保存在C:\my_selenium_script.py,你可以這樣運行:
C:\Python35\python.exe C:\my_selenium_script.py

1.5. 下載Selenium server

Selenium server只在你需要遠程控制 WebDriver 的時候才會用到。

有關詳細信息,請參閱2.5節。如果你是初學者,你可以跳過這一節,繼續下一章。

Selenium服務器是一個Java程序。建議使用Java運行時環境(JRE) 1.6或更新版本運行Selenium服務器。

2.開始使用

2.1.用法簡單示例

from selenium import webdriver
from selenium.webdriver.common.keys import Keys

driver = webdriver.Firefox()
driver.get("http://www.python.org")
assert "Python" in driver.title
elem = driver.find_element_by_name("q")
elem.clear()
elem.send_keys("pycon")
elem.send_keys(Keys.RETURN)
assert "No results found." not in driver.page_source
driver.close()

如果該腳本的文件名是python_org_search.py,那么執行該腳本可以使用使用以下命令:
python python_org_search.py

2.2示例說明

selenium.webdriver module 提供了所有的webdriver實現。目前支持的WebDriver實現是Firefox、Chrome、IE和Remote。Keys class提供鍵盤上的鍵,如RETURN、F1、ALT等。

from selenium import webdriver
from selenium.webdriver.common.keys import Keys

接下來,創建Firefox WebDriver的實例(對象)。

driver = webdriver.Firefox()

driver.get 方法將導航到URL提供的頁面。WebDriver將等待頁面完全加載(即“onload”事件已經啟動),然后才將控制返回給您的測試或腳本。值得注意的是,如果您的頁面在加載時使用了大量AJAX,那么WebDriver可能不知道它何時完全加載了。

driver.get("http://www.python.org")

下一行是斷言,以確認標題中有“Python”字:

assert "Python" in driver.title

WebDriver提供了多種使用find_element_by_*方法查找元素的方法。例如,輸入文本元素可以通過使用find_element_by_name方法的名稱屬性來定位。找到元素的詳細解釋可以在“查找元素”一章中找到:

elem = driver.find_element_by_name("q")

接下來,我們將sending keys,這類似于使用鍵盤輸入鍵。可以使用從selenium.webdriver.common.keys中導入的密鑰類發送特殊的密鑰。為了安全起見,我們將首先清除輸入字段中的任何預填充文本(例如,“Search”),因此它不會影響我們的搜索結果:

elem.clear()
elem.send_keys("pycon")
elem.send_keys(Keys.RETURN)

提交頁面后,如果有結果,就應該得到結果。為了確保找到一些結果,做出一個斷言:

assert "No results found." not in driver.page_source

最后,瀏覽器窗口關閉。您也可以調用quit方法而不是close。退出將退出整個瀏覽器,而關閉“將關閉一個選項卡,但如果只有一個選項卡是打開的,默認情況下大多數瀏覽器將完全退出。

driver.close()
driver.quit()

2.3.使用 Selenium 編寫測試用例

Selenium主要用于編寫測試用例。selenium包本身并沒有提供測試工具/框架。您可以使用Python的unittest模塊編寫測試用例。

在本章中,我們將unittest作為選擇的框架。下面是使用unittest模塊的修改后的示例。這是對python.org搜索功能的測試:

import unittest
from selenium import webdriver
from selenium.webdriver.common.keys import Keys

class PythonOrgSearch(unittest.TestCase):

    def setUp(self):
        self.driver = webdriver.Firefox()

    def test_search_in_python_org(self):
        driver = self.driver
        driver.get("http://www.python.org")
        self.assertIn("Python", driver.title)
        elem = driver.find_element_by_name("q")
        elem.send_keys("pycon")
        elem.send_keys(Keys.RETURN)
        assert "No results found." not in driver.page_source


    def tearDown(self):
        self.driver.close()

if __name__ == "__main__":
    unittest.main()

執行該腳本:

python test_python_org_search.py
.
-----------------------------------

Ran 1 test in 15.566s

OK

上面的結果顯示腳本執行成功。

2.4.舉例說明

最初,所需的所有基本模塊都是導入的。unittest模塊是基于Java JUnit的內置Python。這個模塊提供了組織測試用例的框架。Selenium webdriver模塊提供了所有的webdriver實現。目前支持的WebDriver實現是Firefox、Chrome、Ie和Remote。keys類提供鍵盤上的keys,如RETURN、F1、ALT等。

import unittest
from selenium import webdriver
from selenium.webdriver.common.keys import Keys

測試用例類繼承自unittest.TestCase。從TestCase類繼承的方法是告訴unittest模塊這是一個測試用例:

class PythonOrgSearch(unittest.TestCase):

setUp是初始化的一部分,這個方法將在您將要在這個測試用例類中編寫的每個測試函數之前調用。這里您正在創建Firefox WebDriver的實例。

def setUp(self):
    self.driver = webdriver.Firefox()

這是測試用例方法。測試用例方法應該總是從字符test開始。該方法中的第一行為在setUp方法中創建的驅動對象創建一個本地引用。

def test_search_in_python_org(self):
    driver = self.driver

driver.get方法將跳轉到您所指定的地址:

driver.get("http://www.python.org")

下一行是斷言,以確認標題中有“Python”字:

self.assertIn("Python", driver.title)

WebDriver提供了多種使用find_element_by_*方法查找元素的方法。例如,輸入文本元素可以通過使用find_element_by_name方法的名稱屬性來定位。尋找元素的詳細解釋可以在“查找元素”一章中找到:

elem = driver.find_element_by_name("q")

接下來,我們將sending keys,這類似于使用鍵盤輸入鍵。特殊的鍵可以使用Keys class imported from selenium.webdriver.common.keys:

from selenium.webdriver.common.keys import Keys
elem.send_keys("pycon")
elem.send_keys(Keys.RETURN)

提交頁面后,如果有任何搜索結果,您應該得到搜索結果。為了確保找到一些結果,做出一個斷言:

assert "No results found." not in driver.page_source

每次測試方法后,將調用tearDown方法。這是一個做所有清理動作的地方。在當前的方法中,瀏覽器窗口是關閉的。您也可以調用quit方法而不是close。退出將退出整個瀏覽器,而close將關閉一個選項卡,但如果它是唯一打開的選項卡,默認情況下大多數瀏覽器將完全退出。

def tearDown(self):
    self.driver.close()

最后幾行是執行測試集的模板代碼(boiler plate code):

if __name__ == "__main__":
    unittest.main()

2.5. 使用遠程 WebDriver(remote WebDriver)

要使用遠程WebDriver,應該運行Selenium服務器。要運行服務器,請使用以下命令:

java -jar selenium-server-standalone-2.x.x.jar

在運行Selenium服務器時,您可以看到這樣一條消息:

15:43:07.541 INFO - RemoteWebDriver instances should connect to: http://127.0.0.1:4444/wd/hub

上面這行命令表示您可以使用這個URL連接到遠程WebDriver。下面是一些例子:

from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

driver = webdriver.Remote(
   command_executor='http://127.0.0.1:4444/wd/hub',
   desired_capabilities=DesiredCapabilities.CHROME)

driver = webdriver.Remote(
   command_executor='http://127.0.0.1:4444/wd/hub',
   desired_capabilities=DesiredCapabilities.OPERA)

driver = webdriver.Remote(
   command_executor='http://127.0.0.1:4444/wd/hub',
   desired_capabilities=DesiredCapabilities.HTMLUNITWITHJS)

desired capabilities是一個字典,因此,您不必使用默認字典,您可以顯式地指定值:

driver = webdriver.Remote(
   command_executor='http://127.0.0.1:4444/wd/hub',
   desired_capabilities={'browserName': 'htmlunit',
                         'version': '2',
                        'javascriptEnabled': True})

desired capabilities和 Appium 中的desired capabilities是一樣的。

3.導航(Navigating)

你要做的第一件事是導航到一個鏈接。通常的方法是調用get方法:

driver.get("http://www.google.com")

3.1.與頁面元素進行交互

僅僅能夠跳轉到某一個頁面并不是很有用。我們真正想做的是與頁面交互,或者更確切地說,是頁面內的HTML元素。首先,我們需要找到一個。WebDriver提供了多種查找元素的方法。例如,給定一個元素定義為:

<input type="text" name="passwd" id="passwd-id" />

定位這個 元素你可以使用以下方法:

element = driver.find_element_by_id("passwd-id")
element = driver.find_element_by_name("passwd")
element = driver.find_element_by_xpath("http://input[@id='passwd-id']")

你也可以通過它的文本尋找鏈接,但是要小心!文本必須是精確的匹配!在WebDriver中使用XPATH時也要小心。如果有多個元素與查詢匹配,那么只返回第一個元素。如果沒有發現任何東西,就會出現報錯:NoSuchElementException

比如在輸入框內輸入文本的 API:

element.send_keys("some text")

您可以使用“Keys”類模擬按下箭頭鍵:

element.send_keys(" and some", Keys.ARROW_DOWN)

有些文本輸入內已經有了提示語,比如淘寶京東在搜索框內會預置一些熱門商品,這時候如果你需要搜索其他商品就需要把預置的文本清除再輸入自己的內容,清除文本的 API:

element.clear()

3.2.填寫表單

我們已經看到了如何將文本輸入到文本區域或文本字段中,但是其他元素又如何呢?您可以“切換”(toggle)下拉的狀態,您可以使用“setSelected”來設置一些類似于選擇的選項標記。處理選擇標簽并不是很糟糕:

element = driver.find_element_by_xpath("http://select[@name='name']")
all_options = element.find_elements_by_tag_name("option")
for option in all_options:
    print("Value is: %s" % option.get_attribute("value"))
    option.click()

這將在頁面上找到第一個“SELECT”元素,然后依次遍歷每個選項,打印出它們的值,然后依次選擇它們。

正如您所看到的,這并不是處理SELECT元素的最有效方法。WebDriver的支持類包括一個叫做“Select”的類,它提供了與這些類交互的有用方法:

from selenium.webdriver.support.ui import Select
select = Select(driver.find_element_by_name('name'))
select.select_by_index(index)
select.select_by_visible_text("text")
select.select_by_value(value)

WebDriver還提供了取消選擇所有選項的功能:

select = Select(driver.find_element_by_id('id'))
select.deselect_all()

假設在一個測試中,我們需要所有缺省選擇選項的列表(list),Select類提供了一個屬性方法,該方法返回一個列表:

select = Select(driver.find_element_by_xpath("http://select[@name='name']"))
all_selected_options = select.all_selected_options

獲得所有可用選項:

options = select.options

填寫完表單后,您可能需要提交它。一種方法是找到“提交”按鈕并點擊它:

# Assume the button has the ID "submit" :)
driver.find_element_by_id("submit").click()

或者,WebDriver在每個元素上都有“提交”的便利方法。如果在表單中調用這個元素,WebDriver將遍歷DOM,直到它找到封閉表單,然后調用submit。如果元素不在表單中,那么NoSuchElementException將被提高:

element.submit()

3.3.拖拽

您可以使用拖放,或者將一個元素移動到一定數量,或者切換到另一個元素:

element = driver.find_element_by_name("source")#起點
target = driver.find_element_by_name("target")#終點

from selenium.webdriver import ActionChains
action_chains = ActionChains(driver)
action_chains.drag_and_drop(element, target).perform()

3.4.Moving between windows and frames

網頁中經常會遇到當前頁面上有一個或多個小表單(frame)嵌套的情況,而我們在定位的時候一般只能定位到 driver.get(xxx)的頁面,如果想要定位到表單上的元素,那就需要先切換到目標表單。有時候點擊某一個鏈接會彈出一個新的窗口(windows),要在不同窗口上定位元素,需要在不同窗口間進行切換。
WebDriver支持使用“switch_to_window”方法在指定的窗口之間移動:

driver.switch_to_window("windowName")

所有對驅動程序的調用將被解釋為指向特定的窗口。但是你怎么知道窗戶的名字呢?看一下打開的javascript或鏈接:

<a href="somewhere.html" target="windowName">Click here to open a new window</a>

或者,您可以將“window handle”傳遞給“switch_to_window()”方法。知道了這一點,就可以遍歷每個打開的窗口:

for handle in driver.window_handles:
    driver.switch_to_window(handle)

作者注:HANDLE(句柄)是Windows操作系統中的一個概念。在Windows程序中,有各種各樣的資源(窗口、圖標、光標等),系統在創建這些資源時會為它們分配內存,并返回標示這些資源的標示號,即句柄。(摘自互動百科)

舉例說明,窗口之間的切換:

Search Windows
all handles

#coding=utf-8

import sys
#解決亂碼問題
reload(sys)
sys.setdefaultencoding("utf-8")


sys.path.append("/usr/local/lib/python3.6/site-packages/")


from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By 
import time
#driver = webdriver.Firefox()
#把chromedriver和要執行的.py 文件放在一個文件夾下面
driver = webdriver.Chrome('./chromedriver')


driver.implicitly_wait(10)

driver.get("http://www.baidu.com")

#獲得百度搜索窗口句柄
search_windows = driver.current_window_handle

print('current_window_handle is %s '%search_windows)

time.sleep(10)

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 != search_windows:
        driver.switch_to.window(handle)
        print('Now register window')
        driver.find_element_by_name('userName').send_keys('username935')
        driver.find_element_by_name('phone').send_keys('13976857789')

        #下面被注釋的代碼都不能定位到元素,只有最后一行代碼可以!!!!!
        #ActionChains

        #driver.find_element_by_name('password').send_keys('password')
        #left_click = driver.find_element_by_name('password')
        print("輸入密碼")
        #ActionChains(driver).click(left_click).send_keys('123654').perform()
        #driver.find_element_by_css_selector("input[type=password]").send_keys("123456")
        #driver.find_element_by_xpath("http://input[@type='password']").send_keys("123456")

        #element = WebDriverWait(driver,20).until(
        #   EC.presence_of_element_located((By.XPATH,"(//input[@type='password')")))

        #element = WebDriverWait(driver,10).until(
        #   EC.element_to_be_clickable((By.XPATH,"(//input[@type='password')")))
        #element.click().send_keys("1234567")
        
      #只有這行代碼可以定位到輸入密碼框元素!!!!!
        ActionChains(driver).click(driver.find_element(By.ID,'TANGRAM__PSP_3__password')).send_keys("1234567").perform()


        print("密碼輸入了嗎?")
        time.sleep(10)

        #......

#回到搜索窗口
for handle in all_handles:
    if handle == search_windows:
        driver.switch_to.window(handle)
        print('Now search window!')

        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(3)

#driver.quit()



你也可以從一幀到另一幀(或進入iframe,定義一個表單一般使用標簽 iframe):

driver.switch_to_frame("frameName")

通過將路徑與一個點分開來訪問subframes是可能的,您也可以通過它的索引指定frame。那就是:

driver.switch_to_frame("frameName.0.child")

將會轉到名為“frameName”的frame的第一個subframe的“child”框架。All frames are evaluated as if from *top*.

一旦我們完成了frames的工作,我們就必須回到可以使用的父frame:

driver.switch_to_default_content()

3.5.彈出對話框(Popup dialogs)

Selenium WebDriver內置支持處理彈出對話框。當您觸發了打開彈出窗口的操作之后,您可以通過以下方式訪問alert:

alert = driver.switch_to_alert()

這將返回當前打開的alert對象。通過這個對象,您現在可以接受、取消、讀取它的內容,甚至可以鍵入一個提示符。這個接口在alerts、confirm和prompts上同樣有效。更多信息請參考API文檔。

3.6.導航:定位和訪問歷史

早些時候,我們使用“get”命令(driver.get(“http://www.example.com”))導航到一個頁面,WebDriver有許多較小的、任務集中的接口,導航是一項很有用的任務。要導航到一個頁面,您可以使用get方法:

driver.get("http://www.example.com")

訪問網頁的前一頁和后一頁:

driver.forward()
driver.back()

請注意,此功能完全取決于底層驅動程序。如果你習慣了一個瀏覽器的行為,那么當你調用這些方法時,可能會發生意想不到的事情。

3.7. Cookies

在我們開始下一步之前,您可能會對如何使用cookie感興趣。首先,你需要在cookie有效的領域:

# Go to the correct domain
driver.get("http://www.example.com")

# Now set the cookie. This one's valid for the entire domain
cookie = {‘name’ : ‘foo’, ‘value’ : ‘bar’}
driver.add_cookie(cookie)

# And now output all the available cookies for the current URL
driver.get_cookies()

4.元素定位(Locating Elements)

在頁面中定位元素有多種策略。您可以使用最適合您的案例。Selenium提供了以下方法來定位頁面中的元素:

  • find_element_by_id
  • find_element_by_name
  • find_element_by_xpath
  • find_element_by_link_text
  • find_element_by_partial_link_text
  • find_element_by_tag_name
  • find_element_by_class_name
  • find_element_by_css_selector

定位一組元素(這些方法將返回一個列表(list)):

  • find_elements_by_name
  • find_elements_by_xpath
  • find_elements_by_link_text
  • find_elements_by_partial_link_text
  • find_elements_by_tag_name
  • find_elements_by_class_name
  • find_elements_by_css_selector

除了上面給出的公共方法之外,還有兩種私有方法,它們可能對頁面對象中的定位器有用。這是兩個私有方法:find_element和find_elements。

from selenium.webdriver.common.by import By

driver.find_element(By.XPATH, '//button[text()="Some text"]')
driver.find_elements(By.XPATH, '//button')

這些屬性可用于類:

ID = "id"
XPATH = "xpath"
LINK_TEXT = "link text"
PARTIAL_LINK_TEXT = "partial link text"
NAME = "name"
TAG_NAME = "tag name"
CLASS_NAME = "class name"
CSS_SELECTOR = "css selector"

定位一組元素

針對上面提到的定位一組元素,官網上暫時沒有過多講解,在這里我補充一點,這個方法不常用,但是有時候會幫助到我們。定位一組元素的場景一般是:

  • 批量操作元素,比如勾選某個頁面上所有的復選框;
  • 先獲取一組元素,再從這一組元素中獲取目標元素。

比如在某一個頁面有一組復選框,他們的tag都是 input,但是該頁面還有其他元素的tag 也是 input,但是這兩種元素的 attribute 有所不同,此時就可以很方便的使用該方法:

#coding=utf-8
import sys
#print(sys.path)
sys.path.append('/usr/local/lib/python3.6/site-packages/')

from selenium import webdriver
import time

driver = webdriver.Firefox()
driver.get(xxx)

#選擇頁面上所有的 tag name 為 input 的元素
inputs = driver .find_elements_by_tag_name("input")

#然后從中過濾出 type 為 checkbox 的元素,單擊勾選
for input in inputs:
    if input.get_attribute("type") == "checkbox":
        input.click()

        time.sleep(1)

driver.quit()

除此之外,我們還可以使用 XPath 和 CSS 來直接定位屬性值type==checkbox 的元素。

#coding=utf-8
import sys
#print(sys.path)
sys.path.append('/usr/local/lib/python3.6/site-packages/')

from selenium import webdriver
import time

driver = webdriver.Firefox()
driver.get(xxx)

#通過 XPath 定位到 type==checkbox 的元素
#checkboxes = driver.find_elements_by_xpath("http://input[@type='checkbox']")

#通過 CSS 定位到 type==checkbox 的元素
checkboxes = driver.find_elements_by_css_selector("input[type=checkbox]")

for checkbox in checkboxes:
    checkbox.click()
    time.sleep(1)

#打印當前頁面上 type=checkbox的元素個數
print("checkboxes的個數是:%s"%len(checkboxes))

#取消選中最后一個 checkbox
driver.find_elements_by_css_selector("input[type=checkbox]").pop().click()

pop()方法用于獲取一個列表(list)中的某一個元素,比如 pop(0)。不加索引則認為是列表中的最后一個。

4.1. Locating by Id

當您知道元素的id屬性時,請使用它。使用此策略,將返回與該位置匹配的id屬性值的第一個元素。如果沒有元素具有匹配的id屬性,則會報錯NoSuchElementException
比如下面一段網頁源碼:

<html>
 <body>
  <form id="loginForm">
   <input name="username" type="text" />
   <input name="password" type="password" />
   <input name="continue" type="submit" value="Login" />
  </form>
 </body>
<html>

表單元素可以這樣定位:

login_form = driver.find_element_by_id('loginForm')

作者注:對于有些 網頁的元素id是動態不確定的,此時就不能使用該方法。

4.2.Locating by Name

當您知道元素的name屬性時,請使用它。使用此策略,將返回與位置匹配的name屬性值的第一個元素。如果沒有元素具有匹配的name屬性,則會報錯NoSuchElementException

比如下面一段網頁源碼:

<html>
 <body>
  <form id="loginForm">
   <input name="username" type="text" />
   <input name="password" type="password" />
   <input name="continue" type="submit" value="Login" />
   <input name="continue" type="button" value="Clear" />
  </form>
</body>
<html>

username和password元素可以這樣定位:

username = driver.find_element_by_name('username')
password = driver.find_element_by_name('password')

這將使“登錄”按鈕出現在“清除”按鈕之前:

continue = driver.find_element_by_name('continue')

4.3.Locating by XPath

XPath是用于在XML文檔中定位節點的語言。由于HTML可以是XML (XHTML)的實現,Selenium用戶可以利用這種強大的語言來對web應用程序中的元素進行定位。XPath擴展了(以及支持)通過id或name屬性定位的簡單方法,并打開了各種新的可能性,例如在頁面上定位第三個復選框。

使用XPath的一個主要原因是,當您沒有為希望定位的元素擁有合適的id或name屬性時。可以使用XPath以絕對項(不建議)定位元素,或者相對于具有id或name屬性的元素。XPath定位器還可以用于通過id和名稱以外的屬性指定元素。

絕對xpath包含了來自根(html)的所有元素的位置,因此,只有對應用程序進行最輕微的調整,結果可能會失敗。通過查找具有id或name屬性(理想的父元素)的附近元素,您可以根據關系定位目標元素。這種情況不太可能發生改變,并且可以使您的測試更加健壯。

比如下面一段網頁源碼:

<html>
 <body>
  <form id="loginForm">
   <input name="username" type="text" />
   <input name="password" type="password" />
   <input name="continue" type="submit" value="Login" />
   <input name="continue" type="button" value="Clear" />
  </form>
</body>
<html>

表單元素可以這樣定位:

login_form = driver.find_element_by_xpath("/html/body/form[1]")
login_form = driver.find_element_by_xpath("http://form[1]")
login_form = driver.find_element_by_xpath("http://form[@id='loginForm']")

1、絕對路徑(如果HTML稍有更改,則會中斷)
2、HTML中的第一個表單元素。
3、帶有屬性id和值loginForm的表單元素。

username元素可以這樣定位:

username = driver.find_element_by_xpath("http://form[input/@name='username']")
username = driver.find_element_by_xpath("http://form[@id='loginForm']/input[1]")
username = driver.find_element_by_xpath("http://input[@name='username']")

1、第一個表單元素,帶有一個名為name和值username的輸入子元素。
2、表單元素的第一個輸入子元素,帶有名為id的屬性和值loginForm。
3、第一個輸入元素的屬性命名為' name '和值username。

“Clear”按鈕元素可以這樣定位:

clear_button = driver.find_element_by_xpath("http://input[@name='continue'][@type='button']")
clear_button = driver.find_element_by_xpath("http://form[@id='loginForm']/input[4]")

1、輸入屬性命名的名稱和值繼續,屬性命名type和值button。
2、表單元素的第四個輸入子元素,帶有名為id和value loginForm的屬性。

這些例子包括一些基本知識,但是為了了解更多,推薦以下參考:

還有一些非常有用的附加組件可以幫助發現元素的XPath:

  • XPath Checker - suggests XPath and can be used to test XPath results.
  • Firebug - XPath suggestions are just one of the many powerful features of this very useful add-on.
  • XPath Helper - for Google Chrome

4.4.通過鏈接文本定位超鏈接。

用這個當你知道鏈接文本中使用錨標記。這個策略,鏈接文本值的第一個元素將返回匹配的位置。如果沒有元素有一個匹配的鏈接文本屬性,將會報錯NoSuchElementException

比如下面一段源碼:

<html>
 <body>
  <p>Are you sure you want to do this?</p>
  <a href="continue.html">Continue</a>
  <a href="cancel.html">Cancel</a>
</body>
<html>

continue.html鏈接可以這樣定位:

continue_link = driver.find_element_by_link_text('Continue')
continue_link = driver.find_element_by_partial_link_text('Conti')

4.5.Locating Elements by Tag Name

當您想要通過tag name定位元素時,請使用它。使用此策略,將返回帶有給定tag name的第一個元素。如果沒有元素具有匹配的標記名,則會報錯NoSuchElementException

比如下面一段源碼:

<html>
 <body>
  <h1>Welcome</h1>
  <p>Site content goes here.</p>
</body>
<html>

標題(h1)元素可以這樣定位:

heading1 = driver.find_element_by_tag_name('h1')

4.6.Locating Elements by Class Name

當您想要按class屬性名稱定位一個元素時,請使用它。使用此策略,將返回匹配class屬性名的第一個元素。如果沒有元素具有匹配的類屬性名,則會報錯NoSuchElementException

比如下面一段源碼:

<html>
 <body>
  <p class="content">Site content goes here.</p>
</body>
<html>

“p”元素可以這樣定位:

content = driver.find_element_by_class_name('content')

4.7.Locating Elements by CSS Selectors

當您想通過CSS選擇器語法定位一個元素時,請使用它。使用此策略,將返回匹配的CSS選擇器的第一個元素。如果沒有元素具有匹配的CSS選擇器,將會報錯NoSuchElementException

比如下面一段網頁源碼:

<html>
 <body>
  <p class="content">Site content goes here.</p>
</body>
<html>

“p”元素可以這樣定位:

content = driver.find_element_by_css_selector('p.content')

Sauce Labs has good documentation on CSS selectors.

5.Waits

現在大多數web應用程序都使用AJAX技術。當瀏覽器加載一個頁面時,該頁面中的元素可能在不同的時間間隔加載。這使得定位元素變得困難:如果一個元素還沒有出現在DOM中,定位函數將會拋出一個ElementNotVisibleException異常。使用等待,我們可以解決這個問題。等待在執行的操作之間提供了一些富余的時間——主要是定位一個元素或與元素的任何其他操作。

Selenium Webdriver提供了兩種類型的等待——隱式和顯式。顯式等待使WebDriver等待某個條件發生,然后再繼續執行。隱式等待使WebDriver在嘗試定位元素時,對DOM進行了一定時間的輪詢。

5.1.顯式等待(Explicit Waits)

顯式等待是您定義的代碼,以等待在代碼進一步執行之前等待某個條件。這種情況的極端情況是time.sleep(),它將條件設置為一個確切的時間等待。這里提供了一些方便的方法,可以幫助您編寫只需要等待的代碼。WebDriverWait與ExpectedCondition相結合是實現這一目標的一種方式。

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait

#通過關鍵字 as 將expected_conditions重命名為 EC
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Firefox()
driver.get("http://somedomain/url_that_delays_loading")
try:
    element = WebDriverWait(driver, 10).until(
#調用presence_of_element_located()方法判斷元素是否存在
        EC.presence_of_element_located((By.ID, "myDynamicElement"))
    )
finally:
    driver.quit()

這在拋出TimeoutException之前等待10秒,除非它在10秒內找到返回的元素。默認情況下,WebDriverWait每500毫秒調用一次ExpectedCondition,直到成功返回。一個成功的返回是ExpectedCondition類型是布爾返回true或者不是所有其他ExpectedCondition類型的空返回值。

WebDriverWait(driver,timeout,poll_frequency=0.5,ignored_exceptions= None)

driver:瀏覽器驅動對象。
timeout:最長超時時間,默認以秒為單位。
poll_frequency:檢測的間隔時間,默認是0.5s 檢測一次。
ignored_exceptions:超時后的異常信息,默認拋 NoSuchElementException 異常。

WebDriverWait()一般和 until()或者 until_not()方法配合使用。

Expected Conditions

在自動化web瀏覽器時,經常會出現一些常見的情況。下面列出了每一個的名字。Selenium Python綁定提供了一些方便的方法(第7章),因此您不必自己編寫expected_condition類,也不必為它們創建自己的實用程序包。

  • title_is
  • title_contains
  • presence_of_element_located
  • visibility_of_element_located
  • visibility_of
  • presence_of_all_elements_located
  • text_to_be_present_in_element
  • text_to_be_present_in_element_value
  • frame_to_be_available_and_switch_to_it
  • invisibility_of_element_located
  • element_to_be_clickable
  • staleness_of
  • element_to_be_selected
  • element_located_to_be_selected
  • element_selection_state_to_be
  • element_located_selection_state_to_be
  • alert_is_present
from selenium.webdriver.support import expected_conditions as EC

wait = WebDriverWait(driver, 10)
element = wait.until(EC.element_to_be_clickable((By.ID, 'someid')))

expected_conditions模塊包含一組預定義的條件,可以與WebDriverWait一起使用。

自定義等待條件

你也可以創建自定義條件等待先前所有的便利方法適合您的需求。可以使用一個類創建一個自定義等條件與call方法條件不匹配時返回False。

class element_has_css_class(object):
  """An expectation for checking that an element has a particular css class.

  locator - used to find the element
  returns the WebElement once it has the particular css class
  """
  def __init__(self, locator, css_class):
    self.locator = locator
    self.css_class = css_class

  def __call__(self, driver):
    element = driver.find_element(*self.locator)   # Finding the referenced element
    if self.css_class in element.get_attribute("class"):
        return element
    else:
        return False

# Wait until an element with id='myNewInput' has class 'myCSSClass'
wait = WebDriverWait(driver, 10)
element = wait.until(element_has_css_class((By.ID, 'myNewInput'), "myCSSClass"))

5.2.隱式等待(Implicit Waits)

隱式等待告訴WebDriver在嘗試查找沒有立即可用的元素(或元素)時,對DOM進行輪詢。默認設置為0。設置后,隱式等待設置為WebDriver對象的生命。
eg1:

from selenium import webdriver

driver = webdriver.Firefox()
driver.implicitly_wait(10) # seconds
driver.get("http://somedomain/url_that_delays_loading")
myDynamicElement = driver.find_element_by_id("myDynamicElement")

eg2:

# -*- coding: utf-8 -*-
#!/usr/bin/env python3

import sys
#print(sys.path)
sys.path.append('/usr/local/lib/python3.6/site-packages/')

from selenium import webdriver  
from time import ctime
from selenium.common.exceptions import NoSuchElementException

driver = webdriver.Firefox()
#設置隱式等待為10s
driver.implicitly_wait(10)
driver.get("http://www.baidu.com")

try:
     print("當前時間:%s"%ctime())
#因為定位不到元素“kw22”,所以這里會出錯
     driver.find_element_by_id("kw22").send_keys("selenium")

except NoSuchElementException as e:
    print(u"異常信息:%s"%e)
finally:
    print(ctime())
    #driver.quit()


腳本執行結果如下:

當前時間:Sat May 26 10:40:59 2018
異常信息:Message: Unable to locate element: [id="kw22"]

Sat May 26 10:41:09 2018
[Finished in 15.7s]

try...except...finally...是一套錯誤處理機制。
當我們認為某些代碼可能會出錯時,就可以用try來運行這段代碼,如果執行出錯,則后續代碼不會繼續執行,而是直接跳轉至錯誤處理代碼,即except語句塊,執行完except后,如果有finally語句塊,則執行finally語句塊,至此,執行完畢。


6.頁面對象

本章是對頁面對象(page objects)設計模式的教程介紹。頁面對象表示您的測試正在交互的web應用程序用戶界面中的一個區域。

使用page objects模式的優點有:

  • 創建可跨多個測試用例共享的可重用代碼。
  • 減少重復代碼的數量。
  • 如果用戶界面發生變化,修復程序只需要在一個地方進行更改。

6.1. 測試用例

這里是一個測試用例,它在python.org網站上搜索一個單詞,并確保找到一些結果。

import unittest
from selenium import webdriver
import page

class PythonOrgSearch(unittest.TestCase):
"""A sample test class to show how page object works"""

def setUp(self):
    self.driver = webdriver.Firefox()
    self.driver.get("http://www.python.org")

def test_search_in_python_org(self):
    """
    Tests python.org search feature. Searches for the word "pycon" then verified that some results show up.
    Note that it does not look for any particular text in search results page. This test verifies that
    the results were not empty.
    """

    #Load the main page. In this case the home page of Python.org.
    main_page = page.MainPage(self.driver)
    #Checks if the word "Python" is in title
    assert main_page.is_title_matches(), "python.org title doesn't match."
    #Sets the text of search textbox to "pycon"
    main_page.search_text_element = "pycon"
    main_page.click_go_button()
    search_results_page = page.SearchResultsPage(self.driver)
    #Verifies that the results page is not empty
    assert search_results_page.is_results_found(), "No results found."

def tearDown(self):
    self.driver.close()

if name == "main":
unittest.main()

6.2.Page object classes

頁面對象(Page object)模式打算為每個web頁面創建一個對象。通過遵循這一技術,將創建測試代碼和技術實現之間的一層分離。

舉例說明(page.py):

from element import BasePageElement
from locators import MainPageLocators

class SearchTextElement(BasePageElement):
    """This class gets the search text from the specified locator"""

    #The locator for search box where search string is entered
    locator = 'q'


class BasePage(object):
    """Base class to initialize the base page that will be called from all pages"""

    def __init__(self, driver):
        self.driver = driver


class MainPage(BasePage):
    """Home page action methods come here. I.e. Python.org"""

    #Declares a variable that will contain the retrieved text
    search_text_element = SearchTextElement()

    def is_title_matches(self):
        """Verifies that the hardcoded text "Python" appears in page title"""
        return "Python" in self.driver.title

    def click_go_button(self):
        """Triggers the search"""
        element = self.driver.find_element(*MainPageLocators.GO_BUTTON)
        element.click()


class SearchResultsPage(BasePage):
    """Search results page action methods come here"""

    def is_results_found(self):
        # Probably should search for this text in the specific page
        # element, but as for now it works fine
        return "No results found." not in self.driver.page_source

6.3.Page elements

舉例說明(elements.py):

from selenium.webdriver.support.ui import WebDriverWait


class BasePageElement(object):
    """Base page class that is initialized on every page object class."""

    def __set__(self, obj, value):
        """Sets the text to the value supplied"""
        driver = obj.driver
        WebDriverWait(driver, 100).until(
            lambda driver: driver.find_element_by_name(self.locator))
        driver.find_element_by_name(self.locator).clear()
        driver.find_element_by_name(self.locator).send_keys(value)

    def __get__(self, obj, owner):
        """Gets the text of the specified object"""
        driver = obj.driver
        WebDriverWait(driver, 100).until(
            lambda driver: driver.find_element_by_name(self.locator))
        element = driver.find_element_by_name(self.locator)
        return element.get_attribute("value")

6.4. Locators(定位器)

其中一種做法是將定位字符串與被使用的位置分開。在本例中,同一頁的定位器屬于同一類。

舉例說明(locators.py):

from selenium.webdriver.common.by import By

class MainPageLocators(object):
    """A class for main page locators. All main page locators should come here"""
    GO_BUTTON = (By.ID, 'submit')

class SearchResultsPageLocators(object):
    """A class for search results locators. All search results locators should come here"""
    pass

7.WebDriver API

注意:這章不是官方文檔。但是本章涵蓋了Selenium WebDriver的所有接口。

推薦的 Import 樣式

from selenium import webdriver

導入了 webdriver 以后就可以使用以下這些類:

webdriver.Firefox
webdriver.FirefoxProfile
webdriver.Chrome
webdriver.ChromeOptions
webdriver.Ie
webdriver.Opera
webdriver.PhantomJS
webdriver.Remote
webdriver.DesiredCapabilities
webdriver.ActionChains
webdriver.TouchActions
webdriver.Proxy

特殊的 keys class(Keys)可以這樣導入:

from selenium.webdriver.common.keys import Keys

可以像這樣導入異常類(用下面給出的實際類名替換TheNameOfTheExceptionClass):

from selenium.common.exceptions import [TheNameOfTheExceptionClass]

使用 API 的一些約定俗成的東西

一些屬性是可調用的(或方法),其他屬性是不可調用的(屬性)。所有可調用的屬性都以圓括號結束。

關于屬性的一個示例:

  • current_url

當前加載頁面的URL。

使用方法:

driver.current_url

關于方法的一個示例:

  • close()
    關閉當前窗口
    使用方法:
driver.close()

7.1 異常(Exceptions)

異常可能會出現在 Webdriver 代碼的任何一個地方。

異常1
selenium.common.exceptions.ElementClickInterceptedException(msg=None, screen=None, stacktrace=None)
?? Bases: selenium.common.exceptions.WebDriverException

元素單擊命令無法完成,因為接收事件的元素遮蔽了請求單擊的元素。


異常2
selenium.common.exceptions.ElementNotInteractableException(msg=None, screen=None, stacktrace=None)
?? Bases: selenium.common.exceptions.InvalidElementStateException

拋出該異常的原因:元素雖然存在但是還未加載出來,所以不能對其進行操作。
解決辦法:time.sleep()


異常3
selenium.common.exceptions.ElementNotSelectableException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.InvalidElementStateException

報錯原因:嘗試選擇一個不能被選中的元素。


異常4
selenium.common.exceptions.ElementNotVisibleException(msg=None, screen=None, stacktrace=None)
?? Bases:selenium.common.exceptions.InvalidElementStateException

報錯原因:元素雖然存在,但是不可見,所以不能對其進行操作。
在嘗試單擊或讀取隱藏于視圖的元素的文本時,通常會遇到這種情況。


異常5
selenium.common.exceptions.ErrorInResponseException(response, msg)
??Bases:selenium.common.exceptions.WebDriverException

報錯原因:服務器端出現錯誤。
這可能發生在與firefox擴展或遠程驅動服務器通信時。

init(response, msg)


異常6
selenium.common.exceptions.ImeActivationFailedException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.WebDriverException

報錯原因:激活輸入法失敗。


異常7
selenium.common.exceptions.ImeNotAvailableException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.WebDriverException

報錯原因:輸入法不支持當前設備。
如果機器上沒有該輸入法支持,則會為每一個與該輸入法相關的方法調用拋出這個異常。


異常8
selenium.common.exceptions.InsecureCertificateException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.WebDriverException

報錯原因:腳本中涉及的證書過期或者不安全。


異常9
selenium.common.exceptions.InvalidArgumentException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.WebDriverException

報錯原因:傳遞給命令的參數是無效的。


異常10
selenium.common.exceptions.InvalidCookieDomainException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.WebDriverException

報錯原因:嘗試在不同的域下添加一個cookie,而不是當前的URL。


異常11
selenium.common.exceptions.InvalidCoordinatesException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.WebDriverException

報錯原因:提供給交互操作的坐標無效。


異常12
selenium.common.exceptions.InvalidElementStateException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.WebDriverException

報錯原因:


異常13
selenium.common.exceptions.InvalidSelectorException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.NoSuchElementException

報錯原因:當用于查找元素的選擇器不返回WebElement時拋出。當前,只有當選擇器是xpath表達式時才會出現這種情況,而且它要么是語法無效的(即它不是xpath表達式),要么是表達式不選擇WebElements(例如“count(//input)”)。


異常14
selenium.common.exceptions.InvalidSessionIdException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.WebDriverException

報錯原因:如果給定的會話id不在活動會話列表中,這意味著會話要么不存在,要么不活動。


異常15
selenium.common.exceptions.InvalidSwitchToTargetException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.WebDriverException

報錯原因:試圖切換的窗口不存在。


異常16
selenium.common.exceptions.JavascriptException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.WebDriverException

報錯原因:在執行用戶提供的JavaScript時發生錯誤。


異常17
selenium.common.exceptions.MoveTargetOutOfBoundsException(msg=None, screen=None, stacktrace=None)

??Bases: selenium.common.exceptions.WebDriverException

報錯原因:提供給ActionChainsmovable()方法的目標無效時,異常將拋出,例如:超出文件外。


異常18
selenium.common.exceptions.NoAlertPresentException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.WebDriverException

報錯原因:alert 視圖還未加載出來就對其進行操作。
這可能是由于在屏幕上還沒有alert時調用Alert()類的操作引起的。


異常19
selenium.common.exceptions.NoSuchAttributeException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.WebDriverException

報錯原因:元素屬性未被找到。
您可能想要檢查您正在測試的特定瀏覽器中的屬性是否存在。一些瀏覽器可能對同一屬性有不同的屬性名。(比如同一屬性在IE8中叫做innertext而在Firefox中叫做textcontent)


異常20
selenium.common.exceptions.NoSuchCookieException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.WebDriverException

報錯原因:在當前瀏覽上下文的活動文檔的相關cookie中,沒有找到匹配給定路徑名的cookie。


異常21
selenium.common.exceptions.NoSuchElementException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.WebDriverException

報錯原因:元素未被定位到。
如果遇到此異常,您可能需要檢查以下內容:

  • 檢查您的find_by中使用的選擇器…
  • 在查找操作的時候,元素可能還沒有出現在屏幕上(網頁仍在加載),請參見selenium.webdriver.support.wait.WebDriverWait(),以了解如何編寫等待的包裝器等待元素出現。

異常22
selenium.common.exceptions.NoSuchFrameException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.InvalidSwitchToTargetException

報錯原因:需要切換的目標frame不存在。


異常23
selenium.common.exceptions.NoSuchWindowException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.InvalidSwitchToTargetException

報錯原因:需要切換的目標窗口不存在。
要找到當前活動window handles的集合,可以通過以下方式獲取活動window handles的列表:

print driver.window_handles

異常24
selenium.common.exceptions.RemoteDriverServerException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.WebDriverExceptio

報錯原因:


異常25
selenium.common.exceptions.ScreenshotException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.WebDriverException

報錯原因:無法截屏。


異常26
selenium.common.exceptions.SessionNotCreatedException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.WebDriverException

報錯原因:未能創建一個新的會話(session)。


異常27
selenium.common.exceptions.StaleElementReferenceException(msg=None, screen=None, stacktrace=None)
??Bases: **
selenium.common.exceptions.WebDriverException**

報錯原因:對元素的引用現在“過期”。
“過期”意味著元素不再出現在頁面的DOM中。
StaleElementReferenceException的可能原因包括但不限于:

  • 您不再在同一頁面上,或者頁面可能在元素所在位置之后刷新。
  • 該元素可能已經被刪除并重新添加到屏幕上,因為它位于屏幕上。例如被重新安置的元素。當更新值并重建節點時,通常會使用javascript框架。
  • 元素可能已經在一個iframe中或另一個刷新的上下文中。

異常28
selenium.common.exceptions.TimeoutException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.WebDriverException

報錯原因:請求超時。


異常29
selenium.common.exceptions.UnableToSetCookieException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.WebDriverException

報錯原因:設置 cookie 失敗。


異常30
selenium.common.exceptions.UnexpectedAlertPresentException(msg=None, screen=None, stacktrace=None, alert_text=None)
??Bases: selenium.common.exceptions.WebDriverException

報錯原因:出現意外 alert。
通常是在預期的模式阻塞webdriver表單執行更多命令時引發的。

__init__(msg=None, screen=None, stacktrace=None, alert_text=None)


異常31
selenium.common.exceptions.UnexpectedTagNameException(msg=None, screen=None, stacktrace=None)
??Bases: selenium.common.exceptions.WebDriverException

報錯原因:未獲取到期望元素。


異常32
selenium.common.exceptions.UnknownMethodException(msg=None, screen=None, stacktrace=None)
??Bases: **
selenium.common.exceptions.WebDriverException**

報錯原因:所請求的命令與已知的URL匹配,但不匹配該URL的方法。


異常33
exception selenium.common.exceptions.WebDriverException(msg=None, screen=None, stacktrace=None)
??Bases: exceptions.Exception

Base webdriver 除外。

_init_(msg=None, screen=None, stacktrace=None)

7.2.Action Chains

ActionChains實現,

class
selenium.webdriver.common.action_chains.ActionChains(driver)
??Bases:object

??actionchain是一種自動化低級交互的方法,比如鼠 標移動、鼠標按鍵動作、按鍵和上下文菜單交互。這對于執行更復雜 的操作(如鼠標懸停、拖放)非常有用。

生成用戶操作。

當您調用actionchain對象上的動作的方法時,動作被存儲(stored)在actionchain對象中的一個隊列中。當您調用perform() 這個方法時,事件會按照它們排隊的順序被觸發。

動作鏈(ActionChains)可用于連鎖(chain)模式:

menu = driver.find_element_by_css_selector(".nav")
hidden_submenu = driver.find_element_by_css_selector(".nav #submenu1")

ActionChains(driver).move_to_element(menu).click(hidden_submenu).perform()

或者可以一個一個地排隊,然后執行:

menu = driver.find_element_by_css_selector(".nav")
hidden_submenu = driver.find_element_by_css_selector(".nav #submenu1")

actions = ActionChains(driver)
actions.move_to_element(menu)
actions.click(hidden_submenu)
actions.perform()

無論哪種方式,這些操作都是以它們被調用的順序執行的,一個接一個。

__init__(driver)
??初始化一個新的ActionChains。
??參數-driver:執行用戶操作的WebDriver實例。

click(on_element=None)
??點擊某一個元素
??參數-on_element:單擊的元素。如果沒有,點擊光標當前位置。

click_and_hold(on_element=None)
??在一個元素上點擊鼠標左鍵。
??參數-on_element:鼠標按下的元素。如果沒有,點擊光標當前位置。

context_click(on_element=None)
??在元素上執行上下文單擊(右擊)。
??參數-on_element:需要右擊的元素。如果沒有,點擊鼠標當前位置。

舉例說明,下面這個示例不具備可執行性:

# -*- coding: utf-8 -*-
#!/usr/bin/env python3


import sys
#print(sys.path)
sys.path.append('/usr/local/lib/python3.6/site-packages/')

from selenium import webdriver  
#引入 ActionChains 類
from selenium.webdriver.common.action_chains import ActionChains 

driver = webdriver.Firefox()
driver.get("http://xxx.com")

#定位到要右擊的元素
right_click = driver.find_element_by_id("xx")

#對定位到的元素執行(perform())鼠標右鍵單擊操作
ActionChains(driver).contest_click(right_click).perform()
#....

double_click(on_element=None)
??雙擊某一個元素。
??參數-on_element:雙擊的元素。如果沒有,點擊鼠標當前位置。

# -*- coding: utf-8 -*-
#!/usr/bin/env python3


import sys
#print(sys.path)
sys.path.append('/usr/local/lib/python3.6/site-packages/')

from selenium import webdriver  
#引入 ActionChains 類
from selenium.webdriver.common.action_chains import ActionChains 

driver = webdriver.Firefox()
driver.get("http://xxx.com")

#定位到要雙擊的元素
double_click = driver.find_element_by_id("xx")

#對定位到的元素執行(perform())鼠標雙擊操作
ActionChains(driver).double_click(double_click).perform()
#....

drag_and_drop(source, target)
??按住源元素上的鼠標左鍵,然后移動到目標元素并釋放鼠標按鈕。

??參數-: source:鼠標開始按住的元素位置;
???? :tatget:鼠標松開的元素位置;

# -*- coding: utf-8 -*-
#!/usr/bin/env python3


import sys
#print(sys.path)
sys.path.append('/usr/local/lib/python3.6/site-packages/')

from selenium import webdriver  
#引入 ActionChains 類
from selenium.webdriver.common.action_chains import ActionChains 

driver = webdriver.Firefox()
driver.get("http://xxx.com")

#定位到元素開始的位置
 source = driver.find_element_by_id("xx")
#定位到元素需要釋放的位置
 target = driver.find_element_by_id("yy")

#執行元素的拖放操作
ActionChains(driver). drag_and_drop( source,target).perform()
#....

drag_and_drop_by_offset(source, xoffset, yoffset)
??按住源元素上的鼠標左鍵,然后移動到目標偏移量并釋放鼠標按鈕。
??參數-:source: 鼠標開始按住的元素位置;
???? : xoffset:X 軸的偏移量;
???? : yoffset:Y 軸的偏移量。

key_down(value, element=None)
??按住一個按鍵,而不釋放它。
????常用于( Control、Alt和Shift等修飾鍵(modifier keys ))。
??參數-:value:The modifier key to send. Values are defined in Keys class.;
????: element:The element to send keys. If None, sends a key to current focused element。

不知道該如何翻譯這個參數的含義,直接貼上原文,下面舉例說明。

比如,Control+C 鍵:

ActionChains(driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()

作者注:modifier keys:是指鍵盤中ctrl、shift、alt等需要跟其他鍵一起使用才有作用的鍵。

key_up(value, element=None)
??松開修飾(modifier)鍵。

參數含義和 key_down()方法的參數意思相同。舉例說明:
比如 ctrl+C:

ActionChains(driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()

move_by_offset(xoffset, yoffset)
??將鼠標從當前位置移動到其他位置。
??參數-: xoffset:X偏移,作為正整數或負整數。
???? :yoffset:Y偏移,作為正整數或負整數。

move_to_element(to_element)
??將鼠標移動到元素的中間懸停(多用于鼠標懸停彈出下拉菜單功能)。
??參數-: to_element:鼠標將要移動到的那個元素中間。

#coding= utf-8

import sys
sys.path.append("/usr/local/lib/python3.6/site-packages/")
from selenium import webdriver
from  selenium.webdriver.common.action_chains import ActionChains
import time

driver = webdriver.Firefox()
#定位到要懸停的元素
above = driver.find_element_by_id("id")
#對定位到的元素進行懸停操作
ActionChains(driver).move_to_element(above).perform()#調用 ActionChains 類,把瀏覽器驅動 driver作為參數傳入

move_to_element_with_offset(to_element, xoffset, yoffset)
??將鼠標移動到指定元素的偏移位置。
????偏移量相對于元素的左上角。
??參數-: to_element:移動的位置相對于哪一個元素;
???? :xoffset:X 軸的偏移量(相對于上面元素的左上角)
????:yoffset:Y 軸的偏移量(相對于上面元素的左上角)

pause(seconds)
??暫停所有輸入(時間單位為秒)

perform()
??執行所有ActionChains中存儲(stored)的動作。

release(on_element=None)
??在一個元素上釋放一個被控制的鼠標按鈕。
??參數-: on_element:鼠標上的元素。如果沒有,則釋放當前鼠標位置。

reset_actions()
??清除已存儲在遠程末端的操作。

send_keys(*keys_to_send)
??在當前定位到的元素內輸入內容。
??參數-: keys_to_send:具體輸入的文本內容。對于modifier keys(control、alt 和 shift 鍵)可以在“Keys”類中輸入。

send_keys_to_element(element, *keys_to_send)
??通過鍵盤向某個元素內輸入內容。
??參數-:element:需要通過鍵盤輸入內容的元素;
????:keys_to_send:輸入的具體內容。

7.3. Alerts

Alert 的實現。

class?selenium.webdriver.common.alert.Alert(driver)
??Bases:object

使用這個類與警報(alert)提示交互。它包含了撤銷(dismissing)、接受(accepting)、輸入(inputting)和從警告提示中獲取文本(getting text)的方法。

接收(Accepting)/解除(Dismissing)警報提示:

Alert(driver).accept()
Alert(driver).dismiss()

在 alert 提示中輸入值:

name_prompt = Alert(driver) 
name_prompt.send_keys(“Willian Shakesphere”) 
name_prompt.accept()

獲取提示框上的信息:

alert_text = Alert(driver).text 
self.assertEqual(“Do you wish to quit?”, alert_text)

_init_(driver)
??初始化一個 Alert。

authenticate(username, password)
??將用戶名/密碼發送到一個經過驗證的對話框(類似于基本的(Basic)HTTP Auth)。隱式地“單擊ok”
用法舉例:
driver.switch_to.alert.authenticate(‘cheese’, ‘secretGouda’)

參數: username:在對話框的用戶名部分中設置字符串。
???:password:在對話框的密碼部分中設置字符串。

#coding=utf-8
import sys

sys.path.append("/usr/local/lib/python3.6/site-packages/")
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains 
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By 
from selenium.webdriver.common.alert import Alert
import time

#driver = webdriver.Firefox()
driver = webdriver.Chrome('./chromedriver')
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()

#保存設置
#driver.find_element_by_class_name("prefpanelgo").click()
#driver.find_element_by_xpath("http://a[@class='prefpanelgo']").click()
ActionChains(driver).click(driver.find_element(By.CLASS_NAME,'prefpanelgo')).perform()

#texts = driver.switch_to_alert().text
#print("警告的內容是%s"%texts)

#time.sleep(10)
driver.implicitly_wait(10)

#接受警告框
#driver.switch_to_alert().accept()
#使用這個方法需要導入 Alert 模塊
Alert(driver).accept()
time.sleep(5)

driver.quit()

7.4. Special Keys

Keys 的實現。

7.5. Locate elements By

這些屬性可以用來定位元素。參見“定位元素”章節(第4章)。

7.6. Desired Capabilities

參考2.5節學習desired capabilities的用法。

7.7. Touch Actions

7.8. Proxy(代理)

7.9. Utilities

7.10. Service

7.11. Application Cache

7.12. Firefox WebDriver

class
selenium.webdriver.firefox.webdriver . WebDriver(firefox_profile=None, firefox_binary=None, timeout=30, capabilities=None, proxy=None, executable_path='geckodriver', options=None, log_path='geckodriver.log', firefox_options=None, service_args=None)
??Bases:selenium.webdriver.remote.webdriver.WebDriver

_init_ (firefox_profile=None, firefox_binary=None, timeout=30, capabilities=None, proxy=None, executable_path='geckodriver', options=None, log_path='geckodriver.log', firefox_options=None, service_args=None)
開啟一個新的 Firefox 會話(也就是打開一個新的瀏覽器窗口)。

基于各種關鍵字參數的組合和特性,將構建一個功能字典,它將被傳遞到遠程終端。

給這個構造函數的關鍵字參數是幫助更容易地讓Firefox WebDriver會話定制不同的選項。它們被映射到一個功能字典,它被傳遞到遠程終端。

7.13. Firefox WebDriver Options

7.14. Firefox WebDriver Profile

7.15. Firefox WebDriver Binary

7.16. Firefox WebDriver Extension Connection

7.17. Chrome WebDriver

7.18. Chrome WebDriver Options

7.19. Chrome WebDriver Service

7.20. Remote WebDriver

7.21. Remote WebDriver WebElement

7.22. Remote WebDriver Command

7.23. Remote WebDriver Error Handler

7.24. Remote WebDriver Mobile

7.25. Remote WebDriver Remote Connection

7.26. Remote WebDriver Utils

7.27. Internet Explorer WebDriver

7.28. Android WebDriver

7.29. Opera WebDriver

7.30. PhantomJS WebDriver

7.31. PhantomJS WebDriver Service

7.32. Safari WebDriver

7.33. Safari WebDriver Service

7.34. Select Support

7.35. Wait Support

7.36. Color Support

7.37. Event Firing WebDriver Support

7.38. Abstract Event Listener Support

7.39. Expected conditions Support


??
2018/5/24
內容太多,有時間更新。。。。。。

??

??

8.附錄:FAQ

更多常見問題: https://github.com/SeleniumHQ/selenium/wiki/Frequently-Asked-Questions

8.1.How to use ChromeDriver ?

下載最新的 chromedriver。然后解壓:

unzip chromedriver_linux32_x.x.x.x.zip

您應該看到chromedriver可執行文件。現在,您可以創建這樣的一個Chrome WebDriver實例:

driver = webdriver.Chrome(executable_path="/path/to/chromedriver")

8.2.Does Selenium 2 support XPath 2.0 ?

參考:http://seleniumhq.org/docs/03_webdriver.html#how-xpath-works-in-webdriver

Selenium將XPath查詢委托給瀏覽器自己的XPath引擎,所以Selenium支持XPath支持任何瀏覽器支持的內容。在沒有原生XPath引擎的瀏覽器中(IE 6,7,8),Selenium只支持XPath 1.0。

8.3.How to scroll down to the bottom of a page ?

參考: http://blog.varunin.com/2011/08/scrolling-on-pages-using-selenium.html

可以使用execute_script方法在加載的頁面上執行javascript。因此,您可以調用JavaScript API來滾動到頁面的底部或任何其他位置。

示例:

driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")

DOM中的window對象有一個scrollTo方法,可以滾動到打開的窗口的任何位置。scrollHeight是所有元素的公共屬性。document.body。滾動高度將使頁面的整個主體高度。

8.4. How to auto save files using custom Firefox profile ?

參考: http://stackoverflow.com/questions/1176348/access-to-file-download-dialog-in-firefox

參考: http://blog.codecentric.de/en/2010/07/file-downloads-with-selenium-mission-impossible/

第一步是確定要自動保存的文件類型。

要確定您想要自動下載的內容類型,可以使用curl:
curl -I URL | grep "Content-Type"
另一種查找內容類型的方法是使用request模塊,您可以這樣使用它:

import requests
content_type = requests.head('http://www.python.org').headers['content-type']
print(content_type)

一旦確定了內容類型,您就可以使用它來設置firefox配置文件首選項:browser.helperApps.neverAsk.saveToDisk

示例:

import os

from selenium import webdriver

fp = webdriver.FirefoxProfile()

fp.set_preference("browser.download.folderList",2)
fp.set_preference("browser.download.manager.showWhenStarting",False)
fp.set_preference("browser.download.dir", os.getcwd())
fp.set_preference("browser.helperApps.neverAsk.saveToDisk", "application/octet-stream")

browser = webdriver.Firefox(firefox_profile=fp)
browser.get("http://pypi.python.org/pypi/selenium")
browser.find_element_by_partial_link_text("selenium-2").click()

在上面的示例中,application/octet-stream 被用作內容類型。

browser.download.dir選項指定要下載文件的目錄。

8.5. How to upload files into file inputs ?

選擇<input type="file">元素,并調用send_keys()方法傳遞文件路徑,路徑相對于測試腳本,或絕對路徑。請記住Windows和Unix系統之間路徑名稱的區別。

8.6. How to use firebug with Firefox ?

首先下載Firebug XPI文件,然后調用firefox配置文件的add_extension方法:

from selenium import webdriver

fp = webdriver.FirefoxProfile()

fp.add_extension(extension='firebug-1.8.4.xpi')
fp.set_preference("extensions.firebug.currentVersion", "1.8.4") #Avoid startup screen
browser = webdriver.Firefox(firefox_profile=fp)

8.7. How to take screenshot of the current window ?

使用 webdriver 提供的save_screenshot方法進行截屏操作:

from selenium import webdriver

driver = webdriver.Firefox()
driver.get('http://www.python.org/')
driver.save_screenshot('screenshot.png')
driver.quit()

官網

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容