Uiautomator2
- 支持环境
Androidhg4 4.4+
Python 3.6+ ( 3.8暂不支持)
课程开发环境
开发环境: Windows 10
运行环境: ubuntu18.04 (提供虚拟机文件)
虚拟机: VMware⑧Workstation 15 Pro (提供软件安装包)
SSH : SecureCrt Version 7.0.0 (提供绿色软件安装包)
IDE : PyCharm 2019.3.2 (Professional Edition) ( 提供软件安装包)
安卓模拟器:夜神模拟器V6.6.0 (提供软件安装包)
Python版本: python3.6.9 ( 提供安装包)
开发环境注意事项
不要使用vitualbox虚拟机 与安卓模拟器冲突
执行流程
[图片上传失败...(image-278648-1598670535406)]
① 在移动设备上安装atx-agent(守护进程),随后atx-agent启动uiautomator2服
务(默认7912端口)进行监听
② 在PC.上编写测试脚本并执行(相当于发送HTTP请求到移动设备的server端)
③ 移动设备通过WIFI或USB接收到PC.上发来的HTTP请求,执行制定的操作
环境搭建
- 安装 virtualenv
- pip3 install virtualenv 或者 pip3 install virtualenv -i https://pypi.douban.com/simple/
- 创建一个虚拟的python环境
- python3 -m virtualenv venv
进入虚拟环境
- source venv/bin/activate
查看安装的包(依赖)
- pip freeze
配置PyCharm的python环境
adb
- Android调试桥(adb)
adb client :命令行程序"adb" 用于从shel或脚本中运行adb命令。
adb server : ADB Server是运行在PC上的一个后台进程。
adbd :程序"adbd" 作为一个后台进程在Android设备或模拟器系统中运行。
- adb能用来做什么
安装卸载apk
移动设备和PC之间拷贝文件
查看设备上安装的应用信息
文件管理
各种刷机工具
各种手机root工具
查看连接的移动端设备
- adb devices
给移动设备安装apk应用
- adb install xxx.apk
查看移动设备apk包名
- adb shell pm list packages
卸载移动设备apk应用
- adb uninstall com.xxx.xxxx
Uiautomator2安装
在python虚拟环境下
安装依赖
- pip install uiautomator2 -i https://pypi.douban.com/simple
重要注意⚠️
- curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
- python get-pip.py 或者 sudo -H python get-pip.py
- sudo -H pip install uiautomator2
对当前移动设备进行初始化
- python -m uiautomator2 init
安装u2包:
uiautomator-server :就是谷歌原生的uiautomator
atx-agent : uiautomator的守护进程
atx-agent增加远程控制的功能,依赖minicap和minitouch这两个工具
⚠️python -m uiautomator2 init报错Can't connect to HTTPS URL because the SSL module is not available- brew update && brew upgrade
- brew uninstall --ignore-dependencies openssl; brew install https://github.com/tebelorg/Tump/releases/download/v1.0.0/openssl.rb
⚠️pyenv重新安装python
u2连接移动设备的三种方式
import uiautomator2 as u2
#通过手机WIFI来连接,需要查看手机的IP地址
d = u2.connect_wifi("192.168.0.130")
print(d.info)
import uiautomator2 as u2
#通过手机的序列号来连接
d = u2.connect_usb("4bf05af7")
print(d.info)
import uiautomator2 as u2
#通过adb wifi也就是 adb tcpip模式,注意不要丢掉端口号
d = u2.connect_adb_wifi("192.168.1.7:5555")
#device_info可以获取详细的设备信息
print(d.device_info)
import uiautomator2 as u2
import time
#启动手机上的app,通过aapt工具来获取包名
#appt获取包名的时候,在aapt dump badging xxx.apk,获取到的是包名
d.app_start("com.ss.android.ugc.aweme")
#运行五秒
time.sleep(5)
#停止抖音app
d.app_stop("com.ss.android.ugc.aweme" )
u2自动化工具基本操作
import uiautomator2 as u2
import time
d = u2.connect_wifi("192.168.0.130")
# 查看设备信息
print(d.service("uiautomator").running())
d.service("uiautomator").start()
time.sleep(2)
print(d.service("uiautomator").running())
# 停止服务
d.service("uiautomator").stop()
time.sleep(2)
print(d.service("uiautomator").running())
# 查看atx-agent运行状态,如果atx-agent真的停止了,我们可以通过connect来唤醒atx-agent服务
print(d.agent_alive)
# 查看设备的分辨率
print(d.window_size())
# 查看获取到的wifi地址
print(d.wlan_ip)
import uiautomator2 as u2
# 通过wifi进行连接
d = u2.connect_wifi("192.168.0.130")
# 通过app_install 方法安装 apk,url=“xxx.apk”
d.app_install(url="")
# 启动app
d.app_start(package_name="cn.com.open.mooc")
# 获取当前前台运行的app的信息
print(d.app_current())
# d.app_stop("cn.com.open.mooc")
# 获取app的详细信息
print(d.app_info(pkg_name=""))
# 清楚app缓存
d.app_clear("cn.com.open.mooc")
# 卸载app
d.app_uninstall("cn.com.open.mooc")
# 获取所有app列表
print(d.app_list())
# 获取所有正在运行的app列表
prin(d.app_list_running())
# 停止所有app
d.app_stop_all()
# 卸载所有第三方app
d.app_uninstall_all()
activity和控件,weditor工具的安装
- 安装weditor
pip install weditor -j https:lpypi.tuna.tsinghua.edu.cn/simplel
weditor启动和介绍
查看版本
- pip freeze | grep weditor
查看是否启动
- weditor
查看atx-agent是否启动
- adb shell
- ps | grep atx-agent
UiSelector和控件定位介绍
UiSelector代表一种搜索标准,可以在当前展示界面上查询和获取特
定元素的句柄
# coding: utf-8
import uiautomator2 as u2
d = u2. connect_ usb("4bf05af7")
# 可以通过aapt这个工具来获取包名,是获取的apk的包名,设置这个app的apk
# 包名通过weditor来获取,package:com.android.settings
# 启动设置app
d.app_start(package_name="com.android.settings")
#全文本匹配,点击
d(text="其他无线连接").click()
#文本包含
d(textContains="无线").click()
#传入正则表达式
d(textMatches=".{2}无线.{2}").click()
d(textStartsWith="其他无").c1ick()
#className选取方式
d(className="android.widget.TextView")[4].click()
#d(className="android.widget.TextView",instance=4).click()
d(classNameMatches="android\.widget\.\w{8}",text="蓝牙").click()
Text文本选取方式
- text全文本匹配
- textContains文本包含
- textMatches正则表达式
- textStartsWith起始文本
className选取方式
- className className匹配
- classNameMatches className正则表达式匹配
resourceld资源ID选取方式
- resourceld全资源ID匹配
- resourceldMatches正则表达式匹配
import uiautomator2 as u2
d = u2.connect_usb("4bf05af7")
# 通过资源ID来定位控件,通过索引来进行限定
# 可以选择多个控件,默认选择的是第一-个控件
d(resourceId="android:id/title")[2].click()
# 通过实例来进行查找,值和索引值是一样的
d(resourceId="android:id/title",instance=2).click()
# 物通过多个条件来进行限定
d(resourceId="android:id/title",text="蓝牙").click()
#通过正则表达式的方法来获取资源ID,进行控件的定团
d(resourceIdMatches="android:id\/\w{5}",text="蓝牙").click()
#逋辻className来狹取控件定位的吋候,需要注意层級夫系
# d(className="android.widget.TextView")[4].cLick()
# d(className="android.widget.TextView",instance=4).click()
# d(cLassNameMatches= "android\.widget\.\w[8]",text="蓝牙").cLick()
#这里是链式定位方式
# d(className="adroid.widget.ListView").child(text="蓝牙").cLick()
d(className="android.widget.ListView").child_by_text("蓝牙",resourceId="android:id/title").click()
#完全的链式定位方法,代码非常的冗长,我们是不建议的
d(className="android.widget.ListView").child(resourceId="oppo:id/oppo_ preference")[2].child(resourceId="android:id/title").click()
# 这种方法多次依据,也不建议
d(resourceId="oppo:id/oppo_preference").sibling(resourceId= "oppo:id/oppo_preference“).sibling(resourceId="oppo:id/oppo_ preference",instance=2).click()
坐标定位方式
import uiautomator2 as u2
d = u2.connect_usb("4bf05af7")
d.app_start("com.android.settings")
# 这里可以通过坐标点来进行控件的定位
# 我们可以选用实际的坐标点,也可以通过百分比来进行定位
d.click(0.168,0.341)
import uiautomator2 as u2
d = u2.connect_usb("4bfØ5af7")
d.app_start("com.android.settings")
# 控件不存在,我们该怎么办
# d(text="蓝牙").click(timeout=5)
# click_exists如果控件存在就点,如果控件不存在就返回,在timeout时间之内
d(text="蓝牙1").click_exists(timeout=5)
# 在操作之前,通过exists属性判断控件是否存在
# print(d(text="蓝牙1").exists)
# print(d(text="蓝牙1").exists(timeout=5))
#print(d(resourceId="android:id/list").child(resourceId="oppo:id/oppo_ preference").count)
for view in d(resourceId="android:id/list").child(resourceId="oppo:id/oppo_ preference")
print(view.indo)
通过U2实现移动设备九宫格解锁
import uiautomator2 as u2
d = u2.connect_usb("4bfØ5af7")
# 滑动解锁操作
# 息屏
# d.screen_off()
# 点亮屏幕
#d.screen_on()
# 解锁
#d.unlock()
# 获取屏幕状态
#print(d.info.get("screenOn"))
# home键
#d.press("home")
# 返回键
#d.press("back")
# 向左滑
# d.swipe_ext("left")
# 向右滑
# d.swipe_ext("right")
# 滑动解锁
#swipe_points
# 先解锁调出九宫格界面
d.unlock()
# 在 position 里面拿到坐标
# (0.224,0.393)
# (0.493,0.395)
# (0.781,0.396)
# (0.501,0.551)
# (0.218,0.703)
# (0.773,0.703)
# duration=0.2 是0.2秒
d.swipe_points(points=[
(0.224,0.393),
(0.493,0.395),
(0.781,0.396),
(0.501,0.551),
(0.218,0.703),
(0.773,0.703)
],duration=0.2)
xpath定位方式
Xpath常用规则:
/ 从当前节点选取直接子节点
// 从当前节点选取子孙节点
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性
import uiautomator2 as u2
d = u2.connect_usb("4bfØ5af7")
# 下载当前页面的源代码文件
with open("phone.file", 'w',encoding='utf-8') as f:
# 通过这个方法来获取到控件的源代码文件
f.write(d.dump_hierarchy())
- XPath Helper 游览器扩展插件安装(科学上网安装)
//li[@class='other_item']/a/text())
//li[@class='item-3']//text()
//li[@class='item-3']/a/@href
- XPath 规则获取 WEditor中XPath属性直接获取
1、文字属性定位
2、姿源ID属性定位
3、className属性定位
4、鍵式凋用美系定位
5、坐祢定位
6、xpath定位
实现自动化登录考研帮app并滑动资讯信息
import uiautomator2 as u2
class HandleMeituan(object):
def __init__(self, serial="4bfØ5af7"):
# 当前是通过usb的方法来连接移动设备的
self.d = u2.connect_usb(serial=serial)
self.size = self.get_windowsize()
self.hand1e_watcher()
def hand1e_watcher(self):
“”"定义一个监控器“”“
#监控器会单独的起一个线程
#用户隐私协议
self.d.watcher.when('//*[@resource-id="com.tal.kaoyan:id/tip_commit"]').c1ick()
#广告
self.d.watcher.when('//*[@resource-id="com.tal.kaoyan:id/tv_skip"]').c1ick()
#监控器写好之后,要通过start方法来启动
self.d.watcher.start()
def get_windowsize(self) :
"""获取手机屏幕的大小"""
return self.d.window_size()
def close_app(self):
#监控器关闭
self.d.watcher.stop()
#停止考研帮app
self.d.app_stop("com.tal.kaoyan")
#清理缓存
self.d.app_clear("com.tal.kaoyan")
def handle_meituan_app(self):
"""启动美团app,并实现自动化操作"""
#aapt这个工具 或者 通过weditor 获取 package_name
self.d.app_start(package_name="com.tal.kaoyan")
# 在点击之前需要判断是否有这个控件
self.d(text="密码登录").click_exists(timeout=10)
# 通辻找到相美控件之后文本控件,set_text这个方法来輸入文字 self.d(resourceId="com.tal.kaoyan:id/login_email_edittext").set_text("450120127@qq.com")
#输入密码
self.d(resourceId='com.tal.kaoyan:id/login_password_edittext").set_text("123456")
# self.d(resourceId"com.tal.kaoyan:id/login_login_btn").click()
self.d(text="登录").click()
#在10秒钟如果这个界面启动了
if self.d.wait_activity("com.tal.kaoyan.ui.activity.HomeTabActivity",timeout=10):
self.d(text="研迅").click_exists(timeout=10)
#获取到屏幕的中心点,x轴
#在获取到y轴远方点,获取到y轴近点
x1 = int(self.size[0] * 0.5)
y1 = int(self.size[1] * 0.9)
y2 = int(self.size[1] * 0.15)
while True:
# get toast,是安卓系统系统的一个信息提示操作
if self.d.toast.get_message(e) == "内容已经全部加载完了":
self.close_app()
return
# 开始滑动研迅内容模块
self.d.swipe(x1,y1,x1,y2)
if __name__== '__main__':
k = HandleMeituan()
k.handle_meituan_app()
u2自动化工具基本操作
import uiautomator2 as u2
# 通过wifi进行连接
d = u2.connect_wifi("192.168.1.8")
# 通辻app_install方法安装apk,url="xxx.apk"
d.app_install(url="="http:l/file.mukewang.com/apk/app/115/imooc7.3.41010.apk")
# 启动app
d.app_start(package_name="cn.com.open.mooc")
# 获取当前前台运行的app的信息
print(d.app_current())
d.app_stop("cn.com.open.mooc")
# 获取app详细信息
print(d.app_info(package_name="cn.com.open.mooc"))
# 清除app缓存
#尤其是我们后面要进行的视频数据抓取,会产生-定的缓存
d.app_clear("cn.com.open.mooc")
d.app_uninstall("cn.com.open.mooc")
#获取所有app列表
print(d.app_list())
#获取所有正在运行的app的列表
print(d.app_list_running())
#停止所有app
d.app_stop_a11()
#卸载所有app,卸载所有第三方app,u2项且包不会卸载'pm','list','packages','-3'
d.app_uninstall_a11()
fiddler抓包工具,file&edit功能使用
mitmproxy,mitmdump
from mitmproxy import ctx
# 必须这么写
def request(flowl):
# print(flow.request.headers)
ctx.log.info(str(flow.request.headers))
ctx.log.warn(str(flow.request.headers))
ctx.log.error(str(flow.request.headers))
ctx.log.error(str(flow.request.url))
ctx.log.error(str(flow.request.host))
ctx.log.error(str(f1ow.request.method))
ctx.log.error(str(f1ow.request.path))
def response(flow):
ctx.log.error(str(flow.response.status_code))
ctx.log.error(str(flow.response.text))
实战
创建项目
import uiautomator2 as u2
import time
class Douyin(object):
#在__init__方法里面達接没各
def__init__(self, serial="4bf05af7"):
self.d = u2.connect_usb(serialeserial)
self.start_app()
self.handle_watcher()
self.size = self.get_windowsize()
#是用来获取一个初始时间
self.t0 = time.perf_counter()
def start_ app(self):
"""后劫app"""
self.d.app_start(package_name="com.ss.android.ugc.aweme")
def stop_app(self):
"""app退出逻辑"""
# 先关闭监视器
self.d.watcher.stop()
self.d.app_stop("com.ss.android.ugc.aweme")
self.d.app_clear("com.ss.android.ugc.aweme")
def stop_time(self):
"""停止时间"""
# 时间是秒
if time.perf_counter() - self.tØ > 20:
return True
def handle_watcher(self):
"""监视器"""
# 通知权限
self.d.watcher.when('//*[@resource-id="com.ss.android.ugc.aweme:id/a4r"]').click()
# 发现滑动查看更多
self.d.watcher.when('//*[@text="滑动查看更多"]').click()
# 添加一个监控器
self.d.watcher.when('//*[@text="快熟进入TA的个人中心"]').click()
# 监控器写好之后,一定要记得启动
self.d.watcher.start(interval=1)
def get_windowsize(self):
# 获取窗口大小
return self.d.windowsize()
def swipe_douyin(self):
“““滑动抖音视频和点击视频发布者头像的操作”””
# 来判断是否正常的进入到了视频页面
# 网络情况不好也包含在内了
if self.d(resourceId="com.ss.android.ugc.aweme:id/yy", text="我").exists(timeout=20):
while True:
# 到规定的时间停生循环
if self.stop_time():
self.stop_app()
return
# 查看是不是正常的发布者
if self.d(resourceId="com.ss.android.ugc.aweme:id/uØ").exists:
#是正常的发布者,点击头像
self.d(resourceId="com.ss.android.ugc.aweme:id/tw").click()
#返回
self.d(resourceId="com.ss.android.ugc.aweme:id/et").dlick()
if self.d(resourceId="com.ss.android.ugc.aweme:id/yy", text="我").exists:
# 进入正常的视频页面,开始滑动
x1 = int(self.size[0] * 0.5)
y1 = int(self.size[1] * 0.9)
y2 = int(self.size[1] * 0.15)
self.d.swipe(x1,y1,x1,y2)
if __name__ == '__main__':
d = Douyin()
d.swipe_douyin()
通过mitmproxy解析短视频App返回数据
#特别注意:
#在新版本的抖音中,我们是找不到正常的数据返回的
#当前使用的是10.0版本的抖音app,大家- -定要注意
#个人信息页接口
# https://aweme-eagle.snssdk.com/aweme/v1/user/?user_id
#滑动视频的接口
# https://aweme-eaple.snssdk.com/aweme/v1lfeed
import json
def response(flow):
"""解析10版本抖音app返回数据"""
# 视频
if 'https://aweme-eagle.snssdk.com/aweme/v1/feed' in flow.request.url:
# 使用json 来loadsresponse.text
video_response = json.loads(flow.response.text)
video_list = video_response.get("aweme_list",[])
for item in video list:
print(item.get("desc"), "")
#发布者页面
if 'https://aweme-eagle.snssdk.com/aweme/v1/user/?user_id' in flow.request.url:
person_response = json.loads(flow.response.text)
person_info = person_response.get("user", "")
if person_info:
info = {
'nickname': person_info.get("nickname", ""),
'total_favorited': person_info.get("total_favorited",0)
'following_count' : person_info.get("following_count", 0),
'douyin_id': person_info.get("unique_id", ""),
'follower_count' : person_info.get("follower_count", 0)
}
print(info)
- 运行文件
mitmdump -s decode_douyin.py -p 8889
atxserver2库
atxserver2:
ATX2是一款可以远程控制Android和iOS设备的设备 管理平台。
该平台使用的技术栈为:Python3+ NodeJS + RethinkDB
- 安装atxserver2方法:
安装rethinkdb数据库
https://rethinkdb.com/docs/install
······
省略
atxserver2通过pip安装部署
atxserver2多设备管理库的使用
实现多任务端app应用数据抓取系统










网友评论