启动过程
appium的启动实际上是在本机使用了4723端口开启了一个服务
1. 我们写的 python 代码会访问本机的 appium 服务器,并获取 driver 对象
2. appium 会将我们的 driver 对象调用的方法转化成 post 请求,提交给appium服务器
3. appium 通过接收到的 post 请求发送给手机,再由手机进行执行
# 导模块
from appium import webdriver
# 创建一个字典,包装相应的启动参数
desired_caps = dict()
# 需要连接的手机的平台(不限制大小写)
desired_caps['platformName'] = 'Android'
# 需要连接的手机的版本号(比如 5.2.1 的版本可以填写 5.2.1 或 5.2 或 5 ,以此类推) desired_caps['platformVersion'] = '5.1'
# 需要连接的手机的设备号(andoird平台下,可以随便写,但是不能不写)
desired_caps['deviceName'] = '192.168.56.101:5555'
# 需要启动的程序的包名
desired_caps['appPackage'] = 'com.android.settings'
# 需要启动的程序的界面名
desired_caps['appActivity'] = '.Settings'
# 连接appium服务器
driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
# 退出
driver.quit()
App基础操作API
import os
from app.util import encode_data, decode_data, get_driver
driver = get_driver()
"3、app是否已安装"
is_installed = driver.is_app_installed('com.example.corel.calc')
# eg:没安装安装,安装了就卸载
if is_installed:
"2、卸载手机自己安装的app,不能卸载系统的app"
driver.remove_app('com.example.corel.calc')
else:
"1、安装apk到手机,支持重复安装进行覆盖,绝对路径"
driver.install_app(os.getcwd() + os.sep + 'apk' + os.sep + 'com.example.corel.calc_2.1.1023_11.apk')
"""4、发送⽂件到⼿机,eg:将hello写入到手机
driver.push_file(path, data)
参数:
path:⼿机设备上的路径(例如:/sdcard/a.txt)
data:⽂件内数据,要求base64编码
Python3.x中字符都为unicode编码,⽽b64encode函数的参数为byte类型,需要先转码;⽣成的数据为byte类型,需要将byte转换回去。"""
data = encode_data('hello')
driver.push_file('/sdcard/abc.txt', data) # 不指定abc.txt,会生成一个.tmp结尾的临时文件,不便于后续的维护
"5、从手机拉取文件,返回的是base64编码数据"
data = driver.pull_file('/sdcard/abc.txt') # 返回数据为base64编码
re_data = decode_data(data)
print(re_data)
"6、获取 当前屏幕 元素结构,返回xml字符串,只是当前屏幕(activity页面内容是xml承载的)"
with open('./result/page.xml', 'w') as f:
f.write(driver.page_source)
driver.quit()
import base64
from appium import webdriver
# 输⼊⽂本内容到⼿机,中文输入支持:
# 1 使用Unicode键盘:'unicodeKeyboard': True
# 2 重置键盘:'resetKeyboard': True
def get_driver():
desired_caps = {'platformName': 'android',
'platformVersionName': '5.1',
'deviceName': '192.168.192.101:5555',
'appPackage': 'com.android.settings',
'appActivity': '.Settings',
'unicodeKeyboard': True}
return webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
# base64编码
def encode_data(value_arg):
return str(base64.b64encode(value_arg.encode('utf-8')), 'utf-8')
# base64解码
def decode_data(data):
return str(base64.b64decode(data), 'utf-8')
APP元素定位操作
name |
value |
id |
resource-id属性值 |
class |
class属性值 |
xpath |
xpath表达式 |
from time import sleep
from selenium.webdriver.support.wait import WebDriverWait
# ❀ 借助sdk家⽬录下的tools⽬录中的uiautomatorviewer.bat进行定位,而该shell脚本和appium共用同一个adb,
# 如果自动化脚本中因异常没有关闭adb服务,那此时进行快照截图的话是会报错的,解决办法:adb kill-server
# 三种定位使用优先级:
# 优先使用:id class
# 其次使用:xpath,需借助text、class、resource-id才可完成
# xpath语句:
# 1."//*[contains(@text,'text属性值')]"
# 2."//*[contains(@class,'class属性值')]"
# 3."//*[contains(@resource-id,'resource-id属性值')]"
from app.util import get_driver
driver = get_driver()
# 1、id获取搜索按钮并点击
driver.find_element_by_id('com.android.settings:id/search').click()
# 3.1 xpath + id组合 获取搜索按钮并点击
search_btn = '//*[contains(@resource-id, "com.android.settings:id/search")]'
driver.find_element_by_xpath(search_btn).click()
sleep(2)
# 2、class获取返回按钮并点击
driver.find_element_by_class_name('android.widget.ImageButton').click()
# 3.2 xpath + class组合 获取返回按钮并点击
search_return_btn = '//*[contains(@class, "android.widget.ImageButton")]'
driver.find_element_by_xpath(search_return_btn).click()
sleep(2)
# 3.3 xpath + text 获取返回按钮并点击
more_btn = '//*[contains(@text, "更")]'
driver.find_element_by_xpath(more_btn).click()
sleep(2)
# 定位一组元素id,class,xpath
ids_value = driver.find_elements_by_id('com.android.settings:id/title')
for i in ids_value:
print(i.text)
print('❀______')
classes_value = driver.find_elements_by_class_name('android.widget.TextView')
for i in classes_value:
print(i.text)
print('❀______')
xpath_value = '//*[contains(@text, "示")]'
xpaths_value = driver.find_elements_by_xpath(xpath_value)
for i in xpaths_value:
print(i.text)
# 隐式等待:是等到整个页面加载结束才进行后续操作
# 显示等待:是等待某一个具体的元素成功获取
driver.find_element_by_xpath('//*[contains(@text, "更多")]').click()
WebDriverWait(driver, 10).until(lambda x: x.find_element_by_xpath('//*[contains(@text, "飞行模式")]')).click()
driver.quit()
APP元素信息操作API
from app.util import get_driver
driver = get_driver()
search = driver.find_element_by_id('com.android.settings:id/search')
search.click()
search_input = driver.find_element_by_id('android:id/search_src_text')
search_input.send_keys('文')
search_input.clear()
search_input.send_keys('星魂')
# 获取元素id class text content-desc的属性值:
# get_attribute(value):
# value = name -> 返回content-desc或text
# value = text -> 返回text
# value = className -> 返回class,只有API>=18(Android4.3)才能支持
# value = resourceId -> 返回resource_id,只有API>=18才能支持
print('resource_id:', search.get_attribute('resourceId'))
print('class:', search.get_attribute('className'))
print('content_desc:', search.get_attribute('name'))
# 应用场景:只是想滑动轮播图,并不想整个页面滑动
more_btn = '//*[contains(@text, "更")]'
more = driver.find_element_by_xpath(more_btn)
# 1 获取元素屏幕座标
print('座标:' + more.location)
# 2 获取元素大小
print('宽和高:' + more.size)
# 获取包命和启动名
print('包命:', driver.current_package)
print('启动名:', driver.current_activity)
driver.quit()
APP元素事件操作API
swipe滑动事件
import time from util import get_driver # swipe(start_x, start_y, end_x, end_y, duration=None),duration单位ms # 作用: # 1 滑动没有持续时间,滑动相对随机,随机距离不远 # 2 滑动持续在2s左右时,滑动相对准确 --最常用 # 3 滑动持续为几十毫秒时,会产生点击起点位置操作 driver = get_driver() save = driver.find_element_by_xpath("//*[contains(@text,'存储')]").location wlan = driver.find_element_by_xpath("//*[contains(@text,'WLAN')]").location # driver.swipe(save.get("x"), save.get("y"), wlan.get("x"), wlan.get("y")) # 滑动,有滑动时间,滑动相对准确 最少2s driver.swipe(save.get("x"), save.get("y"), wlan.get("x"), wlan.get("y"), 2000) # 滑动,有滑动时间,滑动相对较长 最少200ms 但如果时几十毫秒,会产生点击起点位置操作 driver.swipe(save.get("x"), save.get("y"), wlan.get("x"), wlan.get("y"), 200) # 关闭 time.sleep(2) driver.quit()
scroll滑动事件
import time from util import get_driver # scroll(origin_el, destination_el, duration=None) # 滑动相对随机,不用 driver = get_driver() save = driver.find_element_by_xpath("//*[contains(@text,'存储')]").location wlan = driver.find_element_by_xpath("//*[contains(@text,'WLAN')]").location # 无滑动时间 driver.scroll(save, wlan) # 有滑动时间2s driver.scroll(save, wlan, 2000) # 关闭 time.sleep(2) driver.quit()
drag拖拽事件
import time from util import get_driver # drag_and_drop(origin_el, destination_el):从⼀个元素滑动到另⼀个元素,起点元素最终会替代终点元素原本屏幕上的位置 driver = get_driver() save = driver.find_element_by_xpath("//*[contains(@text,'存储')]").location wlan = driver.find_element_by_xpath("//*[contains(@text,'WLAN')]").location driver.drag_and_drop(save, wlan) # 关闭 time.sleep(2) driver.quit()
from time import sleep from appium.webdriver.common.touch_action import TouchAction from util import get_driver driver = get_driver() save = driver.find_element_by_xpath("//*[contains(@text,'存储')]") more = driver.find_element_by_xpath("//*[contains(@text,'更多')]") touch_action = TouchAction(driver) # 滑动方式一:press(元素).move_to(元素) 滑动不精准,scroll底层实现方式 touch_action.press(save).move_to(more).release().perform() # 滑动方式二:press(座标).move_to(座标) 滑动不精准,swipe底层实现方式 touch_action.press(x=save.location.get('x'), y=save.location.get('y')).move_to(x=more.location.get('x'), y=more.location.get( 'y')).release().perform() # 滑动方式三:long_press(元素).move_to(座标) 精准滑动,drag_and_drop底层实现方式 touch_action.long_press(save).move_to(more).release().perform() sleep(2) driver.quit()
应⽤置于后台事件
from time import sleep
from util import get_driver
driver = get_driver()
sleep(1)
# 将设置app放置后台5s,在此期间,点击桌面应用无效,因为它并没有运行结束,而是在后台运行
driver.background_app(5)
driver.quit()
APP模拟⼿势⾼级操作
TouchAction是AppiumDriver的辅助类,主要针对⼿势操作,⽐如滑动、⻓按、拖动等, 原理是将⼀系列的动作放在⼀个链条中 发送 到服务器,服务器接受到该链条后,解析各个动作,逐个执⾏
from time import sleep
from appium.webdriver.common.touch_action import TouchAction
from util import get_driver
driver = get_driver()
wlan = driver.find_element_by_xpath("//*[contains(@text,'WLAN')]")
touch_action = TouchAction(driver)
"""1 手指轻敲操作:模拟⼿指轻敲⼀下屏幕操作"""
# TouchAction(driver).tap(wlan).perform() # 元素方式
touch_action.tap(x=wlan.location.get('x'), y=wlan.location.get('y')).perform() # 座标方式
"""2 ⼿指按操作:: 模拟⼿指按下屏幕,然后松开"""
touch_action.press(wlan).release().perform() # 元素方式
touch_action.press(x=wlan.location.get('x'), y=wlan.location.get('y')).perform() # 座标方式
"""3 等待操作:wait(ms=0)"""
# 方式一:press + wait
wlan.click()
ssid = driver.find_element_by_xpath("//*[contains(@text,'SSID')]")
# 添加等待(有⻓按)/不添加等待(⽆⻓按效果)
touch_action.press(ssid).wait(2000).release().perform()
# 方式二:long_press
touch_action.long_press(ssid).release().perform() # 元素方式
touch_action.long_press(x=ssid.location.get('x'), y=wlan.location.get('y')).release().perform() # 座标方式
sleep(2)
driver.quit()
from time import sleep
from appium.webdriver.common.touch_action import TouchAction
from util import get_driver
# appium版本 1.12
# move_to:绝对座标
# appium版本 1.7之前
# move_to:相对座标
driver = get_driver()
bat = driver.find_element_by_xpath("//*[contains(@text,'电池')]")
wlan = driver.find_element_by_xpath("//*[contains(@text,'WLAN')]")
driver.drag_and_drop(bat, wlan)
driver.find_element_by_xpath("//*[contains(@text,'安全')]").click()
sleep(2)
driver.find_element_by_xpath("//*[contains(@text,'屏幕锁定')]").click()
sleep(2)
driver.find_element_by_xpath("//*[contains(@text,'图案')]").click()
sleep(2)
TouchAction(driver).press(x=234, y=850).wait(100).move_to(x=723, y=850).wait(100) \
.move_to(x=723, y=1326).wait(100).move_to(x=234, y=1808).release().perform()
sleep(2)
driver.quit()
⼿机操作API
from time import sleep
from util import get_driver
driver = get_driver()
# 1 获取手机分辨率,通常用于swipe方法完成滑动操作
size = driver.get_window_size()
print('分辨率:', size)
width = size.get('width')
height = size.get('height')
driver.swipe(width * 0.5, height * 0.8, width * 0.5, height * 0.2, 2000)
sleep(3)
driver.close_app()
driver.swipe(width * 0.8, height * 0.5, width * 0.2, height * 0.5, 2000)
sleep(3)
driver.quit()
发送键到设备
from time import sleep
from util import get_driver
driver = get_driver()
# 模拟音量键 +
for i in range(3):
driver.keyevent(24)
# 模拟音量键 -
for i in range(3):
driver.keyevent(25)
sleep(3)
driver.quit()
操作⼿机通知栏
from appium import webdriver
import time
# server 启动参数
from appium.webdriver.common.touch_action import TouchAction
desired_caps = {}
# 设备信息
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.56.101:5555'
# app的信息
desired_caps['appPackage'] = 'io.manong.developerdaily'
desired_caps['appActivity'] = 'io.toutiao.android.ui.activity.MainActivity'
# 声明我们的driver对象
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
# 打开通知栏
driver.open_notifications()
# 获取时间 id
value = driver.find_element_by_id("com.android.systemui:id/date_expanded").text
print("时间为:", value)
time.sleep(2)
driver.quit()
获取⼿机当前⽹络
from appium import webdriver
import time
# server 启动参数
from appium.webdriver.common.touch_action import TouchAction
desired_caps = {}
# 设备信息
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.56.101:5555'
# app的信息
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'
# 声明我们的driver对象
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
# 打印手机网络状态
print("当前网络为:", driver.network_connection)
# 修改手机网络 data
driver.set_network_connection(4)
# 打印手机网络状态
print("当前网络为:", driver.network_connection)
# 修改手机网络
driver.set_network_connection(2)
# 打印手机网络状态
print("当前网络为:", driver.network_connection)
# 修改手机网络
driver.set_network_connection(1)
# 打印手机网络状态
print("当前网络为:", driver.network_connection)
time.sleep(2)
driver.quit()
⼿机截图
from appium import webdriver
import time
# server 启动参数
from appium.webdriver.common.touch_action import TouchAction
desired_caps = {}
# 设备信息
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.56.101:5555'
# app的信息
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'
# 声明我们的driver对象
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
# 截图
driver.get_screenshot_as_file("{}.png".format(int(time.time()))) # 1583378495 不加int:1583378546.1369514
time.sleep(2)
driver.quit()