Selenium Python

作者: 顾顾314 | 来源:发表于2018-05-22 21:01 被阅读705次

    这篇文章在介绍官网的同时使用了比较多的脚本示例,示例里遇到的问题有部分在本篇文章进行了解释,还有一篇文章专门记录了问题。希望可以给予初学者更多的帮助。

    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("//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("//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("//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("//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("//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("//form[1]")
    login_form = driver.find_element_by_xpath("//form[@id='loginForm']")
    

    1、绝对路径(如果HTML稍有更改,则会中断)
    2、HTML中的第一个表单元素。
    3、带有属性id和值loginForm的表单元素。

    username元素可以这样定位:

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

    1、第一个表单元素,带有一个名为name和值username的输入子元素。
    2、表单元素的第一个输入子元素,带有名为id的属性和值loginForm。
    3、第一个输入元素的属性命名为' name '和值username。

    “Clear”按钮元素可以这样定位:

    clear_button = driver.find_element_by_xpath("//input[@name='continue'][@type='button']")
    clear_button = driver.find_element_by_xpath("//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("//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()
    

    官网

    相关文章

      网友评论

        本文标题:Selenium Python

        本文链接:https://www.haomeiwen.com/subject/cwzhjftx.html