APP 自动化那些事儿:高效定位, 像解析网页一样定位 APP

日期 : 2020-08-30 10:15:38作者 : 中原锦绣

原文地址:

https://www.lookcos.cn/?p=816

前言:

随着移动互联网的兴起,国内早已是 APP 的天下,虽然主流的大厂都有 web 端,但是不可否认,很多好数据只有 APP 端才有。

本文只为多提供一种可能,萝卜咸菜,各有所爱。

天下武功,为快不破:

工欲善其事,必先利其器,对工具的选择就是对效率的选择。

久闻 Appium 大名,但是经过上手一番才发现,并没有描述的那么美好,启动 appium 就得十几秒,偶尔还会报错、定位缓慢、操作都是重量级等等。。。

Appium 还需要配合 Java SDK 与 Android SDK,安装安卓 SDK 甚至还得安装 Android studio (尽管也可以不安装,但是我相信,大多数教程还是教你安装)。

那么?如题?到底能不能像解析网页一样解析 APP 页面呢?答案是:从某种意义上来说是可以的!

道,可道:

Appium 定位方法比较多,比较常用的有通过布局 id 定位,或者 xpath 定位。而我们如何查看 APP 当前的布局信息呢?

方法很多,appium 自带和 Android studio 中都是用到了 UI Automator 这个工具。

事实上,安卓系统中自带的就有 uiautomator 这个工具,我们可以利用 adb 执行它的 dump 命令导出当前的布局文件,然后利用 lxml 库分析元素位置等信息。

小试牛刀:

首先 window 用户可以移步至 https://adbshell.com/downloads,下载 adb 工具并配置好环境变量。 
接着跟我执行如下命令:


# adb 连接安卓,( mumu 模拟器默认的地址与端口)

adb connect 127.0.0.1:7555    

# 利用 uiautomator 工具的 dump 命令,将当前 app 界面的布局文件导出至 sdcard 下,保存在 ui.xml 文件中

adb shell uiautomator dump /sdcard/ui.xml

#接着把文件导出到电脑本地

adb pull /sdcard/ui.xml E:\ui

打开可能会发现都在一行,不利于阅读,我们用在线工具美化一下:

不难发现,resource-id,text 、class 这些都出来了。

雄关漫道真如铁,而今迈步从头越:

到了这一步熟悉了吧,祭出我们的 lxml 库来解析出我们想要的数据,取出对应元素的 bounds 值,我们就能获得该元素的坐标,进而点击它。如,bounds="[198,127][292,164]",这个 bounds 表示的是该元素的块区域坐标。

python 使用 lxml 解析 ui.xml

# -*- coding: utf-8 -*-

from lxml import etree

xml = etree.parse("ui.xml")
soup = xml.getroot()
res = soup.xpath('//node[@text="热榜"]')[0]
print(res.get("text"), res.get('bounds'))

 #对应的 node <node index="0" text="热榜" resource-id="" class="android.widget.TextView" package="com.coolapk.market" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[218,127][272,164]" /></node>

这是我获取酷安首页的布局文件,下面是打印结果:

短短几行熟悉的代码,我们就获得了 bounds 的值,也即是 热榜这个对应的坐标范围:

通过实际查看发现,确实是在这个范围内。

我们来写一个函数,解析它,并返回一个合法坐标

# -*- coding: utf-8 -*-

import os
import time
import random
from lxml import etree

os.system(r"adb connect 127.0.0.1:7555")
os.system(r"adb shell uiautomator dump /sdcard/ui.xml")
time.sleep(2.5)
os.system(r"adb pull /sdcard/ui.xml E:\ui")

# 解析 bounds
def parse_bounds(bounds):
    bounds = bounds.replace("[", " ").replace(",", " ").replace("]", " ").replace("  ", " ").strip(" ").split(" ")
    x = random.randint(int(bounds[0]), int(bounds[2]))
    y = random.randint(int(bounds[1]), int(bounds[3]))
    return str(x), str(y)

xml = etree.parse("ui.xml")
soup = xml.getroot()
res = soup.xpath('//node[@text="热榜"]')[0]
x, y = parse_bounds(res.get('bounds'))
# 点击
os.system(r"adb shell input tap {0} {1}".format(x, y))

上动图演示:

结束语:

实际的应用中还是很有用的,比如抖音此类搜索结果,不同的关键词有不同的视频量,有一些关键词我们需要扒拉几百下,而有的关键词只需要几下。

这个时候我们就可以通过这种方式来判断当前页面是否出现了“没有更多了”诸如此类的字眼。

看看,根据 id 、描述、text 等信息定位并不难,而且效率与稳定性更是没得说。

本文仅作为演示,算是提供一种思路吧!

标签 :