要想操作Web界面上的元素,首先要定位到该元素,Selenium提供了定位元素的API,这些方法都被定义在WebDriver类中,浙西额方法都是以find开头。
方法名称 | 描述 | 可能带来的问题 |
---|---|---|
find_element_by_id | 通过id定位元素 | 开发代码id应该是唯一的,常用的id进行定位 |
find_element_by_xpath | 通过xpath定位元素 | 通过浏览器插件来获得xpath路径,选中元素右键copy- copy xpath |
find_element_by_link_text | 通过链接文本定位元素 | – |
find_element_by_partial_link_text | 通过部分链接文本定位元素 | 链接部分文本可能会存在同一个界面多个链接,返回结果就可能是个集合 |
find_element_by_name | 通过属性名称定位元素 | 可能会返回多个元素,因为名字重复,仅返回第一个;使用find_elements_by_name()返回一个集合 |
find_element_by_tag_name | 通过标签名称定位元素 | 一个界面可能会存在多个相同标签的组建,返回的是一个集合 |
find_element_by_class_name | 通过css class定位元素 | class的name不重复的情况下可用,反之会返回一个集合 |
find_element_by_css_selector | 通过css选择器定位元素 | 通过浏览器插件来获取selector,选中元素右键copy- copy selector |
将以上定位元素的方法封装一下,使用的是driver.find_element(*loc)
,通过哪种方式定位引入By,例如By.ID
def get_element(driver, *loc):
element = driver.find_element(*loc)
return element
if __name__ == '__main__':
driver = webdriver.Chrome()
driver.get('https://www.baidu.com/')
input = get_element(driver,By.ID,'kw') #使用方式
# loc = (By.ID, 'kw')
# input = get_element(driver, *loc) #第二种方式
input.send_keys('即可时间')
sleep(1)
get_element(driver,By.ID,'su').click()
sleep(1)
driver.quit()
传参的时候注意也需要加*号
客户端API怎样和驱动进行通信,驱动又怎么驱动浏览器进行工作,其之间的通信协议是什么,又是怎样协同工作的
举一个打车的例子,会有三个角色:
与WebDriver映射关系:
API和驱动之间是一个C/S,驱动中包含一个http 服务器,有一个端口用来监听请求
webdriver使用的协议是:JSON Wire protocol
通信的数据格式是JSON
属性
# | 属性 | 属性描述 |
---|---|---|
1 | driver.name | 浏览器名称 |
2 | driver.title | 当前页面的标题 |
3 | driver.current_url | 当前页面url |
4 | driver.page_source | 当前页面源码 |
5 | driver.current_window_handle | 窗口句柄 |
6 | driver.window_handles | 当前窗口所有句柄 |
方法
# | 方法 | 方法描述 |
---|---|---|
1 | driver.back() | 后退一个界面 |
2 | driver.refresh() | 刷新界面 |
3 | driver.forward() | 前进一个界面 |
4 | driver.close() | 关闭当前窗口 |
5 | driver.quit() | 退出浏览器 |
6 | driver.switch_to.frame() | 切换到frame |
7 | driver.switch_to.alert | 切换到alert |
8 | driver.switch_to.active_element | 切换到活动元素 |
通过window_handles以及switch_to.window方法实现不同窗口切换:
self.driver.get('https://www.baidu.com')
self.driver.find_element_by_link_text('新闻').click()
handles = self.driver.window_handles
while 1: #往复切换
for handle in handles:
self.driver.switch_to.window(handle)
sleep(2)
测试网站:https://sahitest.com/demo/
元素属性
# | 属性 | 属性描述 |
---|---|---|
1 | element.id | 元素id |
2 | element.tag_name | 标签名称(input) |
3 | element.size | 元素宽高 |
4 | element.rect | 宽高以及坐标 |
5 | element.text | 文本内容 |
没有value的属性
元素方法
# | 方法 | 方法描述 |
---|---|---|
1 | element.send_keys() | 输入框中输入内容 |
2 | element.clear() | 清空内容 |
3 | element.click() | 按钮或者超链接点击 |
4 | element.get_attribute() | 获得元素属性值(type、text、value) |
5 | element.is_selected() | 是否被选中(返回true、false) |
6 | element.is_enabled() | 是否可用(返回true、false) |
7 | element.is_displayed() | 是否显示(返回true、false) |
8 | element.value_of_css_property() | css属性值(font、color) |
form表单的流程:
代码举例
本地建立html界面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>form表单事例</title>
</head>
<body>
<form action="javascript:alert('提交成功')">
Username:<input type = 'text' name = 'username' id = 'username'><br/>
Password:<input type = 'password' name = 'password' id = 'password'><br/>
<input type="submit" value="submit" id="submit">
</form>
</body>
</html>
获取本地建立的html界面并打开
def __init__(self):
self.driver = webdriver.Chrome()
#os.path.abspath(__file__) 当前文件的路径
#os.path.dirname(path) 当前路径的文件所在的文件夹
path = os.path.dirname(os.path.abspath(__file__))
file_path = 'file:///' + path + '/formex.html'
self.driver.get(file_path)
进行form的系列操作
username = self.driver.find_element_by_id('username')
username.send_keys('wqq')
password = self.driver.find_element_by_id('password')
password.send_keys('123456')
sleep(2)
print(username.get_attribute('value')) #获取元素属性,可以进行校验
print(password.get_attribute('value'))
self.driver.find_element_by_id('submit').click()
sleep(2)
self.driver.switch_to.alert.accept() # 进入alert界面后选择接受后,关闭alert
sleep(2)
username.clear() #清空输入框
password.clear()
self.driver.quit()
checkbox-多选框,定位到之后,可以先判断一下是否已经选择,以防已经选择再click一下就取消选择了
swimming = self.driver.find_element_by_name('swimming')
if not swimming.is_selected():
swimming.click()
ratio-单选框
gender:<input type="radio" name="gender" value="male">男
<input type="radio" name="gender" value="female">女<br/>
可以发现那么是一样的,所以元素定位后返回的是一个集合,ratio没有判断是否被选中的属性,所以可以根据下标判断选中哪个
gender = self.driver.find_elements_by_name('gender')
gender[0].click()
select-下拉框,分位单选和多选
单选
城市:
<select name="provide" id="provide">
<option value="bj">Beijing</option>
<option value="sd">Shandong</option>
<option value="sh">Shanghai</option>
</select>
多选,区别在于加一个multiple参数
城市:
<select name="provide" id="provide" multiple>
<option value="bj">Beijing</option>
<option value="sd">Shandong</option>
<option value="sh">Shanghai</option>
</select>
select定位需要两步,先找到下拉框元素,然后定义成Select类型
se = self.driver.find_element_by_id('provide')
select = Select(se)
# | 方法/属性 | 方法/属性描述 |
---|---|---|
1 | select.select_by_value(‘sd’) | 根据值选择 |
2 | select.select_by_index(0) | 根据索引下标选择(下标从0开始) |
3 | select.select_by_visible_text(‘Shanghai’) | 根据界面显示文本选择 |
4 | select.deselect_all() | 取消选择所有 |
5 | select.deselect_by_index(1) | 根据索引下标取消选择 |
6 | select.deselect_by_value(‘sd’) | 根据值取消选择 |
7 | select.deselect_by_visible_text(‘Shanghai’) | 根据界面显示文本取消选择 |
8 | select.options | 所有选项 |
9 | select.all_selected_options | 所有被选中的选项 |
10 | select.first_selected_option | 第一个被选中的选项 |
属性8、9返回的是一个list,需要通过循环输出,展示内容使用option.text
页面弹框有三种:
# | 方法/属性 | 方法/属性描述 | 适用弹窗 |
---|---|---|---|
1 | accept() | 接受 | alert、confirm、prompt |
2 | dismiss() | 取消 | alert(右上角取消)、confirm(右上角以及取消按钮)、prompt(右上角取消以及取消按钮) |
3 | text | 显示弹框内容 | alert、confirm、prompt |
4 | send_keys(‘20’) | 输入内容 | prompt |
弹窗元素定位到后,需要切换到弹窗界面,再进行后续操作
self.driver.find_element_by_id('alert').click() #元素定位
alert = self.driver.switch_to.alert #切换到弹窗
注:confirm、prompt的切换到弹窗也是使用self.driver.switch_to.alert
在UI自动化测试中,必然会遇到环境不稳定、网络慢的情况,这时不做任何处理的话代码会由于没有找到元素而报错;另外一种情况就是页面使用ajax异步加载机制;这时就要用到wait,而在Selenium中,一共有三种等待方式。
# | 等待方式 | 优点 | 缺点 |
---|---|---|---|
1 | time.sleep(2) | 脚本调试过程中,方便快捷 | python自带模块的time的sleep方式进行等待,虽然可以自定义等待时间,但是当网络条件良好时,依旧会按照预设的时间继续等待,导致整个项目的自动化时间无限长。不建议使用 |
2 | self.driver.implicitly_wait(10) | 隐式等待实际是设置了一个最长的等待时间,如果在规定时间内页面加载完成,则执行下一步;否则一致等待到时间结束,然后执行下一步。 隐式等待在整个driver周期都起作用,在最开始这只一次就可以了 | Javascript一般都是放在body的最后进行加载,实际这个页面的元素已经加载完毕,我们却还在等待全部页面加载结束,也会导致时间很长 |
3 | WebDriverWait(driver=self.driver,timeout=10) | 显式等待,可以设置等待到什么条件后执行下一步,在任何地方都可以设置 |
wait = WebDriverWait(driver=self.driver,timeout=10)
# | 参数 | 参数说明 |
---|---|---|
1 | driver | 传入WebDriver事例 |
2 | timeout | 设置超时时间,等待的最长时间 |
3 | poll_frequency | 调用until或者until_not中的方法的间隔时间,默认是0.5s |
4 | ignored_exceptions | 忽略异常,默认是None |
# | 参数 | 参数说明 |
---|---|---|
1 | method | 在等待期间,每隔一段时间调用这个传入的方法,直到返回值不是False |
2 | message | 如果超时,跑出TimeoutException,将message传入异常 |
获取当前界面的内容
from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver=self.driver,timeout=10) #定义等待参数
wait.until(method=EC.title_is('百度一下,你就知道'),message='') #循环到是true
等待条件下一课详细讲解
使用以下等待条件需要提前导入一个python自带的模块,from selenium.webdriver.support import expected_conditions as EC
# | 等待条件 | 说明 | 返回结果 |
---|---|---|---|
1 | EC.title_is() | 判断title,是否出现 | 布尔 |
2 | EC.title_contains() | 判断title,是否包含某些字符 | 布尔 |
3 | EC.presence_of_element_located((By.ID, ‘div2’)) | 判断某个元素是否被加到了dom树里,并不代表该元素一定可见 | WebElement |
4 | EC.visibility_of_element_located((By.ID, ‘div2’)) | 判断某个元素是否被添加到了dom里并且可见,宽和高都大于0 | WebElement |
5 | EC.visibility_of(self.driver.find_element_by_id(‘btn’)) | 判断元素是否可见,如果可见久返回这个元素 | WebElement |
6 | EC.presence_of_all_elements_located() | 判断是否至少有1个元素存在于dom树中 | 列表 |
7 | EC.visibility_of_any_elements_located() | 判断是否至少有一个元素在页面中可见 | 列表 |
8 | EC.text_to_be_present_in_element() | 判断指定的元素中是否包含了预期的字符串 | 布尔 |
9 | EC.text_to_be_present_in_element_value() | 判断指定元素的属性值中是否包含了预期的字符串 | 布尔 |
10 | EC.frame_to_be_available_and_switch_to_it() | 判断该frame是否可以switch进去 | 布尔 |
11 | EC.invisibility_of_element_located() | 判断某个元素是否存在于dom或者不可见 | 布尔 |
12 | EC.element_to_be_clickable() | 判断某个元素中是否可见并且是enable的,代表可点击 | 布尔 |
13 | EC.staleness_of() | 等待某个元素从dom树中移除 | 布尔 |
14 | EC.element_to_be_selected() | 判断某个元素是否被选中了,一般用在下拉列表 | 布尔 |
15 | EC.element_selection_state_to_be() | 判断某个元素的选中状态是否符合预期 | 布尔 |
16 | EC.element_located_selection_state_to_be() | 判断某个元素的选中状态是否符合预期 | 布尔 |
17 | EC.alert_is_present() | 判断页面上是否存在alert | alert |
dom树-这个元素不一定能看到,可能是被隐藏
Selenium中的鼠标和键盘事件被封装在ActionChains类中,正确的使用方法,先事例化,再使用,执行用到perform()
action = ActionChains(self.driver)
action.double_click(btn).perform()
鼠标、键盘操作方法
# | 方法 | 介绍 |
---|---|---|
1 | click(on_element=None) | 单击鼠标左键 |
2 | click_and_hold(on_element=None) | 点击鼠标左键,不松开 |
3 | content_click(on_element=None) | 点击鼠标右键 |
4 | double_click(on_element=None) | 双击鼠标左键 |
5 | drag_and_drop(source,target) | 拖拽到某个元素然后松开 |
6 | drag_and_drop_by_offset(source,xoffset,yoffset) | 拖拽到某个坐标然后放开 |
7 | action.key_down(value, element=None) | 按下键盘上某个见 |
8 | action.key_up(value, element=None) | 松开某个键 |
9 | action.move_by_offset(xoffset, yoffset) | 鼠标从当前位置移动到某个坐标 |
10 | action.move_to_element(to_element) | 鼠标从当前位置移动到某个元素 |
11 | action.move_to_element_with_offset(to_element, xoffset, yoffset) | 鼠标从当前位置移动到距某个元素(左上角坐标)多少距离的位置 |
12 | action.release(on_element=None) | 在某个元素位置松开鼠标左键 |
13 | action.send_keys(*keys_to_send) | 发送某个键到当前焦点的元素 |
14 | action.send_keys_to_element(element, *keys_to_send) | 发送某个键到指定的元素 |
15 | perform() | 执行上面方法的动作 |
对于键盘的操作,还需要引入Keys模块,包含键盘上的按钮kw.send_keys(Keys.CONTROL,'a')
,意思是Control+a
WebDriver又两个方法来执行JavaScript
举例
alert弹框
def test1(self):
self.driver.get("http://www.baidu.com")
self.driver.execute_script("alert('test')")
sleep(2)
self.driver.switch_to.alert.accept()
sleep(2)
self.driver.quit()
返回页面title
def test2(self):
self.driver.get("http://www.baidu.com")
js = 'return document.title' #返回title
title = self.driver.execute_script(js)
print(title)
sleep(2)
self.driver.quit()
改变组建样式-未成功
def test3(self):
self.driver.get("http://www.baidu.com")
sleep(2)
#改变组件样式
js = 'var q = document.getElementById("kw");q.style.border="2px solid red"'
self.driver.execute_script(js)
sleep(2)
self.driver.quit()
界面滚动条拉到最下面
def test4(self):
self.driver.get("http://www.baidu.com")
sleep(2)
self.driver.find_element_by_id('kw').send_keys('selenium')
self.driver.find_element_by_id('su').click()
sleep(2)
js = 'window.scrollTo(0,document.body.scrollHeight)'
self.driver.execute_script(js)
sleep(2)
self.driver.quit()
# | 方法 | 方法描述 |
---|---|---|
1 | self.driver.save_screenshot(filename) | 获取当前屏幕截图并保存为指定文件,filename指保存的文件名或者图片的文件名 |
2 | self.driver.get_screenshot_as_base64() | 获取当前屏幕截图base64位编码字符串 |
3 | self.driver.get_screenshot_as_file(file_path) | 获取当前屏幕截图,参数是保存路径 |
4 | self.driver.get_screenshot_as_png() | 获取当前屏幕截图的二进制数据 |
在实际项目中,常常或是用当前时间来命名文件,代码如下
st = strftime("%Y-%m-%d-%H-%M-%S",localtime(time())) #获取当前时间
file_name = st+'.png'
path = os.path.abspath('screenshot')
file_path = path + '/' + file_name #图片存放位置在screenshot文件夹下面
self.driver.get_screenshot_as_file(file_path)
frame标签有frame set、frame、iframe三种,frameset跟其他标签没有区别,不会影响正常定位,而frame、iframe对selenium定位而言是一样的
# | 方法 | 方法描述 |
---|---|---|
1 | self.driver.switch_to.frame(reference) | 切换frame,reference是传入参数,用来定位frame,可以传入id、name、index以及webelement |
2 | self.driver.switch_to.default_content() | 返回主文档,焦点移到主文档,不能再访问frame中的内容 |
3 | self.driver.switch_to.parent_frame() | 返回父文档,可以访问框架外部 |
事例代码
def test_frame(self):
self.driver.get('https://sahitest.com/demo/framesTest.htm')
sleep(2)
top = self.driver.find_element_by_name('top')
self.driver.switch_to.frame(top) #先进入到frame窗口再进行窗口内操作
self.driver.find_element_by_xpath('/html/body/table/tbody/tr/td[1]/a[1]').click()
sleep(3)
self.driver.switch_to.default_content() #焦点回到主文档
sleep(3)
second = self.driver.find_element_by_xpath('/html/frameset/frame[2]')
sleep(2)
self.driver.switch_to.frame(second)
self.driver.find_element_by_xpath('/html/body/table/tbody/tr/td[1]/a[1]').click()
sleep(2)
self.driver.quit()
PyAutoGUI是一个图形用户界面自动化工具,通过屏幕xy坐标系统确认目标位置,控制鼠标和键盘发送虚拟击键和鼠标点击,完成点击按钮、填写表单等操作。常用在常规方法定位不到元素的情况。
需要安装PyAutoGUI插件
# | 方法 | 方法描述 |
---|---|---|
1 | pyautogui.position() | 确认鼠标当前位置 |
2 | pyautogui.moveTo(x=None, y=None[, duration=t]) | 移动 |
3 | pyautogui.mouseDown()、pyautogui.mouseUp()、pyautogui.click()、pyautogui.doubleClick()、pyautogui.rightClick()、pyautogui.middleClick() | 点击 |
4 | pyautogui.dragTo(x=None, y=None[, duration=t]) | 拖动 |
5 | pyautogui.typewrite(x=None, y=None[, duration=t]) | 控制键盘 |
element = self.driver.find_element_by_id('agree')
rect = element.rect #元素所在位置
pyautogui.click(rect['x']+10,rect['y']+130) #点击位置内容
元素位置返回的是一个字典类型
方法 | 描述 |
---|---|
self.driver.maximize_window() | 窗口放大全屏命令 |