随着移动互联网的兴起,国内早已是 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
到了这一步熟悉了吧,祭出我们的 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 等信息定位并不难,而且效率与稳定性更是没得说。
本文仅作为演示,算是提供一种思路吧!
喜欢研究爬虫、web 等技术,欢迎来踩踩。
https://www.lookcos.cn/
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.