随心玩玩(十一)Selenium从入门到入土

写在前面:脚本是最简单的开发


文章目录

  • 介绍
  • 安装
  • 让我们开始吧
    • 简单例子
    • 编写测试
  • 跳转链接
    • 与页面交互
    • 填充表格
    • 拖放
    • 窗口和框架之间移动
    • 弹出对话框
    • 跳转:历史
    • Cookies
  • 定位元素
    • by Id
    • by name
    • by XPath
    • by Link Text
    • by Tag Name
    • by Class Name
    • by CSS Selectors
  • 等待
    • 显式等待
    • 隐式等待
  • 页面对象-设计模式
    • 测试样例
    • 页面对象类
    • 页面元素
    • 定位器
  • API

参考资料:
https://www.geeksforgeeks.org/selenium-python-tutorial/
https://selenium-python.readthedocs.io/

介绍

Selenium是一个用于自动化浏览器操作的开源框架,而Selenium WebDriver则是Selenium的一个组件,它提供了一种编程接口,用于控制浏览器的行为。Selenium WebDriver支持多种浏览器,包括但不限于Chrome、Firefox、Safari和Edge等。

Selenium Python绑定提供了一个方便的API来访问Selenium WebDrivers,如Firefox、Ie、Chrome、Remote等。

安装

pip install selenium
pip install webdriver_manager

Selenium需要一个驱动程序来驱动所选浏览器。例如,Firefox需要geckodriver,谷歌需要chrome driver。

如果自己已经装了chrome的话,查看自己的chrome版本,在https://chromedriver.storage.googleapis.com/index.html安装相应的chrome driver下载后解压随便放一个目录里即可。

或者也可以在https://chromedriver.chromium.org/downloads查看各chrome driver版本的下载

浏览器方面,我就直接网盘提供绿色安装的压缩包吧,不忘初心方便大家,版本是108.0.5359.125:
链接:https://pan.baidu.com/s/1DN0JhGyCIudTLDBXUl0c4w?pwd=fjfc
提取码:fjfc (永久有效,网盘挂了记得踹我一脚)

108版本chrome driver 下载地址:
https://chromedriver.storage.googleapis.com/index.html?path=108.0.5359.71/

下载好后,随便解压到一个目录即可。

让我们开始吧

简单例子

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys

# 启动driver
options = Options()
# 替换为你本地的Chromed路径
options.binary_location = r"D:\software\Chrome-bin\chrome.exe"
# 替换为你本地的Chromedriver路径
chromedriver_path = r'D:\software\Chrome-bin\chromedriver.exe'
# 使用Service对象指定Chromedriver路径
service = Service(chromedriver_path)
driver = webdriver.Chrome(service=service, options=options)
# ==============
driver.get("https://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()

这是一个使用Selenium库进行自动化测试的get start脚本,下面是对代码的解释:

  1. 导入必要的模块和类:

    from selenium import webdriver
    from selenium.webdriver.chrome.service import Service
    from selenium.webdriver.chrome.options import Options
    from selenium.webdriver.common.by import By
    from selenium.webdriver.common.keys import Keys
    

    这里导入了Selenium的webdriver模块,以及一些其他的类和模块,包括ServiceOptionsByKeys

  2. 配置Chrome选项和路径:

    options = Options()
    options.binary_location = r"D:\software\Chrome-bin\chrome.exe"
    chromedriver_path = r'D:\software\Chrome-bin\chromedriver.exe'
    

    在这里,创建了一个Options对象来配置Chrome浏览器的选项,包括指定Chrome二进制文件的路径。chromedriver_path是Chromedriver的可执行文件路径。

  3. 启动Chromedriver服务和浏览器:

    service = Service(chromedriver_path)
    driver = webdriver.Chrome(service=service, options=options)
    

    创建了一个Service对象,用于指定Chromedriver的路径,然后通过webdriver.Chrome启动了Chrome浏览器,并传递了Chromedriver服务和选项。

  4. 访问网页:

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

    打开Chrome浏览器后,通过get方法访问了Python官方网站。

  5. 验证页面标题:

    assert "Python" in driver.title
    

    使用断言来确认打开的页面标题中包含"Python"。

  6. 查找页面元素并输入搜索内容:

    elem = driver.find_element(By.NAME, "q")
    elem.clear()
    elem.send_keys("pycon")
    

    使用find_element方法通过By.NAME查找页面上的元素,然后清空该元素的内容并输入"pycon"。

  7. 模拟键盘按键并提交表单:

    elem.send_keys(Keys.RETURN)
    

    使用send_keys方法模拟按下回车键,以提交搜索表单。

  8. 验证页面中是否包含指定文本:

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

    使用断言确认在页面源代码中不存在"No results found."这个文本,以确保搜索结果不为空。

  9. 关闭浏览器:

    driver.close()
    

    使用close方法关闭浏览器窗口。

这个脚本的主要目的是通过自动化测试的方式打开Python官方网站,执行搜索操作,并验证搜索结果。

编写测试

selenium包本身不提供测试框架。但可以使用Python的unittest模块编写测试用例。工具/框架的其他选项是pytest和nose。
在例子中,我们使用单元测试作为选择的框架。以下是使用单元测试模块的修改示例。这是对python.org搜索功能的测试:

import unittest
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys


class PythonOrgSearch(unittest.TestCase):

    def setUp(self):
        # 启动driver
        options = Options()
        # 替换为你本地的Chromed路径
        options.binary_location = r"D:\software\Chrome-bin\chrome.exe"
        # 替换为你本地的Chromedriver路径
        chromedriver_path = r'D:\software\Chrome-bin\chromedriver.exe'
        # 使用Service对象指定Chromedriver路径
        service = Service(chromedriver_path)
        driver = webdriver.Chrome(service=service, options=options)
        # ==============

        self.driver = driver

    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)
        self.assertNotIn("No results found.", driver.page_source)

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


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

这段代码是一个使用Selenium和unittest模块的测试脚本,它执行以下操作:

  1. 导入必要的模块和类:

    import unittest
    from selenium import webdriver
    from selenium.webdriver.chrome.service import Service
    from selenium.webdriver.chrome.options import Options
    from selenium.webdriver.common.by import By
    from selenium.webdriver.common.keys import Keys
    

    这里导入了unittest模块和Selenium的webdriver模块以及其他相关类。

  2. 定义测试类:

    class PythonOrgSearch(unittest.TestCase):
    

    定义了一个继承自unittest.TestCase的测试类。

  3. 设置测试环境(setUp方法):

    def setUp(self):
        # 启动driver
        options = Options()
        options.binary_location = r"D:\software\Chrome-bin\chrome.exe"
        chromedriver_path = r'D:\software\Chrome-bin\chromedriver.exe'
        service = Service(chromedriver_path)
        driver = webdriver.Chrome(service=service, options=options)
        self.driver = driver
    

    在每个测试方法执行之前,setUp方法被调用,其中初始化了WebDriver,即启动了Chrome浏览器,并将WebDriver对象存储在self.driver中供其他测试方法使用。

  4. 定义测试方法:

    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)
        self.assertNotIn("No results found.", driver.page_source)
    

    这是一个测试方法,以test_开头的方法会被unittest识别为测试用例。该测试方法访问Python官方网站,搜索关键字"pycon",然后断言页面标题中包含"Python",并且页面源代码中不包含"No results found."文本。

  5. 清理测试环境(tearDown方法):

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

    在每个测试方法执行之后,tearDown方法被调用,用于清理测试环境。在这里,它关闭了WebDriver,即关闭了Chrome浏览器。

  6. 运行测试用例:

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

    这段代码确保当脚本被直接运行时才执行测试,而不是在其他模块中被导入时执行测试。执行unittest.main()会运行所有以test_开头的测试方法。

您可以从pycharm上运行上面的测试用例,将会显示类似结果表明测试成功:

Ran 1 test in 8.607s

OK

Process finished with exit code 0

跳转链接

使用WebDriver要做的第一件事就是导航到一个链接。执行此操作的正常方法是调用get方法:
driver.get("http://www.google.com")

WebDriver将等待页面完全加载(即启动onload事件),然后将控制权返回到测试或脚本。请注意,如果您的页面在加载时使用了大量AJAX,那么WebDriver可能不知道它何时已经完全加载。如果需要确保这些页面已完全加载,则可以使用等待。

与页面交互

仅仅能够去一些地方并不是很有用。我们真正想做的是与页面交互,或者更具体地说,是与页面中的HTML元素交互。首先,我们需要找到一个想要交互的HTML元素。WebDriver提供了多种查找元素的方法。例如,给定一个定义为的元素:

您可以使用以下任意一种方法找到它:

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']")
element = driver.find_element(By.CSS_SELECTOR, "input#passwd-id")

你也可以通过文本来寻找链接,但要小心文本必须完全匹配。在WebDriver中使用XPATH时也应该小心。如果有多个元素与查询匹配,则只返回第一个元素。如果找不到任何内容,将引发NoSuchElementException。

WebDriver有一个“基于对象的”API;我们使用相同的接口来表示所有类型的元素。这意味着,您可能会看到许多可能调用的方法来完成组合操作,但并非所有方法都有意义或有效,但别担心WebDriver将尝试做正确的事情。

但如果您调用一个没有意义的方法,例如,“meta”上调用setSelected(),则会引发异常。

所以,你有一个元素。你能用它做什么?首先,您可能需要在文本字段中输入一些文本:
element.send_keys("some text")

您可以使用“keys”类模拟按下箭头键:
element.send_keys(" and some", Keys.ARROW_DOWN)

你可以在任何元素上调用send_keys,这使得测试键盘快捷键(如GMail上使用的快捷键)成为可能。这样做的一个副作用是,您键入的内容会附加到已经存在的内容上。您可以使用clear方法轻松清除文本字段或文本区域的内容:
element.clear()

填充表格

我们已经了解了如何在文本区域或文本字段中输入文本,但其他元素呢?您可以“切换”下拉菜单的状态,也可以使用“setSelected”设置类似于OPTION标记的选定内容。处理SELECT标记:

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

这将取消选择页面上特定SELECT中的所有选项。
假设在一个测试中,我们需要所有默认选择选项的列表,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在每个元素上都有方便的方法“提交”。如果在表单中的元素上调用this,WebDriver将遍历DOM,直到找到封闭的表单,然后对其调用submit。如果元素不在表单中,则将引发NoSuchElementException:

element.submit()

拖放

可以使用拖放,将一个图元移动一定量,也可以移动到另一个图元:

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

窗口和框架之间移动

现代web应用程序很少不具有任何框架或仅限于一个窗口。WebDriver支持使用“switch_to.window”方法在命名窗口之间移动:

driver.switch_to.window("windowName")

所有对驱动程序的调用现在都将被解释为指向特定窗口。但是你怎么知道窗户的名字?看看打开它的javascript或链接:

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

或者,您可以将“窗口句柄”传递给“switch_to.window()”方法。知道了这一点,就可以对每个打开的窗口进行迭代,如下所示:

for handle in driver.window_handles:
    driver.switch_to.window(handle)

在一个网页中,有时会有嵌套的框架,例如HTML页面内包含一个或多个