Python借助UiAutomator操控手机 基本的自动控制手机操作

鳄鱼君

发表文章数:642

Vieu四代商业主题

高扩展、安全、稳定、响应式布局多功能模板。

¥69 现在购买
首页 » Python » Python借助UiAutomator操控手机 基本的自动控制手机操作

要使用Python来操作UI Automator从而控制手机,需要安装一个第三方库,就是uiautomator库,直接使用pip命令进行安装即可,安装完成后导入uiautomator库

from uiautomator import Device
device=Device()
print(device.dump())

由于是第一次运行,uiautomator会往手机中安装两个没有图标的程序,会弹出窗口询问是否安装,单击允许安装即可

Python借助UiAutomator操控手机 基本的自动控制手机操作

安装完成以后,可以看到终端,就是你输入命令的地方,我的是在pycharm里面,会出现类似于XML的内容。

UI AutomatorViewerPython uiautormator不能同时运行,一旦Python uiautomator运行过一次,它安装的两个文件就会在手机后台运行。这两个文件会占用Android内部叫做UI hierarchy的东西,从而导致SDK自带的UI Automator Viewer一旦尝试获取手机屏幕就会报错。

出现这种情况,就需要手动结束Python uiautomator在手机中安装的两个文件的进程,或者重启手机后才能继续使用UI Automator Viewer。这两个进程的名字叫做“uiautomator”和“com.github.uiautomator.test”。我这里是重启手机。

UiAutomator基本使用

现在我准备打开QQ,那么手机屏幕就需要滑到有QQ的呢一个屏幕上,这里还需要之一大小写

from uiautomator import Device
device=Device()
device(text='QQ').click()

如果电脑上只连接一台Android手机,那么初始化设备连接只需要使用device=Device()即可,那么连接很多台Android手机的话,就需要指定手机的串号。要查看手机串号,需要在cmd终端输入以下命令:

adb devices -l
4PSW694DKNBYHYVG       device product:PACM00 model:PACM00 device:PACM00 transport_id:1

只要不是人为去修改,正常情况下,每一台手机的串号都是不一样的,知道手机的串号后,就可以指定控制对应的手机:

device=Device('4PSW694DKNBYHYVG')

在初始化了设备连接以后,所有的操作都是通过这个变量device的各个方法或者属性来完成的。device的参数text=’QQ’,成为selector,也就是选择器。通过不同的选择器来选定不同的元素,并对元素进行不同的操作

选择器

那么该如何确定选择器呢,执行下面的代码:

from uiautomator import Device
device=Device()
print(device.dump())

前面已经尝试过上面的代码了,此时会以XML输出当前手机屏幕显示的窗口布局信息,这里的XML就相当于网页中的HTML,用来描述窗口上面各个部分的布局信息。这里再说一下XML的格式,和HTML非常像:

<标签 属性1="属性值1" 属性2="属性值2">文本</标签>

上面的代码中需要闭合,其中的各个属性和属性值,就是选择器操作的对象。这里的不同属性包括但不限于“bounds” “checkable” “checked” “class” “clickable”

在uiautomator中,也有选择器和这些属性一一对应:
选择器”className“对应”class”标签;
选择器”packageName“对应”package”标签;
选择器”resourceId“对应”resourceld-id”标签。
所以要选择一个元素,可能有如下的写法:

device(className="android.widget.FrameLayout")
device(packageName="com.android.settings")
device(index="0" long-clickable="false")
device(text='QQ')

可以用一个标签来作为选择器,也可以同时使用多个标签来更精准地描述某一个元素。一般操作有文字的元素,主要是使用text这个属性,比方说‘微信’,‘百度’,如果是从屏幕上读文字,就使用其他的属性。

获得屏幕文字

如果要从Android手机上读取当前屏幕上显示的文本内容,用到的是一个元素的“.text”属性。这里以获取biubiu加速器中的游戏为例

from uiautomator import Device
device=Device()
print(device.dump())

你会得到XML代码,仔细观察后发现所有text的resource-id属性值都是”com.njh.biubiu:id/tv_game_name”。那么要获取游戏的名字,就可以使用下面的代码

from uiautomator import Device
device=Device()
game_name=device(resource-id="com.njh.biubiu:id/tv_game_name").text
print(game_name)

这里需要注意:你想要获取呢个页面的东西,屏幕就必须在那个页面,否则会报错,下面的是循环列出所有的内容,上面的代码只是输出一个名字

前面说到过选择器,resourceId选择器对应resource_id,不要搞错了,否则还是会报错

在前面我已经反复强调“当前屏幕”,这是uiautomator与Selenium不一样的地方。对于selenium来说,及时网页没有在浏览器窗口显示出来,也可以获取到”当前网页”中的内容。但是uiautomator只会显示”当前屏幕”上人眼能看到的内容。

同样,该怎么获取到屏幕下面的内容呢?对于我们,要获取屏幕下面的内容,就需要滑动屏幕。对于uiautomator也是如此,也需要把屏幕往上滑动。

滚动屏幕

滚动屏幕对应的操作为“.scroll”.它的操作对象是一个可以滚动的对象。如果手动操作可以把屏幕向上滚动,那么屏幕上应该至少有一个元素是可以滚动的,那么选择器就可以这样写:

device(scrollable=True)
device(scrollable=True).scroll.vert.forward #向上滚动一屏
device(scrollable=True).scroll.vert.backward #向下滚动一屏

这里forward是向前的意思,backward是向后的意思,由于向上、向下是”垂直方向”,所以代码中使用了vert,这个是英语单词vertical(垂直)的简写,可能会遇到有的App会出现左右滚动的情况,这时候就需要使用horiz,这个同样是英语单词horizontal(水平)的简写。那么,向左以及向右滚动就可以这么写:

device(scrollable=True).scroll.horiz.forward #向右滚动
device(scrollable=True).scroll.horiz.backforw #向左滚动

这里你可能需要一个在线翻译工具来辅助你练习,把上面的呢些单词记牢固

使用滚动屏幕的方式,就可以把所有的游戏名字都读取下来,参考下面的代码:

from uiautomator import Device
device=Device()

for a in range(20):
    game_name = device(resourceId="com.njh.biubiu:id/tv_game_name")
for item in game_name:
    print(item.text)
device(scrollable=True).scroll.vert.forward()

这里需要注意,我们向下滑动是forward,就是向上滚动屏幕吧,这里我就不再举例子了,你也可以试试左右滑动,只需要把vert修改为horiz即可

还有一点也需要注意,由于滚动一屏时,有可能前一屏最下面的元素滚动以后刚好到了后一屏的最上面,因此可能出现重复获取同一个游戏名字两次的情况,那么在实际中就需要注意去重

还有每次运行代码都会提示安装呢两个文件,如果安装了话,点击取消即可,目前,我还不知道怎么把这个去掉。

滑动屏幕

在某些情况下,整个窗口布局的XML里面,所有的元素的scrollable属性值全部都是False,oppo手机是没问题的,这里说下小米手机,它好像不能够使用”scrollable=True“来左右滚动桌面,于是就需要根据坐标来滑动桌面的.swipe()

在Android手机的屏幕上,左上角为坐标原点(0,0),越向下,y轴数字越大;越向右,x轴数字越大。

.swipe()“这个方法操作的对象是整个手机屏幕,所以不需要为device设定选择器,使用方法如下:

device.swipe(500,500,0,600)

它接受4个参数,分别为起始点x轴坐标,起始点y轴坐标,终点x轴坐标,终点y轴坐标。对于左右滑动来说,只需要改变x轴坐标即可。如果要显示右边的一,那么起始点的x坐标要大于重点的x坐标。如果要显示左边一屏,起始点x坐标要小于终点x坐标。

这里有些小伙伴可能会搞混,这个全靠自己理解了,我也无能为力,你可以对着自己的手机屏幕,想一下,如果你想要显示右边屏幕的内容,那么首先会把手指放到屏幕的右边,向左滑动,所以起始点的x坐标要大一些。真要是还不理解啦,找耶稣吧….一般情况下都可以使用上面的第一种方法滚动屏幕,不需要使用这种坐标的方式

点击屏幕

选择一个元素以后,除了获取它上面的文字外,还可以点击。就如同前面点击QQ一样。点击操作”.click()”和”.long_click()”,分别对应短按点击和长按点击,它们可以直接应用于一个被选择出来的元素上:

device(text="QQ")
device(text="微信")

同样点击操作也可以直接应用于坐标位置,这里需要知道准确的坐标值,方法可以参考下面图片

device.click(1080,210)  #第一个参数为横坐标x轴,第二个参数为纵坐标y轴参数
device.long_click(1080,210)  #同上

采用坐标的这种方法主要是针对某些App,它的某个元素可能并没有一个独特的标志来让选择器选择。我们发现有些坐标是[722,2132][1080,2266],[722,2132]对应的是按键的左上角坐标,[1080,2266]对应的是按键右下角的坐标,这里根矩形一样,确定了一个对角的坐标,也就确定了一个矩形,因此只要点击这两个坐标之间的位置,就是点击了该按钮。

当然还有一种简单的方式,你把Ui Automator Viewer的窗口拉大,鼠标放到上面移动,就可以看到右边窗口中显示的坐标,此时我的鼠标就放在红色方框中,右边选中的就是坐标。

输入文字

如果我们选中了一个搜索框按钮,那么就需要输入文字来进行搜索。输入文本使用的操作就是”.set_text()“,这里,我们用biubiu加速器的搜索按钮来输入一个游戏进行搜索,代码如下:

from uiautomator import Device
device=Device()
game_name=device(resourceId='com.njh.biubiu:id/toolbar_right_slot_2').set_text('人类一败涂地')

判断一个元素是否存在

由于手机上面的各个元素加载是需要一定时间的,如果在元素加载出来之前就对其进行操作,这会导致程序报错,在操作中你肯定会遇到下面图片中的错误警报,这个就是元素还未加载出来,就进行操作而导致的

那么就需要判断元素是否存在,在对其进行操作。判断元素是否存在,使用”.exists“属性。如果存在,值为True,不存在则为False,用法:

from uiautomator import Device
device=Device()
device.click(963,80)
input=device(resourceId='com.njh.biubiu:id/search_edit_text')
if input.exists:
    input.set_text('人类一败涂地')
else:
    print('输入框还未出现')

这里需要注意,我们没有设置点击按钮的事件,所以你需要把搜索按钮的点击事件加上即可,我这里就不在贴代码了,想想还是说一下吧( •́ .̫ •̀ )

如果元素只是因为没有来得及加载出来而不存在,不是屏幕界面错误,那么还可以设置等待时间,然后再进行操作。使用到的方法是”.wait.exists()”,其中exists还可以设置等待超时间,可以这样使用:

from uiautomator import Device
device=Device()
content=device(text='王者荣耀')
if content.wait.exists(timeout=20000):
    content.click()
else:
    print('元素不存在')

等待文字为”王者荣耀”,如果元素在设置的时间内出现了,就点击它,否则打印不存在的提示,这里”.exists()”的超时时间单位为ms,所以你自己需要注意一下。

点亮关闭屏幕

使用”.wakeup()”方法和”.sleep()”方法可以点亮或者关闭屏幕。当手机屏幕处于关闭状态时,使用”.wakeup()”方法可以让手机屏幕点亮,当手机屏幕处于点亮状态时,使用”.sleep()”方法,可以将其关闭.如果要检查当前手机屏幕是开启状态还是关闭状态,可以使用”.screen”属性来检查。它的值如果为”on”,表示当前手机屏幕亮起,值为”off”,表示当前手机屏幕关闭,两者的使用方法如下:

from uiautomator import Device
device=Device()
device.sleep() #关闭屏幕
device.wakeup() #点亮屏幕
if device.screen =='on':
    print('当前手机屏幕亮起')
elif device.screen=='off':
    print('当前手机屏幕关闭')

操作实体按键

Android手机自带了很多的实体按键,使用Python的uiautomator可以模拟这些按键被按下的状态,使用方法:

device.press.实体按键的名称()

这里再给你们列举一些常见的实体按键吧

实体按键名称 作用
power 电源键
back 返回键
menu 菜单键
volume_up 音量增大
volume_down 音量减小
home 返回桌面

如果说现在你的手机屏幕处于关闭状态,可以使用”.wakeup()”的方法,也可以模拟按下电源键:

device.press.power()  #按下电源键
device.press.volume_down() #减小音量键

Watcher

使用手机的时候,可能会弹出一个对话框,就是叫你更新啊什么的,对于我们来说,直接点击关闭,不用管它就行,但对于操控手机的程序来说,就是会报错,呢肯定不是我们希望看到的结果

,这里做个假设,现在有几百个游戏,我们需要通过biubiu加速器来搜索它们,并获取它们的下载量。正常情况下是这样的流程:

  • 1.初始位置是有搜索框的一屏
  • 2.清空搜索框内的文字
  • 3.输入游戏名字
  • 4.点击搜索按钮
  • 5.点击第一个搜索进入游戏详情页
  • 6.从游戏详情页获取评分
  • 7.点击实体按键back返回搜索界面
  • 8.转到步骤(1)

在正常情况下,可以按照从1~8的顺序循环往复,知道搜索完所有的游戏。但是如果在第6步获取了评分以后,突然弹出了升级框。此时由于程序不知道弹出了提示框,它还在执行第7步,按下了返回键。此时仅仅是关闭了升级的提示框,手机屏幕仍然处于游戏的详情页,但是程序以为已经返回了搜索界面,于是尝试操作搜索框,那么就会报错,这也是我们不想看到的。

针对上面提到的情况,就可以使用exists来判断搜索框是否存在。但是问题在于,即使知道了搜索框不存在,却并不能解决问题。因为根本元婴在于手机屏幕和程序出现了不同步,要解决问题,就需要让它们同步。

这里就可以使用watcher可以让程序在找不到元素时自动尝试解决问题。假设当前手机屏幕在游戏详情页,此时执行以下代码就会报错:

input=device(resourceId='com.njh.biubiu:id/search_edit_text')
input.clear_text()
input.set_text('人类一败涂地')

那么可以在执行这段代码之前,先注册一个watcher:

from uiautomator import Device
device=Device()
device.watcher('In_name').when(text='立即下载').when(text='游戏介绍').press.back()
input=device(resourceId='com.njh.biubiu:id/search_edit_text')
input.clear_text()
input.set_text('人类一败涂地')

执行这段代码之前,你需要知道我们是哪一部分出错的,就是在搜索游戏进入详情页出错的,所以开始的屏幕应该在游戏详情页,然后就可以看到程序自动退到了搜索界面,清空了里面所有的内容并输入了你写的文本。下面的代码可能有些局限性

这里解释一下上面的代码,这一段代码注册了一个名字为”In_name“的watcher,这个watcher要执行的操作是按下实体按键的返回键。它被激活需要满足3个条件:

  • 1.代码出现了找不到元素的情况,即将报错
  • 2.当前屏幕上某个元素的文本为”立即下载”
  • 3.当前屏幕上某个元素的文本为”游戏介绍”

watcher是一种特殊的对象,它被注册以后就一直静静地等待,只有程序在里面某一处代码出现了找不到元素的情况下才会被触发,并按照注册顺序依次进行检查。如果找到了符合条件的watcher,就会执行这个watcher对应的操作。如果所有的watcher都检查完依然没有找到符合条件的情况,那么就会抛出找不到元素的异常。

watcher的第一个参数表示这个watcher的名字,可以是任意字符串。但是要保证watcher的名字不能相同。后面所有的”when()”表示满足这个watcher所需要的条件。每个when()之间是的关系,只有所有的when里面的内容都满足,这个watcher才会被触发,并执行最后的语句

UiAutomator其他操作

Android的UI Automator框架可以实现所有的图形界面的操作,只要是人能做到的它都可以。Python的uiautomator库完整的实现了这些操作。但是由于一些操作对于开发爬虫来说并没什么作用,这里就省略不在进行介绍,有兴趣的小伙伴可以去查看uiautomator官方文档,那么基本的知识就先总结到这里吧•·τнänκ чöü·•

未经允许不得转载:作者:鳄鱼君, 转载或复制请以 超链接形式 并注明出处 鳄鱼君
原文地址:《Python借助UiAutomator操控手机 基本的自动控制手机操作》 发布于2019-11-24

分享到:
赞(0) 赏杯咖啡

评论 抢沙发

8 + 6 =


文章对你有帮助可赏作者一杯咖啡

支付宝扫一扫打赏

微信扫一扫打赏

Vieu4.6主题
专业打造轻量级个人企业风格博客主题!专注于前端开发,全站响应式布局自适应模板。
切换注册

登录

忘记密码 ?

您也可以使用第三方帐号快捷登录

Q Q 登 录
微 博 登 录
切换登录

注册