UI自动化定位方式记录

参考 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.文件上传

element = driver.find_element_by_id('upload-input')
element.send_keys('/path/to/example.txt')
  • 非input型上传
      1. autoIT,借助外力,我们去调用其生成的au3或exe文件。
      1. Python pywin32库,识别对话框句柄,进而操作。
      1. SendKeys库–不稳定,不推荐。
      1. 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)

四、常见的报错


no such elements 定位原因

五、常问的问题


1.相对定位和绝对定位的区别?

  • 绝对定位:从很远的标签开始一级一级的往下下找,从浏览器copy过来的就是绝对定位方式;
  • 相对定位:通过几个css选择器的相对 关系 进行定位,比如 相邻兄弟(+)、兄弟(~)、父子(>)、后代(空格) 相对关系;
  • 相对定位的好处:
    • 可维护性更强
    • 语法更加简洁
    • 解决各种复杂的定位场景