-
-
-
-
Python之UI自动化基础
Python之UI自动化
环境搭建
安装Selenium库
pip install -U selenium
准备浏览器驱动文件
因浏览器版本众多,我们只对开发常用的必备适配的浏览器Google的Chrome和Mozilla的Firefox浏览器进行讲解
我们只需要将我们下载好的驱动文件复制到我们python的安装根目录即可
Windows默认的python安装根目录:C:Users自己电脑的用户名AppDataLocalProgramsPythonPython37
Chrome浏览器驱动下载
查看自己电脑安装Chrome浏览器版本进行下载
下载地址:
Firefox浏览器驱动下载
Firefox没有强制的版本对应,但是有驱动文件最低适配浏览器版本的限制,下载时应注意限制的最低版本
使用selenium对浏览器进行操作
各种对浏览器的操作:https://www.frostysun.cn/archives/34.html
## 引入依赖的库
from selenium import webdriver
## 实例化Chrome方法打开一个chrome浏览器并将这个浏览器对象赋值给drive
drive = webdriver.Chrome()
## 使用drive调用get方法,打开一个网站
drive.get("https://www.baidu.com")
# 设置浏览器最大化
drive.maximize_window()
# 设置浏览器窗口的大小
drive.set_window_size(400,800)
# 回退
drive.back()
# 前进
drive.forward()
# 刷新
drive.refresh()
# 获取网页的title
print(drive.title)
# 获取当前网址
print(drive.current_url)
# 获取当前窗口的id
print(drive.current_window_handle)
## 结束会话
"""
为什么我们要关闭呢?
因为我们使用chromedrive程序去打开chrome浏览器
我们最终可能就仅仅是关闭了浏览器,并未关闭chromedrive驱动程序
我们可以在电脑的进程列表中查看到
此时我们需要调用quit方法进行结束进程
需要注意close方法和quit的区别,close是关闭标签页
"""
drive.quit()
HTML简介
WEB页面的组成
一般WEB页面的组成是由HTML+CSS+JavaScript组成
- HTML:页面的基本内容的展现
- CSS:定义页面样式、颜色、布局、大小等等
- JavaScript:可以进行前后端交互、页面操作的事件、动作、一些炫酷的动画等等
掌握内容
我们学习UI自动化,只需要了解简单的HTML标签以及JavaScript的一些DOM对象即可,针对性了解即可,不需要全量学习
掌握Chrome浏览器或者fFirefox浏览器的元素定位等等
熟悉HTML的常用标签
W3school地址:https://www.w3school.com.cn/tags/index.asp
标签 | 释义 |
---|---|
<a></a> | 超链接 |
<body></body> | 页面主体 |
<br/> | 换行 |
<button></button> | 按钮 |
<div></div> | 块 |
<form></form> | 表单 |
<head></head> | 页面的头部信息 |
<html></html> | 定义HTML页面 |
<img> | 定义图片 |
<input> | 输入框 |
<script></script> | 定义JavaScript脚本 |
<select></select> | 下拉列表,和<option></option>一起配合使用 |
<style></style> | CSS样式 |
<table></table> | 表格 |
<title></title | 定义页面的标题 |
元素定位
元素定位的方式
元素的定位方式分为:ID定位、class定位、tagname(标签名)定位、name定位、链接定位、xpath定位、css定位
简单元素定位
## 引入依赖的库
from selenium import webdriver
## 实例化Chrome方法打开一个chrome浏览器并将这个浏览器对象赋值给drive
drive = webdriver.Chrome()
## 打开百度
drive.get("http://www.baidu.com")
"""
通过ID定位
"""
## 定位到百度的输入框
## 返回一个WebElement,也就是一个DOM元素
wele = drive.find_element_by_id("kw")
# 获取这个DOM元素中的一个属性
print(wele.get_attribute("maxlength"))
"""
通过class定位
"""
## 通过class定位的时候,如果class值有多个,我们只能选择其中一个进行定位
## 应为class属性的值可能不唯一,会导致找到多个元素,此方法会返回多个元素
wele = drive.find_elements_by_class_name("s_ipt")
## 此方法只返回找到的第一个元素
wele = drive.find_element_by_class_name("s_ipt")
## 打印获取到的DOM元素中的一个属性
print(wele.get_attribute("maxlength"))
"""
通过name定位
"""
## 返回一个
wele = drive.find_element_by_name("wd")
## 返回多个
wele = drive.find_elements_by_name("wd")
"""
通过标签名定位
"""
## 返回一个
wele = drive.find_element_by_tag_name("input")
## 返回多个
wele = drive.find_elements_by_tag_name("input")
"""
仅针对文字链接
"""
## 完全匹配
wele = drive.find_element_by_link_text("hao123")
wele = drive.find_elements_by_link_text("hao123")
## 模糊匹配
wele = drive.find_element_by_partial_link_text("hao")
wele = drive.find_elements_by_partial_link_text("hao")
复杂元素定位
xpath定位
元素的xpath定位我们可以在浏览器的F12使用ctrl+f调出查找,然后输入我们的xpath表达式进行验证
绝对定位
- 非常依赖于浏览器中元素的位置和顺序,在正常项目中尽量避免使用,开发的代码修改和数据的改变都可能导致之前的绝对定位在修改后失败
可以在浏览器F12开发者模式选中需要定位的元素,然后右键 copy -> copy full xpath
## 编写,通过标签名称以及以及'下标'进行定位 /html/body/div[2]/div[2]/div[5]/div[1]/div/form/span[1]
相对定位
- 很好的避免了上面绝对定位的很多弊端
- 缺点就是相对于绝对定位编写稍显复杂
最新的chrome浏览器然后右键 copy -> copy xpath 即可获取到当前标签的相对定位
## 编写 ## //标签名称[@标签的元素名称="名称的值"] //input[@name="wd"] ## 逻辑与、逻辑或在此处是可以使用的,我们可以用多个元素的名称定位 //标签名称[@标签的元素名称="名称的值" and @标签的元素名称="名称的值"] """ 层级定位 """ ## 根据上级来定位下级 //div[@id="u1"]/a[@name="tj_login"] //*[@id="u1"]/a[7] ## *代表全部 """ 函数定位 """ ## text文本定位 //*[@id="u1"]/a[text()="登录"] ## contains()包含函数定位 //*[@id="u1"]/a[contains(@name,"login")] //*[@id="u1"]/a[contains(text(),"登录")] """ 轴定位 """ # 轴计算 ## ancestor:祖先节点,包含父节点(常用) ## parent:父节点(常用) ## preceding:当前标签节点标签之前的所有节点 ## preceding-sibling:当前节点元素节点标签之前的所有兄弟节点(常用) ## fillowing:当前元素节点标签之后的所有节点 ## following-sibling:当前元素节点标签之后的所有兄弟节点(常用) ## 经常使用多个相同的类表格元素的定位 /轴名称::节点名称 ## 我们定位192.168.3.100地址下的一个元素 //a[text()="资金周转"]/parent::td/following-sibling::td//span
Selenium框架
等待操作
需求:
打开百度,点击登录,等待点击用户名按钮出现,进行点击
Ps:三种等待方式不冲突,可同时使用
强制等待
强制等待非常不推荐使用
## 引入依赖的库
from selenium import webdriver
import time
## 实例化Chrome方法打开一个chrome浏览器并将这个浏览器对象赋值给drive
drive = webdriver.Chrome()
## 打开百度
drive.get("http://www.baidu.com")
## 定位到登录按钮,并进行点击
drive.find_element_by_xpath('//*[@id="u1"]/a[7]').click()
## 强制等待,单位是秒
time.sleep(15)
## 点击用户名登录
drive.find_element_by_xpath('//*[@id="TANGRAM__PSP_10__footerULoginBtn"]').click()
隐性等待
隐式等待较为机智,我们只需要设置最大等待时间即可,系统会自动去判定,如果元素出现,则自动停止等待,去操作元素,如超过等待时间还是没有出现,则出现NoSuchElementException(找不到定位元素)异常,
## 引入依赖的库
from selenium import webdriver
## 实例化Chrome方法打开一个chrome浏览器并将这个浏览器对象赋值给drive
drive = webdriver.Chrome()
## 打开百度
drive.get("http://www.baidu.com")
## 设置等待
## 需要注意的是这个等待是全局的,局限性源码已经交代,用于元素查找操作
## 单位是秒
drive.implicitly_wait(30)
## 定位到登录按钮,并进行点击
drive.find_element_by_xpath('//*[@id="u1"]/a[7]').click()
## 点击用户名登录
drive.find_element_by_xpath('//*[@id="TANGRAM__PSP_10__footerULoginBtn"]').click()
显性等待
显性等待比隐性等待更加方便,需要明确告诉它等待什么,如果条件成立,则继续执行,否则就继续等待,直到超过最大等待时间抛出异常TimeoutException
WebDriverWait(drive,等待时长,轮循周期).until()/until_not()
drive:监控的内容
轮循周期:每隔多少时间进行一次检查,检查是否满足条件
显性等待中的EC(expected_conditions)模块常用方法
title_is(String title)
:判断当前页面的title是否精确等于预期title_contains(String title)
:判断当前页面的title是否包含预期字符串presence_of_element_located(By locator)
:判断某个元素是否被加到了dom树里,并不代表该元素一定可见visibility_of_element_located(By locator)
:判断某个元素是否可见(代表元素非隐藏,元素的宽和高都不等于0)invisibility_of_element_located(By locator)
:判断某个元素中是否不存在于dom树或不可见text_to_be_present_in_element(By locator, String text)
:判断某个元素中的text是否包含了预期的字符串text_to_be_present_in_element_value(By locator, String text)
:判断某个元素中的value属性是否包含了预期的字符串element_to_be_clickable(By locator)
:判断某个元素中是否可见并且是enable(可用)的,这样的话才clickable(可点击)element_to_be_selected(By locator)
:判断某个元素是否被选中了,一般用在下拉列表alert_is_present()
:判断页面上是否存在alert
示例代码
## 引入依赖的库
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
import time
## 实例化Chrome方法打开一个chrome浏览器并将这个浏览器对象赋值给drive
drive = webdriver.Chrome()
## 打开百度
drive.get("http://www.baidu.com")
"""
WebDriverWait(drive,等待时长,轮循周期).until()/until_not()
"""
## 检查使用账户登录是否存在
## 此处传参我们使用元组的方式,我们需要传定位的方式,以及定位的表达式
## 定位方式我们可以用By下面定义的方式
# EC.visibility_of_element_located((By.ID,"TANGRAM__PSP_10__footerULoginBtn"))
## 设置显性等待,第三个参数有默认值,默认0.5秒
## 每0.5秒检查一次,元素是否存在,如果存在则结束等待,如果不存在则继续等待,超过20秒则超时并抛出异常
WebDriverWait(drive,20).until(EC.visibility_of_element_located((By.ID,"TANGRAM__PSP_10__footerULoginBtn")))
## 点击用户名登录
drive.find_element_by_xpath('//*[@id="TANGRAM__PSP_10__footerULoginBtn"]').click()
iframe切换定位
如果直接定位
iframe
中的元素ID之类的,可能会导致定位失败报错,因为iframe
中一般内嵌了另一个HTML
页面,例如QQ音乐的登录弹出框确定定位内容是否在iframe中可以在F12模式下的Element下的HTML路径中看到
如果要查找页面的iframe有多少个,可以直接用相对定位
//iframe
传统切换
"""
查看源码了解参数,drive.switch_to.frame()方法可以有三种定位方式传参
1、通过页面中iframe的下标传参
driver.switch_to.frame(1)
2、通过iframe的name属性
driver.switch_to.frame('frame_name')
3、通过具有返回element的方法定位
我们一般使用第三种
driver.switch_to.frame(driver.find_elements_by_tag_name("iframe")[0])
"""
## 引入依赖的库
from selenium import webdriver
import time
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
## 实例化Chrome方法打开一个chrome浏览器并将这个浏览器对象赋值给drive
drive = webdriver.Chrome()
drive.get("https://y.qq.com/")
## 设置切换iframe
drive.switch_to.frame(drive.find_element_by_xpath('//*[@id="frame_tips"]'))
## 设置等待时间2秒
time.sleep(2)
## 点击头像登录
drive.find_element_by_xpath('//*[@id="img_out_851146837"]')
使用显性等待的方式切换
## 在此处我们可以通过EC的显性等待判断是否是否存在,EC会帮我们自动切换到iframe,无需我们手动切换
WebDriverWait(drive,10).until(EC.frame_to_be_available_and_switch_to_it(drive.find_element_by_xpath('//*[@id="frame_tips"]')))
## 然后我们直接去操作对应的元素即可
## 点击头像登录
drive.find_element_by_xpath('//*[@id="img_out_851146837"]')
切出iframe窗口
## 切出iframe,回到顶层默认的HTML页面中
drive.switch_to.default_content()
## 切出iframe,切回父级
drive.switch_to.parent_frame()
窗口(页签)切换
切换窗口需要用到窗口的句柄,也就是ID
打开百度,搜索京东,并打开京东页面
传统切换
## 引入依赖的库
from selenium import webdriver
import time
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
## 实例化Chrome方法打开一个chrome浏览器并将这个浏览器对象赋值给drive
drive = webdriver.Chrome()
drive.get("http://www.baidu.com")
## 显性等待,等待出现输入框
WebDriverWait(drive,10).until(EC.visibility_of_element_located((By.XPATH,'//*[@id="kw"]')))
## 输入京东商城
drive.find_element_by_xpath('//*[@id="kw"]').send_keys("京东商城")
## 点击百度一下按钮
drive.find_element_by_xpath('//*[@id="su"]').click()
## 等待搜索结果出现
WebDriverWait(drive,10).until(EC.visibility_of_element_located((By.XPATH,'//*[@id="m19g5x5s"]/div/h2/a[1]')))
## 点击搜索到的京东商城
drive.find_element_by_xpath('//*[@id="m19g5x5s"]/div/h2/a[1]').click()
## 添加等待时间,等待窗口打开
time.sleep(1)
## 返回是一个列表的形式
handles = drive.window_handles
## 一般新窗口是最新出现的,他的ID会在最后一个,直接调用切换方法切换即可
drive.switch_to.window(handles[-1])
## 显示当前当前所在窗口的句柄
print(drive.current_window_handle)
## 点击登录
WebDriverWait(drive,10).until(EC.visibility_of_element_located((By.XPATH,'//*[@id="ttbar-login"]/a[1]')))
drive.find_element_by_xpath('//*[@id="ttbar-login"]/a[1]').click()
使用显性等待切换
## 引入依赖的库
from selenium import webdriver
import time
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
## 实例化Chrome方法打开一个chrome浏览器并将这个浏览器对象赋值给drive
drive = webdriver.Chrome()
drive.get("http://www.baidu.com")
## 显性等待,等待出现输入框
WebDriverWait(drive,10).until(EC.visibility_of_element_located((By.XPATH,'//*[@id="kw"]')))
## 输入京东商城
drive.find_element_by_xpath('//*[@id="kw"]').send_keys("京东商城")
## 点击百度一下按钮
drive.find_element_by_xpath('//*[@id="su"]').click()
## 等待搜索结果出现
WebDriverWait(drive,10).until(EC.visibility_of_element_located((By.XPATH,'//*[@id="m19g5x5s"]/div/h2/a[1]')))
## 获取窗口句柄
handles = drive.window_handles
## 点击搜索到的京东商城
drive.find_element_by_xpath('//*[@id="m19g5x5s"]/div/h2/a[1]').click()
## 显性等待京东商城打开
WebDriverWait(drive,10).until(EC.new_window_is_opened(handles))
## 获取当前的句柄
handles = drive.window_handles
## 切换到新的窗口
drive.switch_to.window(handles[-1])
## 显示当前当前所在窗口的句柄
print(drive.current_window_handle)
## 点击登录
WebDriverWait(drive,10).until(EC.visibility_of_element_located((By.XPATH,'//*[@id="ttbar-login"]/a[1]')))
drive.find_element_by_xpath('//*[@id="ttbar-login"]/a[1]').click()
alert弹窗
"""
alert弹框
"""
alert = drive.switch_to.alert
## 输入内容
alert.send_keys("")
## 同意,确定,OK
alert.accept()
## 不同意,否,cancel
alert.dismiss()
## 获取弹窗内容
print(alert.text)
鼠标操作
鼠标操作由selenium的actionchains来完成
存储鼠标操作,使用perform()来操作鼠标执行
"""
支持的操作:
double_click:双击
context_click:右键操作
drag_and_drop:拖拽操作
move_to_element 鼠标悬浮操作
"""
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
## 实例化Chrome方法打开一个chrome浏览器并将这个浏览器对象赋值给drive
drive = webdriver.Chrome()
## 打开百度
drive.get("http://www.baidu.com")
##设置全局隐性等待
drive.implicitly_wait(30)
## 全屏当前窗口
drive.maximize_window()
## 先定位元素 //*[@id="u1"]//a[@name="tj_settingicon"]
ele = drive.find_element_by_xpath('//*[@id="u1"]//a[@name="tj_settingicon"]')
## 实例化ActionChains
ac = ActionChains(drive)
## 将要执行的操作添加到我们的执行列表
ac.move_to_element(ele)
## 调用执行方法进行执行
ac.perform()
"""
由于ActionChains类中大部分的操作的函数返回值为当前类的引用,我们可以将一个元素的多个鼠标操作直接写到一起,方便操作
源码中有部分示例可作参考
"""
## 鼠标悬浮设置按钮
ActionChains(drive).move_to_element(drive.find_element_by_xpath('//*[@id="u1"]//a[@name="tj_settingicon"]')).perform()
## 通过单击选择设置中的高级搜索按钮
ActionChains(drive).click(drive.find_element_by_xpath('//*[@id="wrapper"]//a[text()="高级搜索"]')).perform()
## 此处也可以调用以下方法来实现
drive.find_element_by_xpath('//*[@id="wrapper"]//a[text()="高级搜索"]').click()
下拉列表操作
下拉列表selenium框架提供的对应的select类操作
"""
选择下拉列表中的标签有以下三种方式:
通过元素下标选择:select_by_index() 下标从0开始
通过value属性的值来选择:select_by_value()
通过下拉列表中展示的文本值来选择:select_by_visible_text()
"""
## 引入select选择的类
from selenium.webdriver.support.ui import Select
## 前置代码参考鼠标选择,我们使用高级搜索中的下拉列表来操作
## 将时间下拉框使用文本值来定位并选择到最近一年
Select(drive.find_element_by_xpath('//*[@id="adv-setting-4"]/select')).select_by_visible_text("最近一年")
## 将文档格式下拉框使用value来定位并选择到doc
Select(drive.find_element_by_xpath('//*[@id="adv-setting-5"]/select')).select_by_value('doc')
键盘操作
普通的键盘输入我们可以直接调用send_keys即可,但是一些组合键比如ctrl+a、ctrl+c等等一些组合键可能就没有办法输入了,selenium提供了keys类来对一些组合键进行操作
from selenium.webdriver.common.keys import Keys
"""
原理是在Keys类中将所有的特殊定义为常量,常量的值为C语言中的对应编码
"""
## ctrl+a
send_keys(Keys.CONTROL, 'a')
## 回车
Keys.ENTER
对浏览器页面的滚动操作
"""
scrollIntoView(false) 带有参数false则是滚动到底部,如果没有参数则是滚动到顶部
需要注意的是这个滚动是以元素为基准的
"""
drive.execute_script("arguments[0].scrollIntoView(false)",element)
## 滚动到页面最顶部
drive.execute_script("window.scrollTo(document.body.scrollHeight,0)")
## 滚动到页面最底部
drive.execute_script("window.scrollTo(0,document.body.scrollHeight)")
时间输入框的处理
"""
对于时间输入框的处理我们一般有以下两种方式
输入框中有readonly属性的
我们可以使用js的dom对象将元素的readonly属性修改,以达到可以输入的目录,将我们的内容输入到我们的输入框中
比如12306的官网,我们可以在console下执行以下代码就可以将输入框修改为可输入状态
document.getElementById('train_date').readOnly=false
或者可以将readOnly属性移除掉
document.getElementById('train_date').removeAttribute('readOnly')
然后使用dom对象给输入框重新赋值
document.getElementById('train_date').value="2020-01-10"
"""
drive.execute_script("document.getElementById('train_date').readOnly=false;document.getElementById('train_date').value='2020-01-10';")
## 此处我们也可以调用DOM对象的点击去点击查询按钮
drive.execute_script('document.getElementById('search_one').click()')
## 也可以使用arguments[0]将定位到的元素传递给js脚本进行操作
element = drive.find_element_by_xpath('//*[@id="search_one"]')
drive.execute_script("arguments[0].click()",element)
上传文件
上传文件分为两种
1、可以直接输入上传文件路径的,我们可以直接调用send_keys,将文件路径输入到输入框中
2、直接是按钮,没有输入框的,我们可能需要用到第三方的python库,针对Windows或者macOS系统的弹出框进行文件选择操作
可能用到的第三方库有Autolt、Python pywin32(可以识别对话框句柄,进行操作)
windows窗口的弹出框的定位也需要使用到一个软件WinSpy来定位,Windows元素的定位是全部绝对定位,没有相对定位
定位到相对的元素,然后使用pywin32包提供的类进行操作
import win32gui
import win32con
这部分内容网上有很多相关的教程,我们在实际项目中基本是使用提前封装好的方法直接调用的,需要注意的是不同浏览器打开的标题可能不一致
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import win32gui
import win32con
"""
需要注意的是国内安装pywin32极有可能安装失败,原因是因为国内被墙,会导致下载失败
可以找到安装的whl文件然后 pip install 文件名称 进行安装
"""
class UpdateFile():
def update_file(self,filepath):
## 一级窗口定位
update_window = win32gui.FindWindow("#32770","打开")
## 二级窗口定位
comboxex32 = win32gui.FindWindowEx(update_window,0,"ComboBoxEx32",None)
## 三级窗口定位
combox = win32gui.FindWindowEx(comboxex32,0,"ComboBox",None)
## 四级窗口,定位文本输入窗口
edit = win32gui.FindWindowEx(combox,0,"Edit",None)
## 二级窗口,打开按钮
button = win32gui.FindWindowEx(update_window,0,"Button","打开(&O)")
## 输入文件地址
win32gui.SendMessage(edit,win32con.WM_SETTEXT,None,filepath)
## 点击打开按钮上传文件
win32gui.SendMessage(update_window, win32con.WM_COMMAND, 1, button)
if __name__ == '__main__':
UpdateFile().update_file("D:\\测试.txt")