Airtest的使用
有了 Appium,我们已经可以方便地自动化控制 App,但在使用过程中或多或少还会有些不方便的地方,例如连接的稳定性一般、提供的 API 功能有限等。
这里我们再介绍一个更好用的自动化测试工具——Airtest,它提供了一些更好用的 API,以及一个非常强大的 IDE,开发效率和响应速度与 Appium 相比也有提升。
Airtest 介绍
AirtestProject 是网易游戏推出的一款自动化测试框架,其项目由如下几部分构成。
-
Airtest:一个跨平台的、基于图像识别的UI自动化测试框架,适用于游戏和App,支持 Windows、Android和ioS平台,基于Python实现。
-
Poco:一款基于UI组件识别的自动化测试框架,目前支持Unity3D、cocos2dx、Android原生 App、iOS原生App和微信小程序,也可以在其他引擎中自行接人poco-sdk使用,基于Python 实现。
-
AirtestIDE:提供一个跨平台的UI自动化测试编辑器,内置了Airtest和Poco的相关插件功能,能够快速、简单地编写Airtest和Poco代码。
-
AirLab:真机自动化云测试平台,目前提供Top100手机兼容性测试、海外云真机兼容性测试等服务。
-
私有化手机集群技术:从硬件到软件,提供在企业内部私有化手机集群的解决方案。
总之,Airtest 建立了一个比较完善的自动化测试方案,我们能利用它实现所见即所爬,个人认为比 Appium 更加简单易用。本节我们先简单了解 AirtestIDE 的基本使用,同时介绍一些 Airtest 和 Poco 的基本 API 用法,12.7 节用它实际爬取一个 App。
准备工作
请确保已经安装好 AirtestIDE、Airtest Python 库和 Poco Python 库。
只使用 AirtestIDE 实现自动化模拟和数据爬取也是没问题的,因为它里面已经内置了 Python 模块、Airtest Python 库和 Poco Python 库,并且提供了非常便捷的可视化点选和代码生成等功能,即使使用者没有任何 Python 基础,也能自动化控制App和完成数据爬取。
但是对于需要爬取大量数据和控制页面跳转的场景而言,仅依靠可视化点选和自动生成代码来自动化控制App,其实是不灵活的。进一步讲,如果我们加入一些代码逻辑,例如流程控制、循环控制语句,就可以爬取批量数据了,这时候需要依赖Airtest、Poco以及一些自定义逻辑和第三方库。
Airtest的官方文档(https://airtest.doc.io.netease.com/tutorial/1_quick_start_guide/)中已经详细介绍了Airtest的安装方式,包括AirtestIDE、AirtestPython库和PocoPython库。所以,这里建议同时安装AirtestIDE、Airtest和Poco。
安装完AirtestIDE之后,它还会安装一个Python环境,这个环境中附带安装了AirtestPython库和PocoPython库,不过这个被打包在AirtestIDE里面的环境,和系统里安装的Python环境并不是同一个,所以推荐直接使用pip3工具将AirtestPython库和PocoPython库安装到系统的Python环境下。
安装Airtest Python库的命令如下
pip3 install airtest
安装 Poco Python 库的命令如下:
pip3 install pocoui
安装完成后,在 AirtestIDE 中把默认的 Python 环境由 AirtestIDE 附带的 Python 环境更换成系统的 Python 环境。打开 AirtestIDE 菜单的 “选项”→“设置”,页面如图 12-60 所示。
图 12-60 AirtestIDE 的设置页面
可以看到其中有一个选项是 “自定义Python.exe路径”,将其值修改为系统的 Python 路径即可,具体的设置方法可以进一步参考 https://airtest.doc.io.netease.com/IDEdocs/settings/1ide_settings/#python。
安装好 AirtestIDE、AirtestPython库和PocoPython库之后,准备一台Android真机或者模拟器真机的话还需要通过USB线和电脑相连,确保adb能够正常连接到手机,具体的设置方法可以参考 https://airtest.doc.io.netease.com/tutorial/1_quick_start_guide/#_4。
如果以上的准备工作在操作过程中遇到问题,可以在 https://setup.scrape.center/airtest 参考更加详细的配置。
AirtestIDE 体验
我用一台 Android 真机演示 AirtestIDE 的使用方式。首先确保可以使用 adb 正常获取手机的相关信息,如执行如下命令:
adb devices
如果能正常输出手机的相关信息,则证明连接成功,示例输出如下:
从中能看到我的设备名称为 R5CN30RM0QL。然后启动 AirtestIDE,打开菜单中的 “文件”→“新建脚本”一→“.airAirtest项目”,新建一个脚本,页面如图12-61所示。
图12-61 新建一个脚本
选定一个路径,将脚本命名为 script.air,之后点击“确定”,进入如图12-62所示的页面。
图12-62 将脚本保存为 script.air 文件
正常情况下,在图12-62右侧可以看到已经连接的设备,如果没有看到,可以查看 https://airtest.doc.io.netease.com/IDEdocs/device_connection/2_androidfaq/ 来排查问题出在哪。接下来点击设备列表右侧的connect按钮,如图12-63所示。
此时往往就可以在AirtestIDE中看到手机的屏幕了,如图12-64所示。
图12-63点击设备列表右侧的connect按钮
我们可以点击页面中的屏幕对手机进行控制,如果出现了连接问题,可以参考 https://airtest.doc.io.netease.com/IDEdocs/device_connection/2_android_fag/ 中的描述来排查常见问题并尝试解决。
图12-64AirtestIDE中出现手机屏幕
至此,请一定确保已完成的步骤都成功了,否则之后的内容将无法进行。
我们再来观察一下整个 AirtestIDE 页面,分为左、中、右三部分,以下内容为对各组件的介绍。
-
左侧靠上的部分是Airtest辅助窗,可以通过一些点选操作实现基于图像识别的自动化配置。
-
左侧中间偏上的部分是Poco辅助窗,可以通过一些点选操作实现基于UI组件识别的自动化配置。
-
中间靠上的部分是脚本编辑窗,即代码编写区域,可以通过Airtest辅助窗和Poco辅助窗自动生成代码,也可以自己编写代码,这个代码是基于Python语言的。
-
中间靠下的部分是Log查看窗,即日志区域,会输出运行、调试时的一些日志。
-
右侧是设备窗,内容为手机屏幕,用鼠标直接点击这个屏幕,真机或模拟器的屏幕也会跟着变化,而且响应速度非常快。
Airtest的图像识别与自动化控制
Airtest可以基于图像识别来自动化控制App,本节我们就体验一下这个功能。例如先点击左侧的touch按钮,意思是点击屏幕上的某个位置,如图12-65所示。
图12-65 点击touch按钮
这时AirtestIDE会提示我们在右侧的手机屏幕上截图,这里我截取的是 “大众点评” App的图标,会发现 script.air 脚本中出现了一行代码。代码内容为touch方法,其参数是刚截取的图片,如图12-66所示。
图12-66script.air脚本中生成了touch方法
然后在右侧的手机屏幕上点击 “大众点评” 的图标,进入这个App,再点击左侧的wait按钮,意思是等待指定内容加载出来,之后同样根据提示截图,如截取首页左上角的“美食”图标,如图12-67 所示。
图12-67等待首页左上角的“美食”图标加载出来
再后点击左侧的swipe按钮,意思是滑动屏幕,操作示意和结果如图12-68所示
图12-68点击swipe按钮
这时AirtestiIDE会提示我们框选一个位置。联想自己平时滑动屏幕的场景,手指一开始先放在一个位置,然后滑动,到某个位置后停止。那么这时第一步需要框选的位置就是手指一开始需要放置的位置,例如图12-68中标1的地方一一中间的菜单栏(菜单栏下方加载的内容会变化,故选择相比之下更加通用不变的菜单栏作为识别目标)框选完毕后,AirtestIDE会提示我们点选一个滑动的目标位置,这时选择框选位置上方的一个点即可,如图12-68中标2的地方。此时会发现script.air脚本中生成了 swipe方法,其第一个参数是我们框选的菜单栏的图片,第二个参数是一个vector,代表滑动方向。
这样我们就通过一些可视化的配置完成了自动化控制。
最后我们再通过左侧的keyevent按钮添加两个键盘事件,在已经生成的代码的开头和结尾分别添加一个HOME键盘事件,代表进入首页和返回首页,添加结果如图12-69所示。
图12-69添加了两个键盘事件
现在总结一下我们实现自动化控制的流程,分以下几步。
-
进入手机首页。
-
点击 “大众点评” App 的图标
-
等待左上角的 “美食” 图标加载出来。
-
向上滑动手机屏幕。
-
返回手机主页。
怎么样,是不是很简单?
|
每个手机的内容可能不一样,灵活配置就好了,原理都是类似的,示例的作用主要是让大家熟悉一些操作流程。 |
接下来点击 script.air 脚本上方的运行按钮(三角按钮),会发现AirtestIDE可以驱动手机完成指定操作了,和我们期望的流程一模一样,点击、等待、滑动操作顺次执行,且“Log查看窗”会显示执行的具体过程,如图12-70所示。
以上便是 Airtest 提供的基于图像识别来自动化控制App的过程,利用这项技术,我们不用编写任何代码就可以让手机自动操作。
图 12-70运行script.air脚本
其实 script.air 脚本内部对应的就是Python代码,只不过利用AirtestiDE封装了一层,使得编写和操作更加简单了。我们可以追踪一下源码,在当前脚本的选项卡上右击,在弹出的菜单项中选择“打开当前项目目录”,如图12-71所示。
会看到源码内容如图12-72所示。
图12-71 选择“打开当前项目目录” 图12-72 script.air 的源码内容
可以看到其中有1个Python脚本,和3张刚才截取的图片,打开Python脚本:
from airtest.core.api import *
auto_setup(__file__)
keyevent("HOME")
touch(Template(r"tpl1622349860646.png",record_pos=(-0.12,0.103),resolution=(1080,2400)))
wait(Template(r"tpl1622350037160.png",record_pos=(-0.394,-0.826),resolution=(1080,2400)))
swipe(Template(r"tpl1622350197166.png",record_pos=(-0.004,-0.419),resolution=(1080,2400))),
vector=[-0.0278, -0.2121])
keyevent("HOME")
可以看到其内容和 AirtestIDE 中自动生成的代码基本一致,不同之处在于这里用一个Template对象代替了图片,该对象包含图片名、位置、分辨率三个参数,而AirtestIDE对图片进行了可视化,使其更加直观。
我们可以直接使用Python环境运行这个脚本吗?可以,但是需要在代码开始的 auto_setup(__file__) 和 keyevent("HOME") 之间添加一行代码 init_device()。调用 init_device 方法的作用完成一些手机的初始化配置,不做这一步可能会有错误。运行脚本之后,会产生同样的效果一一手机被自动化控制执行了一系列操作,同时控制台输出对应的操作日志,如图 12-73 所示。
图12-73 控制台输出的操作日志
至此,基于图像识别来自动化控制手机App的流程就介绍清楚了。
Airtest的相关API
上面的内容仅展示了 Airtest Python 库的冰山一角,本节列举一些它提供的便捷 API。从刚才添加的 init_device 方法说起,这个方法就是用来连接设备并初始化一些连接对象的。如果设备没有初始化则会先初始化设备,并把初始化后的设备当作当前设备。这个方法定义如下:
def init_device(platform="Android",uuid=None,**kwargs):
用法示例如下:
device=init_device('Android') print(device)
示例代码的运行结果如下:
<airtest.core.android.android.Androidobjectatox1o18f3a58>
可以发现返回结果是一个Android对象。这个Android对象实际上属于airtest.core.android包,继承自airtest.core.device.Device类,与之并列的对象还有airtest.core.ios.ios.Ios、airtest.core. linux.linux.Linux、airtest.core.win.win.Windows等。这些对象都有一些用来操作设备的API,下面我们以这个Android对象的API为例总结一下。
-
get_default_device:获取默认设备。
-
uuid:获取当前设备的 UUID。
-
list_app:列举设备上的所有 App。
-
pathapp:打印出某个App的完整路径
-
checkapp:检查某个App是否在当前设备上。
-
startapp:启动某个App。
-
start_app_timing:启动某个App,并计算启动时间。
-
stop_app:停止某个App。
-
clear_app:清空某个App的全部数据。
-
installapp:安装某个App
-
install_multiple_app:安装多个App
-
uninstall_app:卸载某个App。
-
snapshot:获取屏幕截图。
-
shell:获取 adbshell 命令的执行结果。
-
keyevent:执行键盘操作。
-
wake:唤醒当前设备。
-
home:点击HOME键。
-
text:向设备输入内容
-
touch:点击屏幕上的某处。
-
double_click:双击屏幕上的某处。
-
swipe:滑动屏幕,由一点滑动到另外一点。
-
pinch:通过手指的捏合操作放大或缩小手机屏幕。
-
logcat:记录日志。
-
getprop:获取某个特定属性的值。
-
get_ip_address:获取IP地址。
-
get_top_activity:获取当前 Activity。
-
get_top_activity_name_and_pid:获取当前 Activity 的名称和进程号。
-
get_top_activity_name:获取当前 Activity 的名称。
-
is_keyboard_shown:判断当前是否显示键盘了。
-
is_locked:判断设备是否锁定了。
-
unlock:解锁设备。
-
display_info:获取当前的显示信息,如屏幕宽高等。
-
get_display_info:同 display_info。
-
get_current_resolution:获取当前设备的分辨率。
-
get_render_resolution:获取当前渲染的分辨率。
-
start_recording:开始录制。
-
stop_recording:结束录制。
-
adjust_all_screen:调整屏幕的适配分辨率。
了解了这些 API 的功能之后,下面用一个实例感受一下它们的用法:
from airtest.core.android import Android
from airtest.core.api import *
import logging
logging.getLogger("airtest").setLevel(logging.WARNING)
device: Android = init_device('Android')
is_locked = device.is_locked()
print(f'is_locked:{is_locked}')
if islocked:
device.unlock()
device.wake()
app_list = device.list_app()
print(f'app list {app_list}')
uuid = device.uuid
print(f'uuid {uuid}')
display_info = device.get_display_info()
print(f'display info {display_info}')
resolution = device.get_render_resolution()
print(f'resolution {resolution}')
ip_address = device.get_ip_address()
print(f'ip address {ip_address}')
top_activity = device.get_top_activity()
print(f'top activity {top_activity}')
is_keyboard_shown = device.is_keyboard_shown()
print(f'is keyboard shown {is_keyboard_shown}')
这里我们调用API获取了设备的一些基本状态,运行结果如下:
从结果可以看到,借助一些常用的API,我们就完成了唤醒手机和获取ApP列表、UUID、显示器信息、分辨率、IP地址、当前运行的Activity、是否显示键盘等一系列操作。
获取当前设备
Airtest 中有一个全局变量 G, 它的 DEVICE 属性代表当前的设备对象。直接调用 device 方法即可获取当前设备,该方法定义如下:
def device():
return G.DEVICE
获取所有设备
获取所有设备的方法如下:
from airtest.core.android import Android
from airtest.core.api import *
print(G.DEVICE_LIST)
uri = 'Android://127.0.0.1:5037/emulator-5554'
device: Android = connect_device(uri)
print(G.DEVICE_LIST)
运行结果如下:
[] [<airtest.core.android.android.Android object at Oxiobao3978>]
DEVICE_LIST 是一个列表,元素是 Airtest 当前已经连接的设备。需要注意一点,在没有调用 connect_device 方法时,DEVICE_LIST 是空的,调用 connect_device 方法之后,DEVICE_LIST 中会自动添加已经连接的设备。
执行命令行
可以调用 shell 方法,传入 cmd 参数来执行命令行,该方法定义如下:
@logwrap
def shell(cmd):
return G.DEVICE.shell(cmd)
直接调用 adb 命令就可以了,例如执行如下命令获取内存信息:
from airtest.core.api import *
uri = 'Android://127.0.0.1:5037/emulator-5554'
connect_device(uri)
result = shell('cat /proc/meminfo')
print(result)
运行结果如下:
MemTotal: 3627908 kB MemFree: 2655560 kB MemAvailable: 2725928 kB Buffers: 3496 kB Cached: 147472 kB --- DirectMap4k: 16376 kB DirectMapM: 892928 kB
这样我们就成功获取到了设备的内存信息描述。
启动和停止App
调用设备的 start_app 和 stop_app 方法,然后传入 App 的包名,即可启动和停止这个 App,两个方法的定义如下:
@logwrap
def start_app(package, activity=None):
G.DEVICE.start_app(package, activity)
@logwrap
def stop_app(package):
G.DEVICE.stop_app(package)
用法示例如下:
from airtest.core.api import *
uri = 'Android://127.0.0.1:5037/emulator-5554'
connect_device(uri)
package = 'com.tencent.mm'
start_app(package)
sleep(10)
stop_app(package)
这里我指定 package 为微信的包名,然后调用 start_app 方法启动了微信,等待 10 秒后,调用 stop_app 方法停止了微信运行。
安装和卸载 App
调用设备的 install 和 uninstall 方法,前者传入 App 的保存路径,后者传入 App 的包名,即可安装和卸载对应的 App,两个方法的定义如下:
@logwrap
def install(filepath, **kwargs):
return G.DEVICE.install_app(filepath, **kwargs)
@logwrap
def uninstall(package):
return G.DEVICE.uninstall_app(package)
截图
调用 snapshot 方法获取屏幕截图,可以通过参数设置存储截图的文件名称和图片的质量等。该方法的声明如下:
def snapshot(filename=None, msg="", quality=ST.SNAPSHOT_QUALITY)
用法示例如下:
from airtest.core.api import *
uri = 'Android://127.0.0.1:5037/emulator-5554'
connect_device(uri)
package = 'com.tencent.mm'
start_app(package)
sleep(3)
snapshot('weixin.png', quality=30)
运行这段示例代码后,当前目录下会生成一个名为 weixin.png 的图片,如图 12-74 所示。
图12-74 生成微信截图
唤醒和回到首页
调用设备的 wake 和 home 方法,即可唤醒 App 和回到首页,两个方法定义如下:
@logwrap
def wake():
G.DEVICE.wake()
@logwrap
def home():
G.DEVICE.home()
这两个方法不需要任何参数,直接调用即可。
点击屏幕
调用 touch 方法点击屏幕,可以传入要点击的图片或者绝对位置,还可以指定点击次数,该方法声明如下:
@logwrap
def touch(v, times=1, **kwargs)
例如我的手机屏幕现在如图 12-75 所示。
图12-75 当前的手机屏幕
截一张图片,如图 12-76 所示。
图12-76 从手机屏幕上截一张图片
然后把这张图片声明成一个 Template 对象传入 touch 方法:
from airtest.core.api import *
uri = 'Android://127.0.0.1:5037/emulator-5554'
connect_device(uri)
touch(Template('tpl.png'))
运行这段代码,设备启动后,就会识别这张图片所在的位置,然后点击。这个例子是传入了要点击的图片,我们也可以传入要点击的绝对位置,示例如下:
from airtest.core.api import *
uri = 'Android://127.0.0.1:5037/emulator-5554'
connect_device(uri)
home()
touch((400, 600), times=2)
另外,touch 方法完全等同于 click 方法。如果要双击,还可以调用 double_click 方法,其参数和 touch 方法一样。
滑动
调用 swipe 方法滑动屏幕,可以传入起始位置和结束位置,两个位置都可以是图片或者绝对位置,该方法的声明如下:
@logwrap
def swipe(v1, v2=None, vector=None, **kwargs)
例如这时候我想让手机控制向右滑动即可实现如下代码:
from airtest.core.api import *
uri = 'Android://127.0.0.1:5037/emulator-5554'
connect_device(uri)
home()
swipe((200, 300), (900, 300))
放大缩小
放大缩小调用的是 pinch 方法,可以通过 in_or_out 参数指定放大还是缩小,还可以指定手指捏合的中心位置点和放大缩小的比例。该方法的声明如下:
@logwrap
def pinch(in_or_out='in', center=None, percent=0.5)
用法示例如下:
from airtest.core.api import *
uri = 'Android://127.0.0.1:5037/emulator-5554'
connect_device(uri)
home()
pinch(in_or_out='out', center=(300, 300), percent=0.4)
这里我们调用了 pinch 方法,并且指定了放大动作 out,同时指定了捏合中心点和放大的比例。运行代码之后手机上便会模拟执行此操作。
基于 Poco 的 UI 组件自动化控制
在某些场景下,基于图像识别来自动化控制 App 是比较方便的,但也存在一定的局限性。例如图像识别的速度可能并不快,以及 App 中的某些 UI 如果更换了,就无法和之前截的图片匹配成功,这些很可能会影响自动化测试的流程。
所以,这里再介绍一个基于 Poco 的 UI 组件自动化控制,说白了就是基于 UI 名称和属性选择器的自动化控制,有点类似于 Appium、Selenium 中的 XPath。
新建一个脚本,命名为 script2.air,右侧同样连接好手机,然后点击左侧“Poco辅助窗”的下拉菜单,选择“Android”,如图 12-77 所示。
图12-77 选择“Android”
这时 AirtestIDE 会提示我们更新代码,点击确定后脚本中自动添加了如下代码:
from poco.drivers.android.uiautomation import AndroidUiAutomationPoco poco = AndroidUiAutomationPoco(use_airtest_input=True, screenshot_each_action=false)
意思就是导入了 Poco 包的 AndroidUiAutomationPoco 模块,然后声明了一个 poco 对象。接下来就可以通过 poco 对象选择一些内容了。例如点击左侧 UI 组件树中的“Dianping”节点,会发现右侧手机屏幕上对应的 App 高亮显示了,在“Log 查看窗”中还可以看到该节点对应的所有属性,如图 12-78 所示。这个操作有点像在浏览器开发者工具里选取网页源代码,其中的 UI 组件树就相当于网页里的 HTML DOM 树。
图12-78 点击“Dianping”节点
直接双击 “Dianping” 节点, script2.air 脚本中便会出现对应的代码:
poco("Dianping")
这是什么意思呢? 为什么这么写? 我们来看一下 poco 这个 API, 它是一个 AndroidUiAutomationPoco 对象, 查阅 Poco 官方文档 https://poco.readthedocs.io/zh_CN/latest/source/poco.pocofw.html 可以得知, 其用法类似于这样:
poco = AndroidUiAutomationPoco(...)
close_btn = poco('close', type='Button')
会发现, poco 本身就是一个对象, 但可以直接调用 UI 组件的名称, 这归根结底是因为实现了一个 __call__ 方法:
def call(self, name=None, **kw):
if not name and len(kw) == 0:
warnings.warn("Wildcard selector may cause performance trouble. Please give at least one condition to shrink range of results")
return UIObjectProxy(self, name, **kw)
可以看到 __call__ 方法的第一个参数就是 name,其他参数都以 kw 的形式传入,可以任意指定,最后返回一个 UIObjectProxy 对象。
回过头来,我们看看 “Dianping” 这个节点的 name 参数值是什么,这个在 “Log 查看窗” 内显示得很清楚,如图 12-79 所示。
图12-79 “Dianping” 节点的 name 参数
可以看到其 name 值就是 Dianping,而且整个 UI 组件树中没有与其同名的节点,于是可以直接调用 poco('Dianping') 选取这个节点。当然,也可以任意指定 poco 的其他参数,例如:
poco("Dianping', type='android.widget.TextView')
poco('Dianping', text='Dianping')
poco('Dianping', text='Dianping', desc='Dianping')
这 3 种写法都能选取同样的节点。
刚才说到,call 方法会返回一个 UIObjectProxy 对象,现在我们来看一下这个对象的实现,其 API 链接为 https://poco.readthedocs.io/zh_CN/latest/source/poco.proxy.html 。从中可以看到实现了 __getitem__、__iter__、__len__、child、children、offspring 等方法,所以可以实现链式调用、索引操作和循环遍历。
其中一些比较常用的方法如下。
-
child: 选择子节点。第一个参数是 name, 即 UI 组件的名称,如 android.widget.linearlayout 等,还可以额外传入一些属性辅助选择,其返回结果也是 UIObjectProxy 对象。
-
parent: 选择父节点。该方法无须传入参数,可以直接返回当前节点的父节点,返回结果同样是 UIObjectProxy 对象。
-
sibling: 选择兄弟节点。第一个参数是 name, 即 UI 组件的名称,同样可以额外传入一些属性辅助选择,返回结果依然是 UIObjectProxy 对象。
-
click、rclick、double_click、long_click: 分别是点击、右击、双击、长按。UIObjectProxy 对象可以直接调用这几个方法,参数 focus 用于指定点击的偏移位置,sleep_interval 用于指定点击完成后等待的时间(单位为秒)。
-
swipe: 滑动操作。参数 direction 用于指导滑动方向, focus 用于指导滑动焦点的偏移量, duration 用于指导完成滑动所需的时间。
-
wait, wait_for_appearance: 等待节点出现。参数 timeout 用于指定最长等待时间。
-
attr: 获取节点的属性值。参数 name 用于指定要获取的属性名, 如 visable、text、type、pos、size 等。
-
get_text: 获取节点的文本值。这个方法非常有用, 可以获取某个文本节点内部的文本数据。
下面调用一下 click 方法, 将代码改写为:
poco("Dianping").click()
这样就可以选中并点击 “Dianping” 节点了。点击之后, 就进入了 “大众点评” 这个 App, 然后可以设置一下等待条件, 等待某个节点加载出来, 证明已经进入 App, 例如设置图 12-80 框选的部分。
图 12-80 设置等待条件
通过左侧的 Poco Pause 按钮, 可以在右侧屏幕上点击要查看的位置, 左侧的 UI 组件树会自动定位到对应节点, 同时“Log 查看窗”会实时显示节点信息。双击左侧 UI 组件树中定位到的节点, script2.air 脚本中又会增加如下内容:
poco("android.widget.LinearLayout").offspring("android:id/tabhost").offspring("com.dianping.v1:id/id_main _fragment").offspring("com.dianping.v1:id/main_listview").offspring("com.dianping.v1:id/home_category_layout")
可以看到其中包含一系列链式调用, 通过连续调用 offspring 方法选取了一层层节点。
|
刚才我们分析 API 的时候也了解到, UIObjectProxy 对象实现了 |
由于 wait_for_appearance 方法返回的结果依然是 UIObjectProxy 对象, 因此可以继续调用相关方法, 例如 wait_for_appearance 方法。等待结果加载出来, 代码改写如下:
poco("android.widget.LinearLayout").offspring("android:id/tabhost").offspring("com.dianping.v1:id/id_main _fragment").offspring("com.dianping.v1:id/main_listview").offspring("com.dianping.v1:id/home_category_lay out").wait_for_appearance(10)
这里往 wait_for_appearance 方法的参数中传入了 10, 代表最长等待 10 秒, 如果超出 10 秒还没 有加载出结果, 就报错。
同理, 可以选中中间菜单栏的位置, 然后向上滑动, 代码如下:
poco("android.widget.LinearLayout").offspring("android:id/tabhost").offspring("com.dianping.v1:id/id_main_fragm ent").offspring("com.dianping.v1:id/tab_layout").child("android.widget.LinearLayout").swipe([0, -0.1])
这里往 swipe 方法的参数中传入了一个列表, 代表滑动方向, 列表的第一个元素代表横向偏移量, 第二个元素代表纵向偏移量, 由于我们要向上滑动, 因此第一个元素是 0, 第二个元素取了 -0.1。
最后在代码的开头和结尾添加键盘事件, 回到首页, 整理代码如下:
from airtest.core.api import *
from poco.drivers.android.uiautomation import AndroidUiAutomati onPoco
poco = AndroidUiAutomati onPoco(use_airtest_input=True, screenshot_each_action=False)
auto_setup(*file*)
keyevent("HOME")
poco("Dianping").click()
poco("android.widget.LinearLayout").offspring("android:id/tabhost").offspring("com.dianping.v1:id/id_main _fragment").offspring("com.dianping.v1:id/main_listview").offspring("com.dianping.v1:id/home_category_lay out").wait_for_appearance(10)
poco("android.widget.LinearLayout").offspring("android:id/tabhost").offspring("com.dianping.v1:id/id_main _fragment").offspring("com.dianping.v1:id/tab_layout").child("android.widget.LinearLayout").swipe([0, -0.1], duration=1)
keyevent("HOME")
运行这段代码, 之后手机上的操作和用 Airtest 实现的一样, 都是先进入首页, 然后点击大众点评 的图标进入 App。等待相应内容加载出来, 之后向上滑动, 最后返回首页。
可以看出, Poco 使用起来灵活度更高。可以选定某个 UI 节点, 然后调用其各种操作方法执行一 些特定的动作, API 设计也更灵活, 支持链式调用, 功能非常强大。
更多的 API 的使用方法可以直接参考官方文档 https://poco.readthedocs.io/zh_CN/latest/source/poco.proxy.html , 掌握了这些 API, 就可以更加得心应手地使用 Poco 控制 App 操作, 做爬虫自然也变得易如反掌。