参考 https://ceshiren.com/t/topic/28709
冻结窗口 针对一闪而过的窗口定位
一、通用定位
在最新的selenium版本中已经舍弃了driver.find_element_by_定位方式(定位元素)
系列的定位方法,只支持driver.find_element(By.定位方式, 定位元素)
定位方法。
如果结果有多个,find_element()找的是第一个,find_elements()返回的是列表
基础的定位方式: id 、name、 class 、tag_name(标签名) 、link_text(超链接文本)、partial_link_text(超链接文本)
如下还有其他方式:
1.CSS定位
https://ceshiren.com/t/topic/28475
CSS Cascading Style Sheet 层叠样式表
作用: 给html网页设置外观或者样式(HTML网页中文字的大小、颜色、字体,网页的背景颜色、背景图片等等)
开发者工具里 $(‘css公式’)
1.标签
$(‘input’)——input标签直接放到公式里
2.属性
css公式: “[type=‘text’]”
特殊属性
- id —— css公式: “#kw”
- class ——css公式: “.sp”
3.属性+属性
例子:
前端代码
<div id='toolbarText' class='configToolbar'>
定位表达式
# id + class
#configToolbar.toolbarText
4.标签+属性
标签与class组合 “input.s_ipt”
标签与id组合 “input#kw”
标签与其他属性组合 “input[type=‘text’]”
5.标签+属性+属性:
css公式: ‘input.el-input__inner[name=“username”]’
说明:class=‘el-input__inner’,name=“username” , class和name同等级的(在一个标签里)
6.一个class里有空格隔开的值
class=‘tabs-title tabs-closable’,相当于class有两个值tabs-title 、tabs-closable(即为都可以表示为‘.tabs-title’、‘.tabs-closable’),使用一个可能定位不到,所以用两个,那么表示如下:
css公式: ‘.tabs-title.tabs-closable’ ——直接挨着写表示位置等同
7.索引
注意:索引从1开始
- nth-child(n) :定位子元素,第几个小孩
例子 :
“select#nr>option:nth-child(1)” 如果父类下孩子类型不一样,那么还要使用nth-child(n),最好>后不要指定孩子标签类型
“select#nr>:nth-child(1)”
nth-of-type(n):如果父类下的孩子类型不一样,可以使用,即为选父类下孩子为冒号前的标签的第n个孩子,span.tabs-inner>span:nth-of-type(1)
8.层级 上下级关系:>
9.逻辑运算
同时匹配两个属性,这里跟xpath不一样,无需写and关键字 “input[id=‘kw’][name=‘wd’]”
2.XPATH定位
https://ceshiren.com/t/topic/28728
XML Path Language(XPath 最初设计是用来搜寻XML文档的,但是它同样适用于 HTML 文档的搜索)——XML路径语言
2.1XPATH语法
1.常用表达式
表达式 | 描述 | 说明 |
---|---|---|
nodename(标签名) | 选取此节点的所有子节点 | |
/ | 从根节点开始选取 | 绝对定位 |
// | 从符合条件的元素开始,而不考虑他们的位置 | 相对定位 |
. |
选取当前节点 | |
.. |
选取当前节点的父节点 | 有的子孙很容易找到时,使用`…``往上就能轻易找到上面的父辈节点 |
* | 通配符,匹配任意元素节点 |
//* 获取页面的所有节点,所以在定位的时候进行在//* 后再加过滤条件就能轻易找到元素 |
@ | 匹配任何属性节点 | 通常配合谓语表达式[] 使用,如果条件是属性等于值,就用[@属性名='值']
|
说明: 绝对定位以/开头,依赖页面的元素的顺序和位置,相对定位以//开头,不依赖页面元素顺序和位置,根据条件进行匹配,优先使用相对定位。
- 可维护性更强,绝对定位的路径层级太多,但凡路径中间标签稍微改动一下路径就会出问题;
- 语法更加简洁;
- 相比于css可以支持更多的方式;
(1)属性 id name class 等 例子 ‘//*[@id=“idname”]’
(2)标签 例子 ‘//input[@id=“idname”]’
(3)层级
‘//form[@id=“idname”]/span/input’
/表示父子关系,示例 父/子
父子层级都可以使用属性值定位
//div[@id=“nullContent”]/div[@class=“Business_tab_title ng-isolate-scope”]
(4)索引
兄弟标签,无法用层级定位
索引从1开始
‘//select[@id=“idname”]/option[1]’
(5)模糊匹配
“//[contains(text(),‘hao123’)]"
"//[contains(@id,‘kw’)]”
#以什么开头
“//*starts-with(@id,‘s_kw’)”
#以什么结尾
“//end-with(@id,‘kw_wrap’)"
#支持正则表达式
"//[matchs(text(),‘hao123’)]”
(6)运算逻辑 and or not 例子 ‘//*[@id=“kw” and @aut=“xx”]’
- 逻辑与:and,当一个元素无法定位到唯一值得时候,我们可以使用and表达式,同一个标签下多个属性精确定位;
- 逻辑或:or,元素满足其中一个条件的时候,就可以定位到,如下,在百度搜索框中,故意将@class='s_t’元素写错,内容中并没有这个元素,但是还是找到了;
- 逻辑非:!=,想要查找元素值不等于某个值的内容,可以使用=方法,但是这个方法在工作中用的比较少;
2.总结:
(1)由孩子找上级父类/爷爷 : /…
属性 [@key=‘value’]
$x(‘//*[@class=“box-name”]/…/…’)
(2)text()
$x("//div[text()='客服转接']")
$x("//span[contains(text(),'客服转接')]")
(3)索引获取(第几个孩子)
$x(‘//[@class=“box-name”]/…/div[1]‘)
box-name的上级的下级的第一个标签div
(4)最后一个孩子[last()]
$x(’//[@class=“box-name”]/…/div[last()]’)
(5)与 或
$x(‘//*[contains(@class,“tabs-title”) or contains(@class,“tabs-closable”)]’)
2.2 验证表达式
- 浏览器-elements:按键“Ctrl+F”,输入XPath路径在元素页面进行搜索,搜索到的元素会有的颜色标识;
- 浏览器-console:使用jQuery的xpath语法,
$x("xpath表达式")
3.js定位
3.1 常用js脚本
https://ceshiren.com/t/topic/28506
1.通过css查找/操作元素
- 查询 document.querySelector(‘xx’)
- 获取元素文本信息 document.querySelector(‘xx’).innerText
- 点击 document.querySelector(‘xx’).click()
- 修改属性值 document.querySelector(‘xx’).value(‘xx’)
- 修改元素的类属性 document.querySelector(“#J_SiteNavMytaobao”).className()
// 百度首页:https://www.baidu.com/
// 修改属性值--input标签对应的值(对应send_keys)
document.querySelector("#kw").value = "霍格沃兹测试学院"
// 点击操作
document.querySelector("#su").click()
// 修改属性值后再点击
document.querySelector("#kw").value="霍格沃兹";document.querySelector("#su").click();
// 淘宝首页: https://www.taobao.com/
// 修改元素的类属性
document.querySelector("#J_SiteNavMytaobao").className\
="site-nav-menu site-nav-mytaobao site-nav-multi-menu J_MultiMenu site-nav-menu-hover"
// 测试人首页:https://ceshiren.com/
// 获取元素内的文本信息
document.querySelector("#ember63").innerText
2.Js脚本滚动操作
(1)页面滚动到底部:document.documentElement.scrollTop=10000;
(2)页面滚动到指定元素位置:document.querySelector(‘css表达式’).scrollIntoView();
- 先使用css选择器定位到元素,然后再滚动到指定元素;
3.2 js脚本使用/执行
(1)先在浏览器开发者工具的consol中调试js代码,调通达到预期结果之后再用selenium的api去执行js代码;
(2)调用selenium的api去执行js代码;
- 执行普通js代码:driver.execute_script(“js 代码”)
- 执行带返回值的js代码:driver.execute_script(“return js脚本”)
二、控件交互
1.普通控件交互
场景 | api | 说明 |
---|---|---|
点击 | WebElement.click() | |
输入 | WebElement.send_keys(str) | |
清空 | WebElement.clear() | |
获取标签属性 | WebElement.get_attribute(“属性名称”) | |
获取标签文本信息 | WebElement.text | |
获取标签名 | WebElement.tag_name |
2.高级控件交互
https://ceshiren.com/t/topic/28739
三、特殊场景定位
1.切换frame/iframe
参考
frame定义:frame是html中的框架,在html中,所谓的框架就是可以在同⼀个浏览器中显⽰不⽌⼀个页⾯。
(1)frame切换方式
三种方式:id name WebElement
webDriver.switchTo().frame(id/name/WebElement)
说明:
- 如果使用id或者name,那么参数传递的时候是以字符串传递
# 如下是使用id/name切换frame,参数是字符串
self.driver.switch_to.frame('appMainFrame')
- 如果是WebElement,要为先find_element定位到frame,然后把这个结果对象作为参数切换(如1中的例子)
# frame标签定位
eidtCustomerTelForm_frame = self.driver.find_element(By.ID,'eidtCustomerTelForm')
# frame切换
self.driver.switch_to.frame(eidtCustomerTelForm_frame)
(2)特殊切换frame情况
- webDriver.switchTo().parentFrame():切换到父框架
- webDriver.switchTo().defaultContent():切回到最外层
1.切换到最外层的frame(释放frame)
self.driver.switch_to.default_content()
2.文件上传
-
input标签的文件上传: 可以直接使⽤element.send_keys(⽂件地址)上传⽂件;
https://ceshiren.com/t/topic/26869
element = driver.find_element_by_id('upload-input')
element.send_keys('/path/to/example.txt')
-
非input型上传:
-
- autoIT,借助外力,我们去调用其生成的au3或exe文件。
-
- Python pywin32库,识别对话框句柄,进而操作。
-
- SendKeys库–不稳定,不推荐。
-
- keybd_event,跟3类似,不过是模拟按键,ctrl+a,ctrl+c, ctrl+v…。
-
3.切换窗口
场景:一个浏览器同时打开多个窗口或者选项卡,需要跳转到新窗口进行后续操作
在计算机中一个浏览器窗口都是有一个句柄(即为浏览器窗口的id值),而Selenium提供了switchTo().window(windowHandle(句柄))用来处理新的窗口或标签页。
# 方式一
# 获取当前句柄
self.driver.current_window_handle
# 切换窗口
driver.switch_to.window(window_name)
#方式二
# 获取所有窗口句柄列表,一般-1是新窗口
windows = self.driver.window_handles
# 切换窗口
self.driver.switch_to.window(windows[-1])
4.弹框处理
参考
注意:弹框处理要获取弹框里的提示信息,那么需要在alter.accept()之前,否则就获取不到提示信息了。
4.1弹框类型
- div模态框:使用xpath、css等定位方法正常定位;
- 系统弹框:
- Alert弹窗:只有信息及确认按钮
- Confirm弹窗:在Alert弹窗基础上增加了取消按钮
- Prompt类型弹框:在Confirm的基础上增加了可输入文本内容的功能
- 通过js代码动态生成的div弹框:这种弹框基本都是点击js代码监听到点击时间弹出的提示信息,1-2s之后会自动消失,类似于Android中的toast,这种正常定位即可;
4.2系统弹框的处理方式
- driver.switch_to.alert:切换到弹框并且获取到弹框对象
- alert.text:获取弹框中的文案;
- alert.accept():接受提示框,相当于点击确定;
- alert.dismiss():不接收提示框,相当于点击取消;
- alert.send_keys():给提示框输入内容;
"""Alert弹窗获取文本与确认操作"""
driver.get("http://sahitest.com/demo/alertTest.htm")
driver.find_element_by_name("b1").click()
#添加显示等待,等待弹框的出现
WebDriverWait(driver, 5, 0.5).until(EC.alert_is_present())
#切换到弹框
alert = driver.switch_to.alert
#打印弹框的文本
print(alert.text)
#点击确定
alert.accept()
#不点击确定,解除弹框
alert.dismiss()
5.日期控件
场景:
-
<input>
标签组合的下拉框无法定位 -
<input>
标签组合的日期控件无法定位
解决: 面对这些元素,我们可以引入 JS 注入技术来解决问题
针对readonly情况处理 :
https://ceshiren.com/t/topic/1244/2
需要先去除read-only属性
js = 'document.getElementById("cardNo").removeAttribute("readonly")'
driver.execute_script(js)
然后再尝试input 或 click操作
6.select下拉框
方法1: 二次定位
先定位select下拉框,再定位select里的选项
s = self.driver.find_element(by.ID,'nr')
s.find_element(by.ID,'xx').click()
方法2: 直接定位
自己写xpath定位或者css,一次性直接定位到option上的内容。
方法3: Select 模块 https://ceshiren.com/t/topic/22522
(index):selenium还提供了更高级的玩法,导入Select模块。直接根据属性或索引定位。
from selenium.webdriver.support.select import Select
s = self.driver.find_element(by.ID,'nr')
Select(s).select_by_index(2) # 根据索引选
Select(s).select_by_value('o1') # 根据value属性选
Select(s).select_by_visible_text('o1') # 根据文本属性选
Select(s).deselect_all() # 多选时,取消所有选中
Select(s).deselect_by_index(0) # 多选时,根据索引取消选中
Select(s).deselect_by_value('o1') # 多选时,根据value属性取消选中
Select(s).select_by_visible_text('o1') # 多选时,根据文本属性取消选中
7.单选框、复选框
业界的标准:
单选框是圆的 ——单选-radio
复选框是方的 ——多选-checkbox
全部勾选-方式:
(1)定位一组元素
(2)find_elements可以先获取到所有复选框,但是是不能直接点击的,可以通过for循环去一个个点击
(3)判断是否选中:is_selected()
场景:有的时候选项框本身就是选中状态,再去点击一次,变成了未选中状态,这样不是期待的结果,此时需要判断选项框是否选中状态。
is_selected(),返回结果是bool类型,未选中返回false,选中返回true
# 定位到复选框元素
checkbox = driver.find_element(By.CSS_SELECTOR, "#myCheckbox") # 假设复选框有一个ID为myCheckbox
# 检查复选框是否已选中
if not checkbox.is_selected():
# 如果未选中,则选中复选框
checkbox.click()
else:
print("复选框已经是选中状态")
三、 浏览器控制
操作 | api | 说明 |
---|---|---|
打开浏览器 | driver.get(url) | 通过webdriver驱动浏览器打开一个网址 |
刷新driver驱动的网址窗口 | driver.refresh() | 注意:是get打开的网址所在的窗口,不一定是肉眼看到的页面窗口 |
回退到上一个driver驱动的网址 | driver.back() | 注意:是get打开的网址所在的窗口回退到上一个地址,不一定是肉眼看到的页面窗口 |
浏览器窗口最大化 | driver.maximize_window() | |
浏览器窗口最小化 | driver.minimize_window() | |
关闭driver驱动的网址页面 | driver.close() | 注意:是关闭get打开的网址页面,不一定是肉眼看到的页面窗口 |
关闭整个浏览器进程 | driver.quit() | 如果不调用quit方法杀掉浏览器进程,电脑CPU会炸 |
打开新窗口/页面 | driver.execute_script(js) | selenium没有打开新窗口/页面的方法,需要执行js脚本打开新窗口/页面 |
新窗口打开
from time import sleep
from selenium import webdriver
# 创建驱动对象
driver = webdriver.Edge()
# 打开网页--百度
driver.get("https://www.baidu.com")
sleep(3)
# 打开一个新页面--搜狗
# js = 'window.open("https://www.sogou.com", "_blank", "resizable,scrollbars,status");' # 这个是打开新窗口
js = 'window.open("https://www.sogou.com");'# 这个是打开新页面
driver.execute_script(js)
四、常见的报错
五、常问的问题
1.相对定位和绝对定位的区别?
- 绝对定位:从很远的标签开始一级一级的往下下找,从浏览器copy过来的就是绝对定位方式;
- 相对定位:通过几个css选择器的相对 关系 进行定位,比如 相邻兄弟(+)、兄弟(~)、父子(>)、后代(空格) 相对关系;
- 相对定位的好处:
- 可维护性更强
- 语法更加简洁
- 解决各种复杂的定位场景