usb连接手机
DOS命令输入adb devices,查询连接设备的名称。例如:设备名称为238ap45
启动appiumDesktop
{
“platformName”: “Android”,
“deviceName”: “devices”,
“platformVersion”: “安卓版本”,
“appPackage”: “包名”,
“appActivity”: “活动页”,
“automationName”:“uiautomator1”
}
AppiumDesktop连接真机成功
输入adb devices,查询连接设备的名称。例如:设备名称为238ap45
开启端口(端口不能被占用),输入adb tcpip 5555
adb shell ip -f inet addr show wlan0: 查看手机内网ip
通过查询到的手机IP与设置的端口号,输入adb connect 192.168.x.xxx:5555
连接上后就可以拔掉USB线了,查看连接状态,输入adb devices
断开无线连接可执行命令:adb disconnect 192.168.21.100:5556
from appium import webdriver#导入模块
#手机的连接配置信息写入字典中
desired_caps={
"platformName": "Android",
"deviceName": "devices",
"platformVersion": "10",
"appPackage": "com.sina.weibo",
"appActivity": "com.sina.weibo.SplashActivity",
"noReset": "true",
"automationName": "uiautomator1"
}
#调用webdriver模块中的Remote方法启动APP
driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
Appium中可以使用find_element_by_id()来进行元素定位
定位的工具可以用sdk自带的uiautomatorviewer 和 Appium Desktop来进行定位
我现在用 Appium Desktop 来进行定位
打开appium Desktop工具 选择 Selected Element 点击要定位的点右侧就会出现id的元素
driver.find_element_by_id('com.shizhuang.duapp:id/iv_search').click()
by_classname.by
driver.find_element_by_class_name('android.widget.EditText').send_keys('aj')
但有时class属性值有可能有两个以上 重复的 那么就有只能定位第一个元素的情况所以有Id的化一般就不用class
先找到目标元素的父元素 然后再利用父元素去定位子元素
#下方代码解释: 定位父元素 将 父元素 放入变量id中然后 用id去定位子元素的class
id = driver.find_element_by_id('com.shizhuang.duapp:id/title_container')
id.find_element_by_class_name('android.widget.FrameLayout').click()
如果父元素下方有多个子元素且class相同那么同样只能定位第一个
/ | 从根节点选取。 |
---|---|
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 |
nodename | 选取此节点的所有子节点。 |
. | 选取当前节点。 |
… | 选取当前节点的父节点。 |
@ | 选取属性。 |
通配符 | 描述 |
---|---|
* | 匹配任何元素节点。 |
@* | 匹配任何属性节点。 |
node() | 匹配任何类型的节点。 |
driver.find_element_by_xpath('//*[@class="android.widget.FrameLayout" and @index="2"]').click() #and 可以实现多条件定位
driver.find_element_by_xpath('//android.widget.EditText[@text="请输入用户名"]').send_keys('zxw1234')
遇到id和class的属性值都是一样的 这个时候只能定位到第一个 要想获取其他的元素 必须用list定位
思路:
把id或class 集合起来赋值给变量中
用索引取值的方式把值取出来 做相应的操作
注意这里用的是find_elements 最后要加上 s
clas = driver.find_elements_by_class_name('android.widget.TextView')
clas[2].click()
UIAutomator元素定位是 Android 系统原生支持的定位方式,虽然与 xpath 类似,但比它更加好用,且支持元素全部属性定位.定位原理是通过android 自带的android uiautomator的类库去查找元素。 Appium元素定位方法其实也是基于Uiautomator来进行封装的。
使用方法 find_element_by_android_uiautomator() 可以运用UiAutomator元素定位。
id定位是根据元素的resource-id属性来进行定位,使用 UiSelector().resourceId()方法即可。
driver.find_element_by_id('com.shizhuang.duapp:id/rbtn_user').click()#点击我的
driver.find_element_by_android_uiautomator('new UiSelector().resourceId("com.shizhuang.duapp:id/icon")').click()#待付款
text定位就是根据元素的text属性值来进行定位,new UiSelector()
driver.find_element_by_android_uiautomator('new UiSelector().text("待付款")').click()
与Appium class定位方式一样,也是根据元素的class属性来进行定位。new UiSelector().className
driver.find_element_by_android_uiautomator('new UiSelector().className("android.widget.ImageView")').click()
在混合开发的App中,经常会有内嵌的H5页面。那么这些H5页面元素该如何进行定位操作呢
针对这种场景直接使用前面所讲的方法来进行定位是行不通的,因为前面的都是基于Andriod原生控件进行元素定位,而Web网页是单独的B/S架构**,两者的运行环境不同因此需要进行上下文(context)切换,**然后对H5页面元素进行定位操作
Context的中文翻译为:语境; 上下文; 背景; 环境,在开发中我们经常说“上下文”,那么这个“上下文”到底是指什么意思呢?
关于应用程序环境的全局信息的接口。 这是一个抽象类,其实现由Android系统提供。 它允许访问特定于应用程序的资源和类,以及对应用程序级操作的调用,如启动活动、广播和接收意图等。
在程序中context我们可以理解为当前对象在程序中所处的一个环境。 比如前面提到的App一个界面是属于Activity类型,也就是Android界面环境,但是当访问内嵌的网页是属于另外一个环境(网页环境),两者处于不同的一个环境。
driver.find_element_by_id('com.wondershare.drfone:id/btnBackup').click()
WebDriverWait(driver,8).until(lambda x:x.find_element_by_id('com.wondershare.drfone:id/btnRecoverData'))#等待元素出现8秒
print('click NextBtn')
driver.find_element_by_id('com.wondershare.drfone:id/btnRecoverData').click()#点击进入h5
contexts = driver.contexts #获取页面的contexts
print(contexts)
>>>['NATIVE_APP', 'WEBVIEW_com.wondershare.drfone']
#切换获取到的contexts页面
driver.switch_to_default_content('WEBVIEW_com.wondershare.drfone')
Chrome浏览器打开 chrome://inspect/#devices 页面 可以获取该H5的URL
#在H5页面中进行操作
driver.find_element_by_id('email').send_keys('[email protected]')
driver.find_element_by_xpath('//button[@class]').click()
#操作结束后 切换回安卓
driver.switch_to.context('NATIVE_APP')
#点击返回
driver.find_element_by_class_name('android.widget.ImageButton').click()
driver.find_element_by_id('com.wondershare.drfone:id/btnBackup').click()
WebDriverWait(driver,8).until(lambda x:x.find_element_by_id('com.wondershare.drfone:id/btnRecoverData'))#等待元素出现8秒
driver.find_element_by_id('com.wondershare.drfone:id/btnRecoverData').click()#点击进入h5
contexts = driver.contexts #获取页面的contexts
print(contexts)
#切换获取到的contexts页面
driver.switch_to.context('WEBVIEW_com.wondershare.drfone')
#在H5页面中进行操作
driver.find_element_by_id('email').send_keys('[email protected]')
driver.find_element_by_xpath('//button[@class]').click()
#操作结束后 切换回安卓contexts
driver.switch_to.context('NATIVE_APP')
#点击返回
driver.find_element_by_class_name('android.widget.ImageButton').click()
有些页面不一定每次都有 比如版本升级后对于新功能的介绍
这些页面元素 就需要处理一下 否则可能下次执行就会因为找不到元素报错
异常处理的方式解决
id = driver.find_element_by_id('com.shizhuang.duapp:id/iv_search')
try:
id
except EnvironmentError as e:
print("没有定位到元素")
else:
id.click()
有时候有的元素加载时间可能过长 所以要等待这个元素出现再进行下面的操作
设置固定的等待时间,使用sleep()方法即可实现
from time import sleep
#强制等待5秒
sleep(5)
隐式等待是针对全部元素设置的等待时间
driver.implicitly_wait(20)
显式等待是针对某个元素来设置的等待时间
webdriverwait:源码分析
from selenium.webdriver.support.ui import WebDriverWait
WebDriverWait(driver, timeout, poll_frequency=0.5, ignored_exceptions=None)
driver : WebDriver
timeout : 最长超时时间,默认以秒为单位
poll_frequency : 休眠时间的间隔时间,默认为0.5秒
ignored_exceptions : 超时后的异常信息,默认情况下抛NoSuchElementException异常。
from selenium.webdriver.support.ui import WebDriverWait
#等待3秒
WebDriverWait(driver,3).until(lambda x:x.find_element_by_id("com.shizhuang.duapp:id/iv_search"))
save_screenshot() 该方法直接保存当前屏幕截图到当前脚本所在文件位置。
driver.save_screenshot('login.png')
get_screenshot_as_file(self, filename) 将截图保留到指定文件路径
filename: 保存的路径和文件名
driver.get_screenshot_as_file('./img/login.png')
在Appium中模拟用户滑动操作需要使用swipe方法
def swipe(self, start_x, start_y, end_x, end_y, duration=None):
"""
从一个点滑动到另一个点,持续时间可选。
参数:
start_x (int):开始的x坐标
start_y (int):开始的y坐标
end_x (int):要停止的x坐标
end_y (int):停止的y坐标
duration:持续时间
"""
滑动主要分为:
滑动轨迹图如下:
使用AppiumDesktop获取坐标点 连接手机打开App后, 选择Swipe By Coordinates在左侧就能获得相应坐标 点的值 或者在手机开发者中开启指针位置
然后在调用 swipe方法
driver.swipe(422,1150,526,469)#前两个是起点坐标 后面是终点坐标 滑动时间默认为空可以不写 如果需要指定时间 直接写上就好 单位是毫秒
def get_size():
"""
:return: 获取手机屏幕尺寸
"""
x=driver.get_window_size()['width']
y=driver.get_window_size()['height']
return x,y
l = get_size()
print(l)
print("宽:{}".format(int(l[0])))
print("高度:{}".format(int(l[1])))
>>>(1080, 2030)
>>>宽:1080
>>>高度:2030
从下向上滑动
def get_size():
"""
:return: 获取手机屏幕尺寸
"""
x=driver.get_window_size()['width']
y=driver.get_window_size()['height']
return x,y
l = get_size()
print(l)
print("宽:{}".format(int(l[0])))
print("高度:{}".format(int(l[1])))
def swipeLeft():
l = get_size()
x1 = int(l[0]*0.5)#宽度在中间滑动乘0.5
y1 = int(l[1] * 0.9) # 高度在底部开始向上滑动 乘0.9
x2 = int(l[0]*0.5)#终点的宽度也在中间
y2 = int(l[1]* 0.1)#滑动到接近顶部的位置
driver.swipe(x1,y1,x2,y2)
swipeLeft()
滑动操作一般是两点之间的滑动,而实际使用过程中用户可能要进行一些多点连续滑动操作。如九宫格滑动操作,连续拖动图片移动等场景
Touch Action包含一些列操作,比如按压、长按、点击、移动、暂停。由着些不同操作可以组成一套动作。使用TochAction需要先导入对应的模块
from appium.webdriver.common.touch_action import TouchAction
方法:press() 开始按压一个元素或坐标点(x,y)。通过手指按压手机屏幕的某个位置。 press也可以接收屏幕的坐标(x,y)。
press(self, el=None, x=None, y=None)
TouchAction(driver).press(x=0,y=308)
方法:longPress() 开始按压一个元素或坐标点(x,y)。 相比press()方法,longPress()多了一个入参,既然长按,得有按的时间吧。duration以毫秒为单位。1000表示按一秒钟。其用法与press()方法相同。
long_press(self, el=None, x=None, y=None, duration=1000)
方法:tap() 对一个元素或控件执行点击操作。用法参考press()。
tap(self, element=None, x=None, y=None, count=1)
方法:move_to() 将指针从上一个点移动到指定的元素或点。注意:移动到目位置有时是算绝对坐标点,有时是基于前面一个坐标点的偏移量,(比如 x=50 就是横轴向右移动50个单位 x=-50就是向左移动50个单位)
move_to(self, el=None, x=None, y=None)
方法:Wait()暂停脚本的执行,单位为毫秒
wait(self, ms=0)
方法release() 结束的行动取消屏幕上的指针。
release(self)
perform() 执行的操作发送到服务器的命令操作。
perform(self)
在九宫格上画一个数字7的操作
press按压第一个点 然后move_to方法滑动到目标坐标点 最后release().perform()释放指针执行操作
from appium import webdriver
from run_app import driver
from selenium.webdriver.support.ui import WebDriverWait
import time as t
from appium.webdriver.common.touch_action import TouchAction
TouchAction(driver).press(x=307,y=719).wait(2000)\
.move_to(x=536,y=720).wait(2000)\
.move_to(x=552,y=1230).release().perform()
在使用地图App中,我们经常需要对界面进行缩放操作来更加便利的查看位置。那么在Appium中怎样去模拟这类操作呢?
MultiAction 是多点触控的类,可以模拟用户多点操作。主要包含 add() 和 perform() 两个方法, MultiAction可以结合前面所学的 ActionTouch可以模拟出用户的多个手指滑动的操作效果;
from appium.webdriver.common.multi_action import MultiAction
from appium.webdriver.common.touch_action import TouchAction
def add(self, *touch_actions):
"""
将TouchAction对象添加到MultiAction中,稍后执行。
参数:
touch_actions (' TouchAction '):一个或多个TouchAction对象,描述一个手指要执行的操作链
"""
a1 = TouchAction(driver)
a1.press(el1).move_to(el2).release()
a2 = TouchAction(driver)
a2.press(el2).move_to(el1).release()
MultiAction(driver).add(a1, a2)
百度地图APP演示
缩小地图操作
from run_app import driver
from selenium.webdriver.support.ui import WebDriverWait
from appium.webdriver.common.touch_action import TouchAction
from appium.webdriver.common.multi_action import MultiAction
WebDriverWait(driver,6).until(lambda x:x.find_element_by_id("com.baidu.BaiduMap:id/duhelper_flysaucer"))
x = driver.get_window_size()['width']
y = driver.get_window_size()['height']
def pinch():
"""缩小地图"""
"""定义两个手指动作"""
action1=TouchAction(driver)
action2=TouchAction(driver)
zoom_action=MultiAction(driver)
"""
action1:第一个手指滑动的操作
action2:第二个手指滑动的操作
zoom_action.add:将这两个手指操作添加到zoom_action.add方法里 然后perform执行
"""
action1.press(x=x*0.2,y=y*0.2).wait(1000).move_to(x=x*0.4,y=y*0.4).wait(1000).release()
action2.press(x=x*0.8,y=y*0.8).wait(1000).move_to(x=x*0.6,y=y*0.6).wait(1000).release()
zoom_action.add(action1, action2)
zoom_action.perform()
if __name__ == '__main__':
for i in range(3):
pinch()