APPium自动化测试[详细使用]
用途和特点
- 自动化完成一些重复性的任务,比如微信客服机器人
- 爬虫自动化爬取信息,为什么不通过网页、HTTP爬取呢?有的系统产品没有网页端PC端只有app端
- 自动化测试,便于测试人员回归部署验证等测试
Appium自动化方案的特点
那么多自动化app的工具为什么选则appium?
- 开源免费
- 支持多个平台(android、IOS)
- 支持多种类型自动化:支持苹果安卓原生界面的自动化,支持应用内嵌WebView的自动化支持手机浏览器中的web网站自动化支持flutter应用的自动化
- 支持多种编程语言
自动化原理
android版
image.png
IOS版
image.png
appium工作原理
image.png
大家看看这幅图, 包含了 3个主体部分 : 自动化程序、Appium Server、移动设备
自动化程序
自动化程序是由我们来开发的,实现具体的 手机自动化 功能。
要发出具体的指令控制手机,也需要使用 客户端库。
和Selenium一样,Appium 组织 也提供了多种编程语言的客户端库,包括 java,python,js, ruby等,方便不同编程语言的开发者使用。
我们需要安装好客户端库,调用这些库,就可以发出自动化指令给手机。
Appium Server
Appium Server 是 Appium 组织开发的程序,它负责管理手机自动化环境,并且转发 自动化程序的控制指令 给 手机,并且转发 手机给 自动化程序的响应消息。
手机设备
我们这里说的手机设备,其实不仅仅是手机,包括所有 苹果、安卓的移动设备,比如:手机、平板、智能手表等。
为了直观方便的讲解,这里我们简称: 手机
当然手机上也包含了 我们要自动化控制的 手机应用APP。
手机设备为什么能 接收并且处理自动化指令呢?
因为,Appium Server 会在手机上 安装一个 自动化代理程序, 代理程序会等待自动化指令,并且执行自动化指令
比如:要模拟用户点击界面按钮,Appium 自动化系统的流程是这样的:
自动化程序 调用客户端库相应的函数, 发送 点击元素 的指令(封装在HTTP消息里)给 Appium Server
Appium Server 再转发这个指令给 手机上的自动化代理
手机上的自动化代理 接收到 指令后,调用手机平台的自动化库,执行点击操作,返回点击成功的结果给 Appium Server
Appium Server 转发给 自动化程序
自动化程序了解到本操作成功后,继续后面的自动化流程
其中,自动化代理控制,使用的什么库来实现自动化的呢?
如果测试的是苹果手机, 用的是苹果的 XCUITest 框架 (IOS9.3版本以后)
如果测试的是安卓手机,用的是安卓的 UIAutomator 框架 (Android4.2以后)
这些自动化框架提供了在手机设备上运行的库,可以让程序调用这些库,像人一样自动化操控设备和APP,比如:点击、滑动,模拟各种按键消息等。
环境搭建
安装包在为了方便我放在阿里云盘上面了是地址:https://sharelinkpre.rongdasoft.com/share-link/index.html?q=158e744d8a404000
步骤如下
- 安装client编程库
Python语言开发则使用pip install appium-python-client
使用java语言开发则使用:导jar包方式进行也可以使用maven进行下载
<!-- https://mvnrepository.com/artifact/io.appium/java-client -->
<dependency>
<groupId>io.appium</groupId>
<artifactId>java-client</artifactId>
<version>6.1.0</version>
</dependency>
-
安装appium Server
appium Server是用nodejs运行的,基于js开发出来的.Appium组织为了方便大家安装使用制作了一个可执行程序appiumDesktoop
把nodejs运行环境,appiumServer和一些工具打包在了里面了,只需要简单的下载安装就可以了
「Appium-windows-1.15.1.exe」https://www.aliyundrive.com/s/vqSgSGpw78y -
安装AndroidJDK
百度androidSDK进行下载,下载完毕后配置ANDROID_HOME,配置adb命令monkey命令等地址即可 -
安装javaSDK
需要用到androidSDK而androidSDK需要用到JAVA的JDK环境.所以在这里也需要安装一下java的JDK
访问地址:配置JAVA_HOME和bin和jre\bin即可:「jdk-8u211-windows-x64.exe」https://www.aliyundrive.com/s/FSMV3bbxCvd -
连接手机/模拟器
上述环境都准备好,要自动化手机app需要,在你运行程序的电脑上用USB线或者wifi连接上你的安卓手机,进入手机设置->关于手机,不断点击版本号菜单,推出上级菜单,就会出现一个开发者模式设置按钮进入后启动USB调试即可
连接好以后输入adb devices -l 命令来列出连接在电脑上的安卓设备.,点击回车如果输出一下内容则表示连接成功可以进行自动化了
注意:输入这个可以获取当前正在运行app的报名和activity: adb shell dumpsys window | findstr mCurrentFocus
快速入门
from appium import webdriver
from selenium.webdriver.common.by import By
from appium.webdriver.extensions.android.nativekey import AndroidKey
desired_caps = {
'platformName': 'Android', # 被测手机是安卓
'platformVersion': '8', # 手机安卓版本
'deviceName': 'xxx', # 设备名,安卓手机可以随意填写
'appPackage': 'tv.danmaku.bili', # 启动APP Package名称
'appActivity': '.ui.splash.SplashActivity', # 启动Activity名称
'unicodeKeyboard': True, # 使用自带输入法,输入中文时填True
'resetKeyboard': True, # 执行完程序恢复原来输入法
'noReset': True, # 不要重置App
'newCommandTimeout': 6000,
'automationName' : 'UiAutomator2'
# 'app': r'd:\apk\bili.apk',
}
# 连接Appium Server,初始化自动化环境
driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
# 设置缺省等待时间
driver.implicitly_wait(5)
# 如果有`青少年保护`界面,点击`我知道了`
iknow = driver.find_elements(By.ID, "text3")
if iknow:
iknow.click()
# 根据id定位搜索位置框,点击
driver.find_element(By.ID, 'expand_search').click()
# 根据id定位搜索输入框,点击
sbox = driver.find_element(By.ID, 'search_src_text')
sbox.send_keys('开心的')
# 输入回车键,确定搜索
driver.press_keycode(AndroidKey.ENTER)
# 选择(定位)所有视频标题
eles = driver.find_elements(By.ID, 'title')
for ele in eles:
# 打印标题
print(ele.text)
input('**** Press to quit..')
driver.quit()
appium2的find_element写法
注意:Appium Python 现在已经升级到 2.x 大版本,依赖 Selenium 4 以后, 下面这种 find_element_by* 方法都作为过期不赞成的写法
driver.find_element_by_id('username').send_keys('byhy')
运行会有告警,都要写成下面这种格式
from selenium.webdriver.common.by import By
wd.find_element(By.ID, 'username').send_keys('byhy')
而后续还有 Appium独有的查找方式,比如
driver.find_element_by_accessibility_id('byhy')
driver.find_element_by_android_uiautomator(code)
要改成
from appium.webdriver.common.appiumby import AppiumBy
driver.find_element(AppiumBy.ACCESSIBILITY_ID, 'byhy')
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, code)
# 这样也可以根据ID
wd.find_element(AppiumBy.ID, 'username').send_keys('byhy')
定位元素
从示例代码,大家就可以发现,和Selenium Web自动化一样,要操作界面元素,必须先 定位(选择)元素。
Appium是基于Selenium的,所以 和 Selenium 代码 定位元素的 基本规则相同:
- find_element_by_XXX 方法,返回符合条件的第一个元素,找不到抛出异常
- find_elements_by_XXX 方法,返回符合条件的所有元素的列表,找不到返回空列表
- 通过 WebDriver 对象调用这样的方法,查找范围是整个界面
- 通过 WebElement 对象调用这样的方法,查找范围是该节点的子节点
界面元素查看工具
做 Selenium Web 自动化的时候,要找到元素,我们是通过浏览器的开发者工具栏来查看元素的特性,根据这些特性(属性和位置),来定位元素
Appium 要自动化手机应用,同样需要工具查看界面元素的特征。
常用的查看工具是: Android Sdk包中的 uiautomateviewer 和 Appium Desktop 中的 Appium Inspector
- uiautomateviewer:安卓查看APP界面元素,最常用的就是 Android SDK 中的工具 uiautomateviewer ,它在SDK目录目录 的 tools\bin 目录中
和Selenium一样,我们要定位选择元素,也是根据元素的特征,包括
元素的属性
元素的相对位置(相对父元素、兄弟元素等) - Appium Inspector:Appium Desktop 中的 Appium Inspector 也可以查看元素。它的一个优点是可以直接验证 选择表达式是否能定位到元素
- 根据ID
在Selenium Web自动化教程里,我们说过,如果能根据ID选择定位元素,最好根据ID,因为通常来说ID是唯一的,所以根据ID选择 效率高。
在安卓应用自动化的时候,同样可以根据ID查找。
但是这个ID ,是安卓应用元素的 resource-id 属性:
如:resource-id com.phi.letter.letterphi:id/data_title
其中data_title就是该元素的ID
from appium.webdriver.common.appiumby import AppiumBy
eles = driver.find_elements(AppiumBy.ID, 'data_title')
- 根据CLASS NAME
安卓界面元素的 class属性 其实就是根据元素的类型,类似web里面的tagname, 所以通常不是唯一的。
通常,我们根据class 属性来选择元素, 是要选择多个而不是一个。
当然如果你确定 要查找的 界面元素的类型 在当前界面中只有一个,就可以根据class 来唯一选择。
使用如下代码
from appium.webdriver.common.appiumby import AppiumBy
driver.find_elements(AppiumBy.CLASS_NAME,'android.widget.TextView')[3].click()
- 根据ACCESSIBILITY ID
元素的 content-desc 属性是用来描述该元素的作用的。
如果要查询的界面元素有 content-desc属性,我们可以通过它来定位选择元素。
使用如下代码
from appium.webdriver.common.appiumby import AppiumBy
driver.find_element(AppiumBy.ACCESSIBILITY_ID, '找人')
- Xpath
Appium 也支持通过 Xpath选择元素。
但是其可靠性和性能不如 Selenium Web自动化。因为Web自动化对Xpath的支持是由浏览器实现的,而Appium Xpath的支持是 Appium Server实现的。
毕竟,浏览器产品的成熟度比Appium要高很多。
当然,Xpath是标准语法,所以这里表达式的语法规则和 以前学习的Selenium里面Xpath的语法是一样的,比如
from appium.webdriver.common.appiumby import AppiumBy
driver.find_element(AppiumBy.XPATH, '//ele1/ele2[@attr="value"]')
注意:
selenium自动化中, xpath表达式中每个节点名是html的tagname。
但是在appium中, xpath表达式中 每个节点名 是元素的class属性值。
比如:要选择所有的文本节点,就使用如下代码
# 定位text
driver.find_element_by_xpath("//*[@text='扫一扫']").click()
# 定位 resource-id
driver.find_element_by_xpath("//*[@resource-id='com.taobao.taobao:id/tv_scan_text']").click()
# 也可以联合@resource-id属性和@text文本属性来下定位
driver.find_element_by_xpth("//*[@resource-id='com.taobao.taobao:id/tv_scan_text'][@text='扫一扫']").click()
# 定位搜索框 //class属性
driver.find_element_by_xpath("//android.widget.EditText").click()
# 定位搜索框 //*[@class='class属性']
driver.find_element_by_xpath("//*[@class='android.widget.EditText']").click()
dd=driver.find_element(AppiumBy.XPATH,'//androidx.recyclerview.widget.RecyclerView/android.view.ViewGroup[1]/android.widget.TextView[1]')# xpath是从1开始的
print(dd.text)
contains模糊定位
# contains匹配text
driver.find_element_by_xpath('//*[contains(@text, "注册/登录")]').click()
time.sleep(3)
# contains匹配textcontent-desc
driver.find_element_by_xpath("//*[contains(@content-desc, '帮助')]").click()
安卓UIAutomator
根据id,classname, accessibilityid,xpath,这些方法选择元素,其实底层都是利用了安卓 uiautomator框架的API功能实现的。
参考 这里的谷歌安卓官方文档介绍:https://developer.android.google.cn/training/testing/ui-automator
也就是说,程序的这些定位请求,被Appium server转发给手机自动化代理程序,就转化为为uiautomator里面相应的定位函数调用。
其实,我们的自动化程序,可以直接告诉 手机上的自动化代理程序,让它 调用UI Automator API的java代码,实现最为直接的自动化控制。
主要是通过 UiSelector 这个类里面的方法实现元素定位的,比如
code = 'new UiSelector().text("业务").className("android.widget.TextView")'
ele=driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, code)
ele.click()
就是通过 text 属性 和 className的属性 两个条件 来定位元素
UiSelector里面有些元素选择的方法 可以解决 前面解决不了的问题。
比如
text 方法
可以根据元素的文本属性查找元素
textContains
根据文本包含什么字符串
textStartsWith
根据文本以什么字符串开头
textmartch 方法
可以使用正则表达式 选择一些元素,如下
code = 'new UiSelector().textMatches("^我的.*")'
UiSelector 的 instance 和 index 也可以用来定位元素,都是从0开始计数, 他们的区别:
instance是匹配的结果所有元素里面 的第几个元素
index则是其父元素的几个节点,类似xpath 里面的*[n]
UiSelector 的 childSelector 可以选择后代元素,比如
code = 'new UiSelector().resourceId("tv.danmaku.bili:id/recycler_view").childSelector(new UiSelector().className("android.widget.TextView"))'
ele = driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, code)
注意: childSelector后面的引号要框住整个 子 uiSelector 的表达式
目前有个bug:只能找到符合条件的第一个元素,参考appium 在github上的 issues:
https://github.com/appium/java-client/issues/150
界面操作
- click点击
最常见的操作之一,使用 WebElement 对象的 click 方法 - tap点按
WebElement 对象的 tap 方法和 click 类似,都是点击界面。
但是最大的区别是, tap是 针对坐标 而不是针对找到的元素。
为了保证自动化代码在所有分辨率的手机上都能正常执行,我们通常应该使用click方法。
但有的时候,我们难以用通常的方法定位元素, 可以用这个tap方法,根据坐标来点击
既然tap是用坐标来点击界面的,我们怎么知道这个元素的坐标呢?
大家还记得用inspect 查看该元素的属性中,有一个 bounds 属性吗?
它就是表示元素的左上角,右下角坐标的 坐标。
我们还可以使用 UIAutomatorviewer 直接光标移动,看右边的属性提示。
tap 方法可以像这样进行调用
driver.tap([(850,1080)],300)
它 有 两个参数:
第一个参数是个列表,表示点击的坐标。
注意最多可以有5个元素,代表5根手指点击5个坐标。所以是list类型。
如果我们只要模拟一根手指点击屏幕,list中只要一个元素就可以了
第二个参数 表示 tap点按屏幕后 停留的时间。
如果点按时间过长,就变成了长按操作了。 - 输入
使用 WebElement 对象的 send_keys 方法 - 滑动
我们做移动app测试的时候,经常需要滑动界面。
怎么模拟滑动呢? WebDriver对象的 swipe方法,就提供了这个功能
比如
driver.swipe(start_x=x, start_y=y1, end_x=x, end_y=y2, duration=800)
前面4个参数 是 滑动起点 和 终点 的x、y坐标。
第5个参数 duration是滑动从起点到终点坐标所耗费的时间。
注意这个时间非常重要,在屏幕上滑动同样的距离,如果时间设置的很短,就是快速的滑动。
比如:一个翻动新闻的界面,快速的滑动,就会是扫动的动作,会导致内容 惯性 滚动很多。 - 按键
前面的示例代码中已经使用过 调用 press_keycode 方法,就能模拟 按键动作,包括安卓手机的实体按键和 键盘按钮。
from appium.webdriver.extensions.android.nativekey import AndroidKey
# 输入回车键,确定搜索
driver.press_keycode(AndroidKey.ENTER)
按键的定义,可以参考这篇文档 https://github.com/appium/python-client/blob/master/appium/webdriver/extensions/android/nativekey.py
- 长按\双击\移动
Appium的 TouchAction 类提供了更多的手机操作方法,比如:长按、双击、移动
from appium.webdriver.common.touch_action import TouchAction
# ...
actions = TouchAction(driver)
actions.long_press(element)
actions.perform()
- 查看通知栏
打开通知栏
安卓手机, 查看通知栏的动作可以是从屏幕顶端下滑来查看通知。
我们刚刚学过滑动,感兴趣的朋友可以自己试试,关键是要找对滑动的起始点和滑动距离。
更方便的,我们可以使用如下代码,直接打开通知栏
driver.open_notifications()
或者使用滑动
driver.swipe(start_x=10, start_y=0, end_x=10, end_y=1000, duration=500)
收起通知栏
收起通知栏,可以使用前面介绍的模拟按键, 发出返回按键 的方法
driver.back()
driver.keyevent(4)
我们自动化过程中,可能需要截屏手机,并且下载到指定目录中,就可以在我们的Python程序中这样写
import os
os.system('adb shell screencap /sdcard/screen3.png && adb pull /sdcard/screen3.png')
特别是,还可以通过adb 使用 am(activity manager) 和pm (package manager) 两个工具, 可以启动 Activity、强行停止进程、广播 intent、修改设备屏幕属性、列出应用、卸载应用等。
关闭APP
使用 driver.terminate_app(appPackage) 即可
内嵌网页自动化
- 内嵌网页的混合App
很多移动App 都是 Hybrid(混合) 应用。
混合应用主要是指 它的一部分是原生界面和代码,而另一部分是内嵌网页 。
现在基本上需要打开网页浏览的app都是 混合app,比如微信、支付宝等。
微信的sms界面是原生代码实现的,而打开某个朋友圈,或者别人发来的的链接部分则是 web部分。
App中的内嵌的展示网页内容的模块,我们称之为 webview 。
我们自动化的时候如果需要操作内嵌webview中的网页内容,该怎么做呢? - 修改编译App
前面讲过,Appium 的原则是不修改应用本身,就可以对应用进行自动化。
但是,这里要违背一下 appium的原则。
要对App内嵌网页进行自动化,首先要请 开发人员修改源代码,保证对webview 对象加入setWebContentsDebuggingEnabled 的调用。
安卓应用,修改java代码,如下所示:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 找到 webview 对象
WebView myWebView = (WebView) findViewById(R.id.jcywebview);
// 添加打开 webview内容debug开关
myWebView.setWebContentsDebuggingEnabled(true);
};
然后,编译出一个支持自动化的版本。
否则无法对webview中的内容进行自动化操作。
- 用Chrome查看App内嵌网页
编译产生了 支持自动化 的App,接下来我们就可以安装到手机上,进行内嵌网页的自动化了。
App内嵌网页的自动化,也是使用 Selenium。 和电脑上自动化Chrome浏览器本质是一样的。
因为 安卓手机内嵌webview 基本上就是 手机版的 Chrome浏览器 。
但是,因为网页内容在手机中呈现,不能像电脑浏览器那样,打开开发者工具,查看元素内容。
那我们怎么查看元素的属性特征,来定位选择元素呢?
这里面要分两种情况:
-
webview不依赖App环境
这种情况:App的内嵌 webview 和 native部分没有交互 的。
webview 只是打开一个固定的网址(一般是该公司的手机网址)而已。
这种情况,我们要查看webview内容非常简单。
因为其实和手机没有关系。
直接用Chrome浏览器F12里面的手机模式打开对应的网页,即可。 -
webview依赖App环境
image.png
首先,确保我们的应用运行,然后应用访问webview页面,打开电脑浏览器地址栏输入 chrome://inspect ,出现如下所示界面
(括号中说的是webview版本号)
webview版本也比较老的,就会有问题, 尽量使用新手机进行自动化
点击 inspect即可查看
webview自动化代码 和 电脑上浏览器的自动化 基本差不多。
但是有一点要注意:
手机App中 webview里面的网页内容, 是在一个独立于应用native部分的环境里面的。
而缺省情况下,find_element_by_xxx 这样的代码选择元素, Appium 只会在 native 部分的界面寻找元素。 肯定找不到元素。
Appium 把一个界面环境 称之为一个 context 。
native 部分的 context 名字为 NATIVE_APP , 而webview部分的context则为 WEBVIEW_XXX (XXX部分是 应用的 app package名)
我们怎么查看当前有哪些context呢?
我们的代码通过 driver 对象的 contexts 属性来获取,也就是 driver.contexts。
driver 对象的 current_context 属性对应当前的 context 对象。
大家可以打开自动化代码, 添加如下内容
print(driver.contexts)
执行一下,解释一下,可以发现结果如下。
['NATIVE_APP', 'WEBVIEW_stetho_com.phi.letter.letterphi']
我们的应用中, webview 的 context 就是 WEBVIEW_stetho_com.phi.letter.letterphi
而当前的context 是’NATIVE_APP', 所以当前的自动操作都是在native context里面的进行的。
要对该webview里面的网页内容进行自动化操作,必须先将当前的context切换为 webview的context,怎么切呢?
使用 switch_to.context
driver.switch_to.context('WEBVIEW_stetho_com.phi.letter.letterphi')
使用该语句如果webview版本和chrome驱动版本不一致则会报版本错误问题此时我们需要查看报错的版本,下载对应的chrome版本驱动包即可
版本对应查看地址:https://raw.githubusercontent.com/appium/appium-chromedriver/master/config/mapping.json
chrome驱动包下载地址:http://chromedriver.storage.googleapis.com/index.html
先点击 Advanced 设置项
image.png
然后在 下图位置 写上你的 老版本的 chromedriver 的路径
image.png
好,现在我们修改代码,如下所示
from selenium.webdriver.common.by import By
driver.switch_to.context('WEBVIEW_com.example.jcy.wvtest')
driver.find_element(By.ID, 'index-kw').send_keys('开心的小哈')
driver.find_element(By.ID, 'index-bn').click()
执行一下,发现可以自动化了
那么怎么切换回 native app 进行自动化呢?
当然是 继续使用 switch_to.context ,如下
driver.switch_to.context('NATIVE_APP')
手机浏览器网页自动化
有的公司开发了手机版网站,直接用手机浏览器打开的,比如,xiaomi 京东等。
并不是 手机App
这种手机网页,我们怎么 程序自动化呢?
首先,必须在手机上安装谷歌浏览器。
以百度为例,
首先启动 Appium Desktop。
然后,我们的自动化程序代码如下所示:
from appium import webdriver
from selenium.webdriver.common.by import By
desired_caps = {}
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '7'
desired_caps['deviceName'] = 'test'
desired_caps['browserName'] = 'Chrome'
desired_caps['newCommandTimeout'] = 6000
driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
driver.implicitly_wait(10)
driver.get('http://www.baidu.com')
driver.find_element(By.ID, 'index-kw').send_keys('开心的小哈')
driver.find_element(By.ID, 'index-bn').click()
driver.quit()
更多了解:AndroidViewClient UiAutomator2
在使用过程中出现的问题
- 点击web页面元素时如果使用ID,NAME定位结果出现
raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.InvalidArgumentException: Message: invalid argument: invalid locator
(Session info: chrome=88.0.4324.93)
Stacktrace:
Backtrace:
GetHandleVerifier [0x000BB963+483]
GetHandleVerifier [0x000BB941+449]
GetHandleVerifier [0x00463308+3832712]
GetHandleVerifier [0x004863A7+3976231]
GetHandleVerifier [0x004A8822+4116642]
GetHandleVerifier [0x0049B43A+4062394]
GetHandleVerifier [0x004A7159+4110809]
GetHandleVerifier [0x0049B2EB+4062059]
GetHandleVerifier [0x0047ED14+3945876]
GetHandleVerifier [0x0047FBCE+3949646]
GetHandleVerifier [0x0047FB59+3949529]
Ordinal0 [0x0007B5CC+46540]
Ordinal0 [0x00079F53+40787]
Ordinal0 [0x00079B12+39698]
GetHandleVerifier [0x00381468+2907368]
GetHandleVerifier [0x001C71EE+1096302]
GetHandleVerifier [0x00183E8D+821005]
GetHandleVerifier [0x0018396B+819691]
GetHandleVerifier [0x00183881+819457]
GetHandleVerifier [0x001AF463+998627]
BaseThreadInitThunk [0x76B4FA29+25]
RtlGetAppContainerNamedObjectPath [0x77B57A7E+286]
RtlGetAppContainerNamedObjectPath [0x77B57A4E+238]
(No symbol) [0x00000000]
问题原因导致:如果 chromedriver 在 W3C 模式下工作,则会出现这种情况。W3C标准只声明CSS和XPATH定位器,其中id和名称的位置已被删除为过时,因为CSS涵盖了它们。
有三种可能的解决方法:
- 更新不受支持的定位器
- 将 chromedriver 强制执行 JSONWP 模式(将 chromedriver 选项设置为w3cfalse)
- 将过时的定位器类型传递给客户端代码中的转换方法,该方法会自动将它们升级到CSS定位器(这是Selenium lib当前正在做的事情)
其中第二个需要在开启时配置
desired_caps['chromeOptions'] = {'w3c': False}
desired_caps['showChromedriverLog'] = data['showChromedriverLog']
即可;