Python Web UI自动化测试之Selenium基础篇


背景历史

2004年Jason Huggins身在ThoughtWorks发起了Selenium项目。

Selenium1.0包括三部分:

  • Selenium IDE:嵌入到Firefox浏览器中的一个插件,实现对浏览器操作的录制和回放
  • Selenium Grid:实现大量用例的分布式测试
  • Selenium RC:Selenium Remote Control,支持多种不同语言编写的测试脚本,去控制访问浏览器,从而达到测试的目的。Selenium RC分为Client Libraries和Selenium Server。Client Libraries库主要用于编写测试脚本,用来控制Selenium Server的库。Selenium Server负责控制浏览器行为。

2006年Google的Simon Stewart发起了WebDriver项目。

WebDriver是通过原生浏览器支持或者浏览器扩展来直接控制浏览器。WebDriver针对各个浏览器而开发,取代了嵌入到被测Web应用中的JavaScript,与浏览器紧密集成,因此支持创建更高级的测试,避免了JavaScript安全模型导致的限制。除了来自浏览器厂商的支持之外,WebDriver还利用操作系统级的调用,模拟用户输入。

2009年,Selenium和Webdriver合并,2011年Selenium2.0诞生。

Selenium2.0包含四个部分:

  • Selenium IDE
  • Selenium Grid
  • Selenium RC
  • WebDriver:利用浏览器原生的API,封装成一套更加面向对象的API,直接操作浏览器页面里的元素,甚至操作浏览器本身(截屏,窗口大小,启动,关闭,安装插件,配置证书之类的)。由于使用的是浏览器原生的API,速度得到了大大提高。

WebDriver作为Selenium RC的替代方案,成为Selenium2.0的核心功能。

2016年7月,Selenium3.0版本发布

  • 去掉了Selenium RC
  • 需要java8以上版本
  • Selenium的Firefox浏览器需要单独的驱动
  • Mac OS集成Safari浏览器驱动,默认路径:/usr/bin/safaridriver

2021年10月13日,Selenium4.0发布

  • 需要Python3.7以上版本
  • capabilities书写格式变更
  • 定位元素方法格式变更find_element_by_xxx(‘yyy’)改为find_element(By.xxx,’yyy’)
  • executable_path语法废弃,使用Serviece方式替代
  • 新增相对定位方法

Selenium特点

  • 免费开源
  • 支持多平台:Windows、Mac、Linux
  • 支持多种编程语言:Java、C、C++、Python、Ruby、PHP、Perl、JavaScript等
  • 支持多种浏览器:Firefox、Chome、Safari、Edge、Opera、IE等
  • 支持分布式运行测试

(本系列文章中,不会介绍关于IE浏览器的测试,IE浏览器已于2022年6月15日停止维护!)

环境搭建

安装Selenium (这里使用最新版本4)

$ pip install selenium

本机的python高于3.7,默认会安装最新版本,当前是4.8.0

下载浏览器

Firefox:https://www.mozilla.org/zh-CN/firefox/enterprise/ (这里使用延长支持版)

Chrome:https://www.chromedownloads.net/chrome64osx/ (这里是历史版本的下载)

Edge:https://www.microsoft.com/zh-cn/edge/download?form=MA13FJ

Safari:Mac系统自带浏览器。(Windows版本已于2012年停止更新)

(浏览器版本不建议安装最新版,而是安装稳定版)

下载浏览器驱动

(下载的浏览器驱动,需要与浏览器的版本对应上,否则无法调起浏览器报错)

Firefox Driver:https://github.com/mozilla/geckodriver/releases/

Chrome Driver:http://chromedriver.storage.googleapis.com/index.html

https://registry.npmmirror.com/binary.html?path=chromedriver/

Edge Driver:https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/

Safari Driver:随系统安装会自动安装驱动。

  • Safari11.1及之前的版本:
    • 浏览器偏好设置 - 高级 - 在菜单栏中显示“开发”菜单
    • 浏览器开发菜单 - 允许远程自动化
    • 授权safaridriver启动托管本地web服务器的XPC服务。要允许此操作,请手动运行/usr/bin/safridriver一次,然后按照身份验证提示进行操作。
  • Safari12及之后的版本,需执行safaridriver --enable

下载下来的驱动程序,需要存放到一个在系统环境变量中定义了的位置:

  • Mac下,可以将浏览器的驱动文件存放在/usr/local/bin,也可以是Python的bin目录
  • Windows下,可以将浏览器的驱动文件存放在Windows/system32,也可以是Python的bin目录

上面只是给了个例子,也可以存放在其他目录中,但必须是在系统环境变量中进行了定义的。后续的项目实战中,会将驱动文件存放在项目的lib目录中,使用代码指定驱动程序的路径,而不是在环境变量中定义,以方便管理。

Selenium原理

上面介绍selenium历史的时候,已经得知从3.0版本已经去掉了RC模块,由WebDriver来取代RC模块,即使用封装好的WebDriver API来操作浏览器及网页元素。(下面介绍的所有的Webdriver的语法、操作等都是在介绍API的使用。)

但,目前很多网上的最新教程虽然介绍的是selenium3或4,但原理还是讲“Remote Control、Client Libraries和Selenium Server”,这是错误的。selenium3或4,已经没有RC(Remote Control)模块了。

WebDriver是W3C的一个标准( https://w3c.github.io/webdriver/ ),WebDriver与浏览器之间的交互是标准的Server-Client的模式,Server是Remote Server端,是任意的浏览器;Client是测试脚本(testcase)。(客户端Client发送一个request请求,服务器端返回一个response响应数据)

WeiDriver的工作流程可以简单的描述为:

  1. 创建一个WebDriver实例,启动目标浏览器,并绑定到指定端口。启动的浏览器将作为WebDriver的Remote Server。
  2. Client端通过CommandExcuter发送HTTP Request给Remote Server的侦听端口。
  3. Remote Server需要依赖浏览器驱动,例如Firefox Driver等,来转化成浏览器的原生操作。

其中第2、3步骤不需要我们干预,我们只需要掌握第1步骤中测试脚本的设计,即如何使用WebDriver APIs对driver传送指令、传送什么指令。

测试代码发送指令给浏览器驱动,浏览器驱动通过“JsonWireProtocol”(基于HTTP通信)将指令转成浏览器的原生操作。

比如:site-packages/selenium/webdriver/remote/webdriver.pydriver.get()get方法的源码:

......

    def execute(self, driver_command: str, params: dict = None) -> dict:
        """Sends a command to be executed by a command.CommandExecutor.

        :Args:
         - driver_command: The name of the command to execute as a string.
         - params: A dictionary of named parameters to send with the command.

        :Returns:
          The command's JSON response loaded into a dictionary object.
        """
        params = self._wrap_value(params)

        if self.session_id:
            if not params:
                params = {"sessionId": self.session_id}
            elif "sessionId" not in params:
                params["sessionId"] = self.session_id

        response = self.command_executor.execute(driver_command, params)
        if response:
            self.error_handler.check_response(response)
            response["value"] = self._unwrap_value(response.get("value", None))
            return response
        # If the server doesn't send a response, assume the command was
        # a success
        return {"success": 0, "value": None, "sessionId": self.session_id}

    def get(self, url: str) -> None:
        """Loads a web page in the current browser session."""
        self.execute(Command.GET, {"url": url})
......

调用get()方法,会执行execute(Command.GET, url)方法,通过上面的注释信息可以得到“Sends a command to be executed by a command.CommandExecutor”调用execute()方法就会通过command.CommandExecutor去执行一个命令,这里的命令是Command.GET,对应到remote_connection.py源代码中的

......
        self._commands = {
            Command.NEW_SESSION: ("POST", "/session"),
            Command.QUIT: ("DELETE", "/session/$sessionId"),
            Command.W3C_GET_CURRENT_WINDOW_HANDLE: ("GET", "/session/$sessionId/window"),
            Command.W3C_GET_WINDOW_HANDLES: ("GET", "/session/$sessionId/window/handles"),
            Command.GET: ("POST", "/session/$sessionId/url"),
......

这里的session、sessionId指的是remote server和浏览器的会话,通过这个会话变成对浏览器的操作。

基础语法

创建测试

创建一个py文件,导入webdriver

# 导入selenium模块的webdriver函数
from selenium import webdriver

# 通过“webdriver.浏览器”获得一个浏览器对象
# browser是变量名,可以是任意名,但一般情况指定为"driver"或"browser"
browser = webdriver.Chrome()
# browser = webdriver.Safari()
# browser = webdriver.Firefox()
# browser = webdriver.Edge()

运行上面的代码,可以正确调起对应的浏览器,表示环境已经配置正确。

若浏览器的版本号与driver的版本号不匹配,就会出现类似下面的报错:

selenium.common.exceptions.SessionNotCreatedException: Message: session not created: This version of Microsoft Edge WebDriver only supports Microsoft Edge version 110
Current browser version is 108.0.1462.76 with binary path /Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge

通过selenium访问一个网址

# 导入selenium模块的webdriver函数
from selenium import webdriver

# 通过webdriver.浏览器获得一个浏览器对象
# browser是变量名,可以是任意名,但一般情况指定为"driver"或"browser"
browser = webdriver.Firefox()
# 实例网站:SHOPWIND商城
url = "http://101.43.8.10:9696/"
# 访问指定的网址
browser.get(url)

运行上面的代码

操作浏览器

  1. 控制浏览器窗口大小

    通过set_window_size()方法来设置浏览器的窗口大小

    from selenium import webdriver
    
    browser = webdriver.Firefox()
    url = "http://101.43.8.10:9696/"
    # 访问指定的网址
    browser.get(url)
    # 设置浏览器窗口大小为(1280,800)
    browser.set_window_size(1280, 800)
  2. 控制浏览器页面后退、前进

    通过back()方法和forward()方法来实现页面的后退和前进操作

    from selenium import webdriver
    
    browser = webdriver.Firefox()
    # 实例网站:SHOPWIND商城
    url1 = "http://101.43.8.10:9696/"
    # 实例网站:ShopXO商城
    url2 = "http://101.43.8.10:6996/"
    # 访问指定的网址
    browser.get(url1)
    browser.get(url2)
    # 从url2回退到url1
    browser.back()
    # 从url1前进到url2
    browser.forward()
  3. 浏览器页面刷新

    通过refresh()方法实现浏览器的页面刷新

    from selenium import webdriver
    
    browser = webdriver.Firefox()
    # 实例网站:SHOPWIND商城
    url = "http://101.43.8.10:9696/"
    # 访问指定的网址
    browser.get(url)
    # 刷新浏览器页面
    browser.refresh()
  4. 最大化、最小化浏览器窗口

    通过使用maximize_window()方法和minimize_window()方法实现最大化及最小化浏览器窗口

    from selenium import webdriver
    
    browser = webdriver.Firefox()
    # 实例网站:SHOPWIND商城
    url = "http://101.43.8.10:9696/"
    # 访问指定的网址
    browser.get(url)
    
    # 最大化浏览器窗口
    browser.maximize_window()
    # 最小化浏览器窗口
    browser.minimize_window()
  5. 关闭标签页

    通过使用close()方法来关闭浏览器的标签页

    from selenium import webdriver
    
    browser = webdriver.Firefox()
    # 实例网站:SHOPWIND商城
    url = "http://101.43.8.10:9696/"
    # 访问指定的网址
    browser.get(url)
    # 关闭标签页
    browser.close()
  6. 关闭浏览器

    通过使用quit()方法来关闭浏览器

    from selenium import webdriver
    
    browser = webdriver.Firefox()
    # 实例网站:SHOPWIND商城
    url = "http://101.43.8.10:9696/"
    # 访问指定的网址
    browser.get(url)
    # 关闭浏览器
    browser.quit()
  7. 获取浏览器属性

    可以通过浏览器对象,获取对象的相关属性:

    title:页面标题

    current_url:当前页面地址

    page_source:页面的源代码

    (还有句柄相关属性,会在后续内容进行介绍)

    from selenium import webdriver
    
    browser = webdriver.Firefox()
    # 实例网站:SHOPWIND商城
    url = "http://101.43.8.10:9696/"
    # 访问指定的网址
    browser.get(url)
    
    # 打印输出页面标题
    print(browser.title)
    # 打印输出页面url
    print(browser.current_url)
    # 关闭标签页
    browser.close()

    执行代码

    ShopWind开源电商系统 - 专业的B2B2C电商系统解决方案 - Powered by ShopWind
    http://101.43.8.10:9696/

    打印输出源代码内容较多,这里不进行演示。

8大页面元素定位方式

定位页面元素有以下8种:

  • id 通过元素的id值进行定位,一个html中每个id是唯一的。如果有id,id定位是首选!
  • name 通过元素的名称name属性进行定位,name不是唯一的,可以重名
  • class name 通过元素的类名来定位
  • link text 通过link_text定位超链接文本的
  • partial link text 是link_text的模糊搜索模式
  • tag name 通过标签tag进行定位,不建议使用
  • xpath 通过元素路径进行定位,最为推荐使用的定位方式 ( XPath详解
  • css selector 通过css选择器进行定位,是比较推荐的一种定位方式

在实际工作中,一般以xpath或css_selector为主(xpath和css_selector二选一,不建议混合使用),以id、name为辅(在实际工作中,不是所有元素都有id,name存在重复定义现象等)

定位元素使用使用:

find_element("", "")	# 定位一个元素,第一个“”中为上面8种方式之一,第二个“”为对应的值
find_elements("", "")  # 定位一组元素,第一个“”中为上面8种方式之一,第二个“”为对应的值

find_element_by_xx()已经弃用。

通过实例网页来演示8种定位方式的使用

SHOPWIND商城登录页:http://101.43.8.10:9696/login.html

By id

from selenium import webdriver

browser = webdriver.Firefox()
# 实例网站:SHOPWIND商城
url = "http://101.43.8.10:9696/login.html"
# 访问指定的网址
browser.get(url)

browser.find_element("id", "username").send_keys('buyer')
browser.find_element("id", "password").send_keys('123456')

browser.quit()

执行上面代码,可以看到成功定位到了username和password两个元素,并在其中输入了对应的值。

其中,

send_keys(),是向可输入的控件中输入指定内容

另,在执行测试时发现输入框中默认带了指定值,通过send_keys()输入值导致输入框的值变成了“buyerbuyer”,“123456123456”,重复了。这需要在执行输入前,先使用clear()清空输入框的默认值。

from selenium import webdriver

browser = webdriver.Firefox()
# 实例网站:SHOPWIND商城
url = "http://101.43.8.10:9696/login.html"
# 访问指定的网址
browser.get(url)
browser.find_element("id", "username").clear()
browser.find_element("id", "username").send_keys('buyer')
browser.find_element("id", "password").clear()
browser.find_element("id", "password").send_keys('123456')

browser.quit()

By name

from selenium import webdriver

browser = webdriver.Firefox()
# 实例网站:SHOPWIND商城
url = "http://101.43.8.10:9696/login.html"
# 访问指定的网址
browser.get(url)
browser.find_element("name", "username").clear()
browser.find_element("name", "username").send_keys('buyer')
browser.find_element("name", "password").clear()
browser.find_element("name", "password").send_keys('123456')

browser.quit()

By class name

登录页面的“登录”按钮可以通过class name进行定位

from selenium import webdriver

browser = webdriver.Firefox()
# 实例网站:SHOPWIND商城
url = "http://101.43.8.10:9696/login.html"
# 访问指定的网址
browser.get(url)
browser.find_element("name", "username").clear()
browser.find_element("name", "username").send_keys('buyer')
browser.find_element("name", "password").clear()
browser.find_element("name", "password").send_keys('123456')

browser.find_element("class name", 'login-submit').click()

browser.quit()

其中,click()是对定位到的元素执行鼠标点击操作

页面上的“立即注册”按钮可以使用link text方式进行定位

from selenium import webdriver

browser = webdriver.Firefox()
# 实例网站:SHOPWIND商城
url = "http://101.43.8.10:9696/login.html"
# 访问指定的网址
browser.get(url)

browser.find_element("link text", "立即注册").click()

browser.quit()

页面上的“立即注册”按钮可以使用partial link text方式进行定位,这里使用“立即”两个字

from selenium import webdriver

browser = webdriver.Firefox()
# 实例网站:SHOPWIND商城
url = "http://101.43.8.10:9696/login.html"
# 访问指定的网址
browser.get(url)

browser.find_element("partial link text", "立即").click()

browser.quit()

By xpath

from selenium import webdriver

browser = webdriver.Firefox()
# 实例网站:SHOPWIND商城
url = "http://101.43.8.10:9696/login.html"
# 访问指定的网址
browser.get(url)
browser.find_element("xpath", '//input[@placeholder="用户名"]').clear()
browser.find_element("xpath", '//input[@name="username"]').send_keys('buyer')
browser.find_element("xpath", '//input[@type="password"]').clear()
browser.find_element("xpath", '//input[@name="password"]').send_keys('123456')

browser.find_element("xpath", '//input[@title="登录"]').click()

browser.quit()

By css selector

通过CSS选择器进行定位,需要对CSS比较了解,这里不再详细描述CSS相关的知识。

下面是通过id及标签的层级关系进行定位

from selenium import webdriver

browser = webdriver.Firefox()
# 实例网站:SHOPWIND商城
url = "http://101.43.8.10:9696/login.html"
# 访问指定的网址
browser.get(url)
browser.find_element("css selector", '#username').clear()
browser.find_element("css selector", '#username').send_keys('buyer')
browser.find_element("css selector", '#password').clear()
browser.find_element("css selector", '#password').send_keys('123456')

browser.find_element("css selector", '#login_form > dl:nth-child(4) > dd > input.login-submit').click()

browser.quit()

By tag name

找不到合适的例子举例,tag name,是非常不推荐的定位方式。

除了上面的定位方式外,还可以通过下面的方式进行定位,效果是一样的,这里不再详细说明。

from selenium.webdriver.common.by import By

browser.find_element(By.ID, 'username')
browser.find_element(By.NAME, 'username')
browser.find_element(By.CLASS_NAME, 'username')
browser.find_element(By.LINK_TEXT, '立即注册')
browser.find_element(By.PARTIAL_LINK_TEXT, '立即')
browser.find_element(By.XPATH, '//input[@placeholder="用户名"]')
browser.find_element(By.CSS_SELECTOR, '#username')
browser.find_element(By.TAG_NAME, '')

3种等待模式

上面的代码,在执行时,都是一闪而过。在真实的手动操作时,操作网页都有个加载过程,极快的代码执行速度会有可能导致页面元素还没有加载出来,代码已经执行到下一步骤,导致程序误判没有找到对应的元素而执行失败。这就需要在代码中适当的位置加入等待时间。

selenium提供了3种等待方式:

  • 强制等待
  • 隐性等待(智能等待)
  • 显性等待(智能等待)

强制等待

使用time模块

from selenium import webdriver
import time
from selenium.webdriver.common.by import By

browser = webdriver.Firefox()
# 实例网站:SHOPWIND商城
url = "http://101.43.8.10:9696/login.html"
# 访问指定的网址
browser.get(url)
browser.find_element("css selector", '#username').clear()
browser.find_element("css selector", '#username').send_keys('buyer')
browser.find_element("css selector", '#password').clear()
browser.find_element("css selector", '#password').send_keys('123456')
time.sleep(2) # 等待2秒
browser.find_element("css selector", '#login_form > dl:nth-child(4) > dd > input.login-submit').click()
time.sleep(2) # 等待2秒
browser.quit()

可以看到在点击登录按钮前和后各等待了2秒(sleep(2)),但这个时间写死的,固定一定要等2秒+2秒。这样有个坏处,就是若执行脚本时网络环境差,进行了10秒才点击了登录按钮,这时等待2秒杀就已经报错了;亦或者,0.5秒钟已经点击完成了登录,但设置等待了2秒,这样整个执行过程就多耗时了1.5秒。

隐性等待

通过selenium提供的implicitly_wait()方法等待元素出现。比如设置了10秒implicitly_wait(10),若元素1秒即可加载出来,那么加载出来后即结束等待,即1秒后就开始执行下面的代码。若元素等待了10秒钟后还没有显示出来,那么就会直接执行下面的代码,引起报错。

隐性等待,是全局的等待时间,只需设置一次。

from selenium import webdriver
from selenium.webdriver.common.by import By

browser = webdriver.Firefox()
# 实例网站:SHOPWIND商城
url = "http://101.43.8.10:9696/login.html"
# 访问指定的网址
browser.get(url)
browser.implicitly_wait(10)
browser.find_element("css selector", '#username').clear()
browser.find_element("css selector", '#username').send_keys('buyer')
browser.find_element("css selector", '#password').clear()
browser.find_element("css selector", '#password').send_keys('123456')
browser.find_element("css selector", '#login_form > dl:nth-child(4) > dd > input.login-submit').click()
browser.quit()

隐性等待只能用于查找元素、等待元素出现,即find_element()

显性等待

通过selenium提供的WebDriverWait()类,等待当前要操作的元素

from selenium.webdriver.support.wait import WebDriverWait

通过selenium提供的expected_conditions模块,判断等待条件是否满足

from selenium.webdriver.support import expected_conditions

expected_conditions提供了很多个判断方法,这里只举一个最常用的presence_of_element_located()元素是否存在(默认每0.5秒去判断一次,这个频率可以自己设置poll_frequency=0.5,一般无需改动)。

wait等待。。。直到util。。。判断方法presence_of_element_located

from selenium import webdriver
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions

browser = webdriver.Chrome()
# 实例网站:SHOPWIND商城
url = "http://101.43.8.10:9696/login.html"
# 访问指定的网址
browser.get(url)
browser.find_element("css selector", '#username').clear()
browser.find_element("css selector", '#username').send_keys('buyer')
browser.find_element("css selector", '#password').clear()
browser.find_element("css selector", '#password').send_keys('123456')
browser.implicitly_wait(10)
browser.find_element("css selector", '#login_form > dl:nth-child(4) > dd > input.login-submit').click()
# 定义定位元素的方法及对应值
loc = ('xpath', '//*[@id="main"]/div/div/p/b')
# 最多等5秒,判断上面定义的元素是否存在
WebDriverWait(browser, 5).until(expected_conditions.presence_of_element_located(loc))
# 返回定位到的元素的文本信息
text = browser.find_element(*loc).text
# 断言文本信息是否等于登录成功
assert text == "登录成功"

上面的代码是显性等待的例子,是否有必要添加等待,需要根据实际情况来判断,这里只是演示要添加显示等待的基本方法。

3大切换

handle窗口切换

当点击页面上的某个元素后,会重新打开一个页签,这时代码操作在停留在上一个页签,若要操作新开的页签页面中的内容,就要进行handle窗口切换。

from selenium import webdriver
import time
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions

browser = webdriver.Firefox()
# 实例网站:SHOPWIND商城
url = "http://101.43.8.10:9696"
# 访问指定的网址
browser.get(url)
browser.find_element('xpath', '//a[@class="login-btn"]').click()
time.sleep(1)
browser.find_element("xpath", '//input[@placeholder="用户名"]').clear()
browser.find_element("xpath", '//input[@name="username"]').send_keys('buyer')
browser.find_element("xpath", '//input[@type="password"]').clear()
browser.find_element("xpath", '//input[@name="password"]').send_keys('123456')

browser.find_element("xpath", '//input[@title="登录"]').click()
browser.implicitly_wait(10)
loc = ('xpath', '//*[@id="main"]/div/div/p/b')
WebDriverWait(browser, 5).until(expected_conditions.presence_of_element_located(loc))
text = browser.find_element(*loc).text
assert text == "登录成功"
time.sleep(2)
# 点击页面头部的banner广告,在新页签打开页面
browser.find_element("xpath", "//img[@class='lazyload block']").click()
# 打印全部的窗口信息
print(browser.window_handles)
# 打印当前正在操作的窗口信息
print(browser.current_window_handle)
# 切换到新开的页面窗口
browser.switch_to.window(browser.window_handles[-1])
time.sleep(1)
# 打印当前正在操作的窗口信息
print(browser.current_window_handle)
# 获取新开页面的元素内容
text1 = browser.find_element("xpath", "//div[@class='channel2-special-goods']//h2").text
print(text1)
time.sleep(5)
browser.quit()

上面的代码进行了窗口切换的演示,为了方便演示加入了较多的time.sleep()

其中,

  • window_handles:获取当前打开的所有窗口句柄,返回类型为一个列表;
  • current_window_handle:获取当前正在操作的窗口句柄;
  • switch_to.window():切换窗口,需要传递一个参数,代表切换到哪个窗口;

还有另外一个判断方法,先通过window_handles获取全部打开的句柄handles,然后执行新开窗口的操作,通过new_window_is_opened()方法判断是否有新句柄(新的句柄总数大于之前的句柄总数)

WebDriverWait(browser, 5).until(expected_conditions.new_window_is_opened(handles))

iframe切换

iframe表示在html中再嵌入一层或多层html。目前的网站,多数都是使用框架写的,很少再有iframe了。这里只列举几个iframe切换的常用方法,不再进行举例说明。(很老旧的一些系统、网站还是有iframe的)

  • switch_to.frame():切换到frame,需要一个参数,frame名称、frame下标或WebElement对象均可
  • switch_to.default_content():切换到主html页面
  • switch_to.parent_frame():切换到上一层html页面(父页面)

当定位的iframe可以用时自动切换过去:

WebDriverWait(browser, 5).until(expected_conditions.frame_to_be_available_and_switch_to_it(iframe))

alert弹窗切换

alert弹窗,即浏览器原生的弹窗。由于目前网站都是使用框架,alert弹窗都变成了框架或开发自己写的弹窗,即alert弹窗也较少使用。这里只列举几个alert弹窗切换的常用方法,不再进行举例说明。

  • switch_to.alert:切换到alert弹框
  • accept():确定
  • dismiss():取消

等待alert弹出并自动切换WebDriverWait(browser, 5).until(expected_conditions.alert_is_present)

常用操作

鼠标操作

鼠标操作一般包括:单击、双击、右击、光标悬停、拖拽等,下面逐一介绍。

单击

前面已经介绍了browser.find_element('', '').click()这种形式的单击操作,接下来通过selenium提供的ActionChains模块来模拟鼠标操作,下面的几个鼠标事件都有这个模块进行实现。

from selenium import webdriver
import time
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions
from selenium.webdriver import ActionChains

browser = webdriver.Chrome()
# 实例网站:SHOPWIND商城
url = "http://101.43.8.10:9696"
# 访问指定的网址
browser.get(url)
browser.implicitly_wait(10)
# 定义一个鼠标模拟对象
action = ActionChains(browser)
# 定位登录按钮
login = browser.find_element('xpath', '//a[@class="login-btn"]')
# 点击登录按钮,通过perform进行执行
action.click(login).perform()

time.sleep(5)
browser.quit()

上面的关键代码可以写成一行

ActionChains(browser).click(browser.find_element('xpath', '//a[@class="login-btn"]')).perform()

可以看到通过ActionChains提供的单击操作复杂很多,更建议执行使用browser.find_element('', '').click()

光标悬停

通过ActionChainsmove_to_element悬停指定元素上

from selenium import webdriver
import time
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions
from selenium.webdriver import ActionChains

browser = webdriver.Chrome()
# 实例网站:SHOPWIND商城
url = "http://101.43.8.10:9696"
# 访问指定的网址
browser.get(url)
browser.implicitly_wait(10)
# 定义一个鼠标模拟对象
action = ActionChains(browser)
# 定位左侧导航家电五金
jiadian = browser.find_element('link text', '家用电器、五金')
# 光标移动到家电元素位置悬停
action.move_to_element(jiadian).perform()

time.sleep(5)
browser.quit()
拖拽

通过ActionChainsmove_to_element悬停指定元素上

source = driver.find_element('xpath','//*[@id="1"]/div/div[3]/div/div[1]')
time.sleep(1)
target = driver.find_element('xpath','//*[@id="4"]/div/div/div')
time.sleep(1)
# 从source的位置拖拽到target的位置,source是指按下鼠标的位置,target是指抬起鼠标按键的位置
ActionChains(driver).drag_and_drop(source,target).perform()
双击

通过double_click(),实现双击,这里只做一下简单演示。

# 定义一个鼠标模拟对象
action = ActionChains(browser)
# 定位登录按钮
login = browser.find_element('xpath', '//a[@class="login-btn"]')
# 双击登录按钮
action.double_click(login).perform()
右击

通过context_click(),实现鼠标右击,这里只做一下简单演示。

# 定义一个鼠标模拟对象
action = ActionChains(browser)
# 定位登录按钮
login = browser.find_element('xpath', '//a[@class="login-btn"]')
# 鼠标右击登录按钮
action.context_click(login).perform()

键盘操作

单个控件的操作

通过导入Keys模块,来实现通过代码实现键盘操作

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

browser = webdriver.Chrome()
# 实例网站:SHOPWIND商城
url = "http://101.43.8.10:9696"
# 访问指定的网址
browser.get(url)
browser.implicitly_wait(10)

dlb=browser.find_element('xpath', '//a[@class="login-btn"]')
dlb.send_keys(Keys.ENTER)
dlb.send_keys(Keys.CONTROL, Keys.ALT, Keys.SPACE)

全局操作

通过ActionChains模块,来实现通过代码实现键盘操作,对整个页面有效

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

browser = webdriver.Chrome()
# 实例网站:SHOPWIND商城
url = "http://101.43.8.10:9696"
# 访问指定的网址
browser.get(url)
browser.implicitly_wait(10)

ac = ActionChains(browser)
ac.send_keys(Keys.CONTROL, Keys.SPACE).perform()
ac.send_keys(Keys.CONTROL,‘a’).perform()
ac.send_keys(Keys.F1).perform()

下拉列表

下拉列表,即select标签,selenium中提供了对应的模块进行操作。(注意,目前有很多所谓的下拉列表,并不是通过select标签实现的)

from selenium import webdriver
import time
from selenium.webdriver.support.select import Select

browser = webdriver.Chrome()
# 实例网站:SHOPWIND商城
url = "http://101.43.8.10:9696"
# 访问指定的网址
browser.get(url)
browser.implicitly_wait(10)

browser.find_element('xpath', '//a[@class="login-btn"]').click()
time.sleep(1)
browser.find_element("xpath", '//input[@placeholder="用户名"]').clear()
browser.find_element("xpath", '//input[@name="username"]').send_keys('buyer')
browser.find_element("xpath", '//input[@type="password"]').clear()
browser.find_element("xpath", '//input[@name="password"]').send_keys('123456')

browser.find_element("xpath", '//input[@title="登录"]').click()
browser.implicitly_wait(10)

browser.get('http://101.43.8.10:9696/my_address/index.html')

browser.find_element("xpath", "//div[@class='eject_btn']").click()
# 定位select元素
select_el1 = browser.find_element("xpath", "//div[@id='region']//select")
# 获取select对象
select_el_list1 = Select(select_el1)
# 通过索引选择下拉框中的数据,注意第一个值的索引是0,这个网站,第一个值为”请选择“
select_el_list1.select_by_index(10)
time.sleep(1)
# 定位select二级元素
select_el2 = browser.find_element("xpath", "//select[2]")
select_el_list2 = Select(select_el2)
time.sleep(1)
select_el_list2.select_by_index(1)
time.sleep(1)
# 通过html页面中的value值进行下拉框数据的选择
select_el_list1.select_by_value('2')

time.sleep(1)
# 通过下拉选项值的文本内容进行选择
select_el_list1.select_by_visible_text("河北省")

time.sleep(5)
browser.quit()

其中,先进行元素定位,将定位到的元素以参数形式传给Select(),获得下拉框选项内容的WebElement。通过索引select_by_index()、值select_by_value()或文本select_by_visible_text()的形式来选择具体的值。

除了上面的select专属方式,也可以通过点击展开下拉列表,然后点击要选择的元素进行选择。

checkbox

这里使用12306网站的作为例子,勾选车次类型演示checkbox的操作

import time

from selenium import webdriver

browser = webdriver.Chrome()
# 实例网站:SHOPWIND商城
url = "https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc&fs=%E4%B8%8A%E6%B5%B7,SHH&ts=%E5%8C%97%E4%BA%AC,BJP&date=2023-01-29&flag=N,N,Y"
# 访问指定的网址
browser.get(url)
browser.implicitly_wait(10)

# 定位车次类型,这次返回的是一个列表
train_types = browser.find_elements("name", "cc_type")
# 遍历列表,勾选每一个选项
for train_type in train_types:
  	# 判断type属性是否等于checkbox
    if train_type.get_attribute("type") == "checkbox":
        train_type.click()
# 勾选某个选项
# train_types[1].click()

time.sleep(5)
browser.quit()

radiobox

import time

from selenium import webdriver

browser = webdriver.Chrome()
# 实例网站:SHOPWIND商城
url = "https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc&fs=%E4%B8%8A%E6%B5%B7,SHH&ts=%E5%8C%97%E4%BA%AC,BJP&date=2023-01-29&flag=N,N,Y"
# 访问指定的网址
browser.get(url)
browser.implicitly_wait(10)
# 单程往返单选框,组定位,返回一个列表
come_go = browser.find_elements("name", "singleRoundType")
# 本例子中只有两个选项:单程和往返,这里勾选往返
come_go[1].click()
time.sleep(5)
browser.quit()

submit()

按钮的type类型若为submit,在点击按钮的时候,可以使用submit()来代替click()

from selenium import webdriver
import time

browser = webdriver.Chrome()
# 实例网站:SHOPWIND商城
url = "http://101.43.8.10:9696"
# 访问指定的网址
browser.get(url)
browser.implicitly_wait(10)

browser.find_element('xpath', '//a[@class="login-btn"]').click()
time.sleep(1)
browser.find_element("xpath", '//input[@placeholder="用户名"]').clear()
browser.find_element("xpath", '//input[@name="username"]').send_keys('buyer')
browser.find_element("xpath", '//input[@type="password"]').clear()
browser.find_element("xpath", '//input[@name="password"]').send_keys('123456')

browser.find_element("xpath", '//input[@title="登录"]').submit()

time.sleep(5)
browser.quit()

JS操作

某些情况下,使用selenium无法或者较难实现的时候,通过调用JS代码可以曲线实现。通过execute_script()方法

修改控件属性

例如有些需要操作的控件,设置成了readonly只读或者disabled置灰无法进行值的输入,可以通过JS代码去掉对应的属性,即可继续使用selenium操作。这里的例子是12306网站,在没有选择往返程时,返程时间控件是置灰的无法操作,这里通过JS去掉disabled属性

import time

from selenium import webdriver

browser = webdriver.Chrome()
# 实例网站:SHOPWIND商城
url = "https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc&fs=%E4%B8%8A%E6%B5%B7,SHH&ts=%E5%8C%97%E4%BA%AC,BJP&date=2023-01-29&flag=N,N,Y"
# 访问指定的网址
browser.get(url)
browser.implicitly_wait(10)
data = browser.find_element("xpath", "//input[@id='train_date']")
browser.find_element("xpath", "//input[@id='train_date']").clear()
time.sleep(1)
browser.find_element("xpath", "//input[@id='train_date']").send_keys("2023-02-02")

back_train_date = browser.find_element("id", "back_train_date")
# 当前返程日期控件为置灰的,需要去掉disabled属性
js = "let el=arguments[0];el.removeAttribute('disabled')"
# 执行js
browser.execute_script(js, back_train_date)
time.sleep(1)
browser.find_element("id", "back_train_date").clear()
browser.find_element("id", "back_train_date").send_keys("2023-02-05")

time.sleep(30)
browser.quit()
滑动滚动条

定位元素、操作元素,是无需一定要元素可见的,即当前窗口中没有显示出元素,但页面已经加载完,元素也已经加载,只是没有显示(需要拖拽滚动即可让元素显示出来的),这种情况是无需进行滑动滚动条的。

滑动滚动条的应用场景是:页面存在懒加载或慢加载的情况下,必须将页面滚动条拖拽到某个位置,对应的元素才被加载出来,这样才能定位到元素进行操作。

  • scrollTo 滚动到

    scrollTo(0, document.body.scrollHeight) 从顶部滚动到底部

    scrollTo(document.body.scrollHeight, 0) 从底部滚动到底部

    scrollTo(0, document.body.scrollHeight / 2) 从顶部滚动到页面中间位置

    from selenium import webdriver
    import time
    
    browser = webdriver.Chrome()
    # 实例网站:SHOPWIND商城
    url = "http://101.43.8.10:9696"
    # 访问指定的网址
    browser.get(url)
    browser.implicitly_wait(10)
    browser.execute_script("scrollTo(0, document.body.scrollHeight)")
    time.sleep(1)
    browser.execute_script("scrollTo(document.body.scrollHeight, 0)")
    time.sleep(1)
    browser.execute_script("scrollTo(0, document.body.scrollHeight / 2)")
    time.sleep(5)
    browser.quit()
  • scrollBy 滚动(xx像素)

    from selenium import webdriver
    import time
    
    browser = webdriver.Chrome()
    # 实例网站:SHOPWIND商城
    url = "http://101.43.8.10:9696"
    # 访问指定的网址
    browser.get(url)
    browser.implicitly_wait(10)
    # 页面向下滚动500像素高度
    browser.execute_script("scrollBy(0, 500)")
    
    time.sleep(5)
    browser.quit()
  • scrollIntoView 滚动到元素

    from selenium import webdriver
    import time
    
    browser = webdriver.Chrome()
    # 实例网站:SHOPWIND商城
    url = "http://101.43.8.10:9696"
    # 访问指定的网址
    browser.get(url)
    browser.implicitly_wait(10)
    
    login_btn = browser.find_element('xpath', '//a[@class="login-btn"]')
    # 滚动到登录按钮,顶部对齐 
    browser.execute_script("arguments[0].scrollIntoView()", login_btn)
    time.sleep(2)
    # 滚动到登录按钮,底部对齐
    browser.execute_script("arguments[0].scrollIntoView(false)", login_btn)
    
    time.sleep(5)
    browser.quit()
  • selenium提供了location_once_scrolled_into_view方法来实现滚动到元素对应位置

    from selenium import webdriver
    import time
    
    browser = webdriver.Chrome()
    # 实例网站:SHOPWIND商城
    url = "http://101.43.8.10:9696"
    # 访问指定的网址
    browser.get(url)
    browser.implicitly_wait(10)
    
    login_btn = browser.find_element('xpath', '//a[@class="login-btn"]')
    # 滚动条滚动到登录按钮的位置
    login_btn.location_once_scrolled_into_view
    
    time.sleep(5)
    browser.quit()

上传

send_keys

若上传文件的控件是由input标签及type="file",那么可以直接使用send_keys直接传递上传文件即可

from selenium import webdriver
import time
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions
from selenium.webdriver import ActionChains

browser = webdriver.Chrome()
# 实例网站:SHOPWIND商城
url = "http://101.43.8.10:9696"
# 访问指定的网址
browser.get(url)
browser.implicitly_wait(10)

browser.find_element('xpath', '//a[@class="login-btn"]').click()
time.sleep(1)
browser.find_element("xpath", '//input[@placeholder="用户名"]').clear()
browser.find_element("xpath", '//input[@name="username"]').send_keys('buyer')
browser.find_element("xpath", '//input[@type="password"]').clear()
browser.find_element("xpath", '//input[@name="password"]').send_keys('123456')

browser.find_element("xpath", '//input[@title="登录"]').submit()

browser.get('http://101.43.8.10:9696/apply/fill.html')

browser.find_element('name', 'identity_front').send_keys('/Users/laobai/workspaces/selnium_test_01/demo01/01.jpg')

time.sleep(5)
browser.quit()
pywinauto

这个库只对windows系统有效

安装:

$ pip install pywinauto

示例:

from selenium import webdriver
import time
from pywinauto.keyboard import send_keys

browser = webdriver.Chrome()
# 实例网站:SHOPWIND商城
url = "http://101.43.8.10:9696"
# 访问指定的网址
browser.get(url)
browser.implicitly_wait(10)

browser.find_element('xpath', '//a[@class="login-btn"]').click()
time.sleep(1)
browser.find_element("xpath", '//input[@placeholder="用户名"]').clear()
browser.find_element("xpath", '//input[@name="username"]').send_keys('buyer')
browser.find_element("xpath", '//input[@type="password"]').clear()
browser.find_element("xpath", '//input[@name="password"]').send_keys('123456')

browser.find_element("xpath", '//input[@title="登录"]').submit()

browser.get('http://101.43.8.10:9696/apply/fill.html')
# 定位上传按钮
upload_btn = browser.find_element('name', 'identity_front')
# 点击上传按钮
upload_btn.click()
time.sleep(1)
# 向对话框中传递文件
send_keys(r'c:\01.jpg')
# 点击确定按钮
send_keys('{VK_RETURN}')

time.sleep(5)
browser.quit()
pyautogui

这是一个跨平台的第三方库,类似于按键精灵。谨慎安装,会一并安装近90个第三方库,建议安装到虚拟环境中。

安装

$ pip install pyautogui

示例

from selenium import webdriver
import time
import pyautogui

browser = webdriver.Chrome()
# 实例网站:SHOPWIND商城
url = "http://101.43.8.10:9696"
# 访问指定的网址
browser.get(url)
browser.implicitly_wait(10)

browser.find_element('xpath', '//a[@class="login-btn"]').click()
time.sleep(1)
browser.find_element("xpath", '//input[@placeholder="用户名"]').clear()
browser.find_element("xpath", '//input[@name="username"]').send_keys('buyer')
browser.find_element("xpath", '//input[@type="password"]').clear()
browser.find_element("xpath", '//input[@name="password"]').send_keys('123456')
time.sleep(1)
browser.find_element("xpath", '//input[@title="登录"]').submit()
time.sleep(2)
browser.get('http://101.43.8.10:9696/apply/fill.html')
# 定位上传按钮
upload_btn = browser.find_element('xpath', "//dd[@class='pb10']//div[@class='add-image-btn']")
# 点击上传按钮
upload_btn.click()
time.sleep(2)
# 输入要上传文件的路径
pyautogui.write("/Users/laobai/workspaces/selnium_test_01/demo01/01.jpg")
# 点击确认按钮,参数中的2,指的是按2次回车,第一次去确认路径上的文件,第二次是上传文件
pyautogui.press("enter", 2)

time.sleep(5)
browser.quit()

截图

通过save_screenshot()get_screenshot_as_file()可以实现截图,这里两个方法几乎没有区别,截图的后缀推荐使用png

from selenium import webdriver
import time

browser = webdriver.Chrome()
# 实例网站:SHOPWIND商城
url = "http://101.43.8.10:9696"
# 访问指定的网址
browser.get(url)
browser.implicitly_wait(10)

browser.find_element('xpath', '//a[@class="login-btn"]').click()
time.sleep(1)
browser.find_element("xpath", '//input[@placeholder="用户名"]').clear()
browser.find_element("xpath", '//input[@name="username"]').send_keys('buyer')
browser.find_element("xpath", '//input[@type="password"]').clear()
browser.find_element("xpath", '//input[@name="password"]').send_keys('123456')
browser.find_element("xpath", '//input[@title="登录"]').submit()
browser.get('http://101.43.8.10:9696/user/profile.html')
browser.find_element("name", "im_qq").send_keys("123456")

browser.get_screenshot_as_file('04.png')
browser.save_screenshot('05.png')
time.sleep(5)
browser.quit()

文章作者: 老百
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 老百 !
 上一篇
WordPress转Hexo to GiteePages WordPress转Hexo to GiteePages
之前的博客系统使用的是WordPress,由于疏于维护,版本插件等已出现了不兼容等状况。恰好空间到期,就借这个机会,将老旧的博客系统迁移到GiteePages。
2023-02-11
下一篇 
基于Requests + Pytest + Allure搭建接口测试框架 基于Requests + Pytest + Allure搭建接口测试框架
从0到1搭建完整的python+requests+pytest+excel接口自动化测试框架,使用Allure来生成测试报告。
2023-01-15
  目录