自动化工具 python 版 appcrawler

off_wind · 2020年10月20日 · 最后由 TangSir61 回复于 2021年03月11日 · 5027 次阅读

项目地址:https://gitee.com/hyt777/app-py-crawler

appPyCrawler

python+appcrawler = appPyCrawler

基于 airtest+poco 的 app 自动遍历工具

该框架的核心思路来自于 思寒大佬做的appcrawler

但由于大佬是基于 Scala 编写的,对于我来说运行起来有点不方便

并且蒙阴与目前 AirTest 下 poco 框架的发展,相比于 appium 来说稳定了许多

因此有了编写一个 airtest+poco 的基于 python 的 app 爬虫测试框架

安装依赖

python 版本为 3.6

airtest 安装
pip install airtest

配置文件

config.yml

devices: # airtest 链接设备的字符串
  - Android://127.0.0.1:5037/127.0.0.1:62025?cap_method=JAVACAP

start_app: cn.ehanghai.android.hex # 启动的应用的packa

start_action_list: # 启动应用后的动作列表,只在应用启动后按顺序执行一次。
  - action: wait # 等待
    info: 10
  - action: click # 点击,
    info:
      name: cn.ehanghai.android.hex:id/statement_agree
  - action: swipe # 滑动,info可以选择 left,right,也可以直接写点(本质是 pos1,pos2 = eval("(0.8, 0.5), (0.1, 0.5)"))
    info: (0.8, 0.5), (0.1, 0.5)
  - action: swipe # 第二次滑动 目前没有做次数设置,如果动作要重复多次,那就复制粘贴几次
    info: (0.8, 0.5), (0.1, 0.5)

select_list: # 筛选列表,首先对控件进行初步筛选
  - enabled: true
    visible: true
    type: android.widget.ImageView
  - enabled: true
    visible: true
    type: android.widget.TextView
  - enabled: true
    visible: true
    type: android.widget.ImageButton
  - enabled: true
    visible: true
    editalbe: true
    type: android.widget.EditText
  - enabled: true
    visible: true
    checkable: true
    type: android.widget.CheckBox


first_list: # 优先列表,出现以下控件优先点击
  - name: cn.ehanghai.android.hex:id/cancle_tv
  - name: cn.ehanghai.android.hex:id/iv_map_close

trigger_dict: # 遇到对应的控件,触发对应的操作
  修改昵称:
    target:
      name: cn.ehanghai.android.hex:id/change_name
    trigger:
      action: send_key
      text: _(:3」∠❀)_
      time: 1

back_list: # 返回按钮,一般最后点击
  - name: cn.ehanghai.android.hex:id/iv_back
  - name: 转到上一层级
  - text: 确定
  - text: 保存

black_list: # 黑名单
  - text: 相册

运行

``
python main.py


### 运行逻辑

程序注意的运行逻辑如下
```python
class Crawler:
    def __init__(self):
        self.config = yaml.load(open("config.yml", encoding="utf-8"))
        self.driver = Driver(self.config)

        self.init_actions = [  # 初始化用的action
            StartAppInitAction(),  # 启动app设置
            StartActionInitAction(),  # app启动后的动作设置
        ]

        self.actions = [SelectAction(),  # 初步进行控件筛选
                        FilterAction(),  # 对特定的控件进行筛选
                        BlackAction(),  # 将在黑名单中的控件排除
                        FirstAction(),  # 优先点击的控件,执行run后续不执行
                        TriggerAction(),    # 触发器,遇到特定控件进行特定的操作,执行run后续不执行
                        BackHeadAction(),   # 将返回类型的按键单独取出来
                        NormalAction(),     # 一般的控件,每个控件默认点击一次,执行run后续不执行
                        BackEndAction(),    # 前面的都没有执行,那么点击返回按钮
                        ]


    def crawler(self):
        self.init()

        while self.run():
            pass

    def init(self):
        for action in self.init_actions:
            action.run(self.config, self.driver)

    def run(self):
        for action in self.actions:
            if not action.entrance(activity, self.config, self.driver):
                return True
        return False

主要是按顺序执行 action,可以随意插入 action 来扩充功能
后续是希望可以通过配置文件来动态配置 action

新人第一次发帖,希望大家多多提建议
目前还只是 demo 能跑的阶段,需要改的地方还很多。
我能力有限希望大家也来帮忙

共收到 26 条回复 时间 点赞

看 yaml 不支持 ios?

目前的确不支持。但 poco 和 airtest 应该都是支持 IOS 的。技术上来说应该是可以做的

执行应用和初始步骤没有配置的吗

TD 回复

现在更新了个版本,加了执行应用和初始步骤设置

结果能够输出遍历的所有菜单么

devices: # airtest 链接设备的字符串

  • Android://127.0.0.1:5037/127.0.0.1:62025?cap_method=JAVACAP
    请问这个字段怎么获取的?
tester20198 回复

这个字段是可以理解为 3 部分,本地的 adb 端口 + Android 设备的连接端口,你也可以使用设备的标识符代替,使用 adb devices 获取 + 连接的方法:使用 JavaCap。你也可以看一下 Airtest 的文档。

tester20198 回复

设备连接字符串如何编写
在刚才的命令行中使用的 --device 参数,传入的是一个设备字符串,以安卓设备为例,字串完整定义如下:

Android://<adbhost[localhost]>:<adbport[5037]>/<serialno>

其中,adbhost 是 adb server 所在主机的 ip,默认是本机 127.0.0.1,adb port 默认是 5037,serialno 是 android 手机的序列号。

这里提供一些常见的填写范例供大家参考:

#什么都不填写,会默认取当前连接中的第一台手机
Android:///
# 连接本机默认端口连的一台设备号为79d03fa的手机
Android://127.0.0.1:5037/79d03fa
# 用本机的adb连接一台adb connect过的远程设备,注意10.254.60.1:5555其实是serialno
Android://127.0.0.1:5037/10.254.60.1:5555

# 模拟器等特殊设备、使用了连接参数时:
# 模拟器连接,勾选了Use javacap模式
Android://127.0.0.1:5037/127.0.0.1:7555?cap_method=JAVACAP
# 所有的选项都勾选上之后连接的设备,用&&来连接多个参数字符串
Android://127.0.0.1:5037/79d03fa?cap_method=JAVACAP&&ori_method=ADBORI&&touch_method=ADBTOUCH

更多的可以参考这个文档: https://airtest.doc.io.netease.com/tutorial/4_Android_automated_testing_one/

清风上灬 回复

目前还没有做这方面的功能

遍历的算法是什么

咸鱼菜鸡 回复

遍历的算法实际上的本质是给界面上的每个控件进行筛选和分组

首先使用 poco dump 下所有控件的结构信息
poco 的 dump 的数据结构是 json,appium 使用的原生的 dump 结构是 xml

  "children": [
        茫茫多的子结构
  ]
  "name": "android.widget.FrameLayout",
  "payload": {
    "checkable": false,
    "pos": [
      0.5,
      0.5
    ],
    "scrollable": false,
    "boundsInParent": [
      1,
      1
    ],
    "selected": false,
    "anchorPoint": [
      0.5,
      0.5
    ],
    "size": [
      1,
      1
    ],
    "zOrders": {
      "global": 0,
      "local": 0
    },
    "editalbe": false,
    "visible": true,
    "enabled": true,
    "checked": false,
    "focused": false,
    "touchable": false,
    "package": "cn.ehanghai.android.hex",
    "name": "android.widget.FrameLayout",
    "scale": [
      1,
      1
    ],
    "dismissable": false,
    "longClickable": false,
    "type": "android.widget.FrameLayout",
    "focusable": false
  }
}

首先把 dump 下拉的控件(或者说节点),都存在一个 list 里面

然后把这个 list 塞到 actons 里面去过滤

actions = [SelectAction(),  # 初步进行控件筛选
        FilterAction(),  # 对特定的控件进行筛选
        BlackAction(),  # 将在黑名单中的控件排除
        FirstAction(),  # 优先点击的控件,执行run后续不执行
        TriggerAction(),    # 触发器,遇到特定控件进行特定的操作,执行run后续不执行
        BackHeadAction(),   # 将返回类型的按键单独取出来
        NormalAction(),     # 一般的控件,每个控件默认点击一次,执行run后续不执行
        BackEndAction(),    # 前面的都没有执行,那么点击返回按钮
        ]

这堆 action 相当于一层层的漏斗,筛选并且分类控件
action 的执行过程如下图

可以扩充 action 来增加遍历的逻辑

不可能有可以适用于所有 app 的遍历逻辑,所以我倾向于可以方便的扩充 action,
来对各个不同的 app 进行对应的配置

off_wind 回复

我看了一下你这个算法 你现在是一个页面的控件放到一个 list 里面根据优先级去点击 但是点击之后应该是进入下一个页面吧 难道你现在是只在当前页面遍历吗?

咸鱼菜鸡 回复

每个页面根据 activity 标识,建立一个页面对象,当前页面的控件存在对应页面的 list 里

相当于不同页面有不同的命名空间

进入到新页面后,按照同样的逻辑进行筛选和分类,且执行操作

off_wind 回复

根据 activity 而不是根据上层的控件吗

咸鱼菜鸡 回复

没太懂你的意思…你的想法是什么?

off_wind 回复

如果根据 activity 来的话 这一层的控件的作用你是无法和上一层的操作对应起来的吧

咸鱼菜鸡 回复

是的。目前的逻辑就是把当前看到的控件按照规则遍历,不会去管当前界面是怎么进入的和控件之间的逻辑关系。

off_wind 回复

这样是要求每个页面 activity 不一样吧 这个能做到吗

你好,我在使用过程中遇到 2 个问题:

  1. 控件点击间隔平均 10s 一次,比较慢,这个是因为 dump 页面源比较慢么
  2. 点击了 10 个控件就直接结束测试了,只遍历了 3 个页面,只点击了页面中的小部分控件
easyHi 回复

1、我只有在刚启动 app 的时候,设置了 10 秒的等待启动的时间。之后遍历控件的时候,并没有设置操作延迟。我这里使用基本 3 秒点击一次。可能和设备性能有关?😳
2、你可以用 airtest IDE 查看一下那些没有被点击的控件的属性是什么,和配置文件中的属性对比一下。
只有满足 select_list 下面条件的控件才会被解析。

咸鱼菜鸡 回复

一般不同页面的 activity name 是不一样的,但也有特殊页面或者 H5 页面,activity name 一样但内容不一样。那你可以通过页面上独有的特殊控件给 Activity 对象打标签

easyHi 回复

现在更新了下版本,你可以试试看。之前控件唯一指纹定义的的确有问题

off_wind 回复

等啥时候有空研究一下你这个。哈哈

👍 这个真的很棒,学习了

针对 tab 页的遍历有解决么?

对于混淆的 id 如何去处理配置文件呢

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册