AppCrawler Appcrawler 参数实验经验

老马 · October 25, 2017 · Last by 小肉包 replied at December 03, 2019 · 2750 hits

配置文件内容

存放路径 appcrawler/conf/acp.yml

配置内容

---
#插件列表
#pluginList:
#- "com.testerhome.appcrawler.plugin.FlowDiff"
#- "com.testerhome.appcrawler.plugin.ProxyPlugin"
#- "com.testerhome.appcrawler.plugin.TagLimitPlugin"
#- "com.testerhome.appcrawler.plugin.ReportPlugin"
reportTitle: AppCrawler-acp4.7
currentDriver: "android"
# 结果目录
resultDir: ""
# 最大运行时间
maxTime: 10800
logLevel: "TRACE"
#结果报告是否展示没有遍历被取消的控件
showCancel: true
#特定元素的tag布局层级完全一样时的遍历最大值
tagLimitMax: 4
#是否截图
saveScreen: true
screenshotTimeout: 20

# appium的capability通用配置
capability:
  newCommandTimeout: 120
  launchTimeout: 120000
  platformVersion: ""
  platformName: ""
# Appium是否需要自动安装和启动应用。默认值true  
  autoLaunch: "true"
# 直接转换到 WebView 上下文。 默认值 false    
  autoWebview: "false"
# 不要在会话前重置应用状态。默认值false。  
  noReset: "false"

# android专属配置 最后会和capability合并
androidCapability:
  deviceName: "192.168.58.101:5555"
  appPackage: "com.sinacp.ggaicai"
  appActivity: "com.aicai.pluginhost.activity.MainActivity"
  app: "/home/cmd/appcrawler/acp4.7p.apk"
  # 你想使用的自动化测试引擎 可以是 uiautomator2  macaca 等 默认appium
  automationName: appium
  appium: "http://127.0.0.1:4730/wd/hub"
  macaca: "http://127.0.0.1:3456/wd/hub"
  fullReset: false
  noReset: true
  reuse: 3
  #以下为重置手机输入法为appium输入法
  unicodeKeyboard: true
  resetKeyboard: true


iosCapability:
  deviceName: "iPhone 6 Plus"
  bundleId: "com.sinacp.ggaicai"
  screenshotWaitTimeout: "10"
  platformVersion: "9.3"
  autoAcceptAlerts: "true"
  app: "/home/cmd/appcrawler/acp4.7p.apk"
  appium: "http://127.0.0.1:4730/wd/hub"

#appWhiteList:
#- android
#- com.shafa.market

# 用来确定url的元素定位xpath 他的text会被取出当作url因素
#defineUrl:


# 设置一个起始url和maxDepth, 用来在遍历时候指定初始状态和遍历深度
#baseUrl:

# 默认的最大深度10, 结合baseUrl可很好的控制遍历的范围
maxDepth: 10

# 是否是前向遍历或者后向遍历
headFirst: true

# 是否遍历WebView控件
enterWebView: true

# url黑名单.用于排除某些页面
#urlBlackList:

#urlWhiteList:
#- ".*Main.*"

# 后退按钮标记, 主要用于iOS, xpath
#backButton:

# 优先遍历元素特征列表
firstList:
#- "//*[contains(@resource-id,'com.acp.main:id/tvBottomTab4')]//android.widget.TextView"
- "//*[contains(@resource-id,'com.acp.main:id/tvBottomTab3')]//android.widget.TextView"


# 默认遍历元素特征列表 需要注意的是firstList和lastList指定的元素必须包含在selectedList中
#selectedList:

# 最后遍历元素特征列表
#lastList:

# 黑名单列表 matches风格, 默认排除内容是2个数字以上的控件
blackList:

- "//*[contains(@resource-id,'com.acp.main:id/tvBottomTab4')]//android.widget.TextView"
- //*[@resource-id='com.acp.main:id/module_tj1_name']    
- //*[@resource-id='com.acp.main:id/module_tj1_description']

# 引导规则. name, value, times三个元素组成
triggerActions:
- action: "yourname"
  xpath: "//*[@resource-id='com.sinacp.ggaicai:id/etUserName']"
  times: 1
- action: "yourpasswd"
  xpath: "//*[@resource-id='com.sinacp.ggaicai:id/etPwd']"
  times: 1

遍历控制需求

进入首页后,直接优先遍历社区界面,遇到社区内登录就执行登录输入,遍历完社区退到首页不进入天天盈球界面。


部分参数用法解释:

# 优先遍历元素特征列表

firstList:

- "//*[contains(@resource-id,'com.acp.main:id/tvBottomTab3')]//android.widget.TextView"

该段配置,是优先点击首页-》社区元素 进入社区页

tagLimitMax: 4

该值在进入社区页内,会看到效果。大家看到社区页内,有这么几个元素 首页 热点 聊吧 名人 专题 一共横向排列的 5 个元素。
如果 tagLimitMax: 4 值为 4 的话,就会只点击到第 4 个 “名人"为止,不点击第 5 个 "专题"。

官方的解释:智能判断列表和其他的相似布局元素.只遍历前 4 个相似空间. 适用于微博这种无限刷新的列表. 用于节省时间. 原理是利用特定元素的 tag 布局层级是否完全一样.

# 引导规则. name, value, times三个元素组成

triggerActions:

- action: "yourname"

  xpath: "//*[@resource-id='com.sinacp.ggaicai:id/etUserName']"

  times: 1

- action: "yourpasswd"

  xpath: "//*[@resource-id='com.sinacp.ggaicai:id/etPwd']"

  times: 1

该段配置是遇到登录界面,执行用户名和密码输入.

# 黑名单列表 matches风格, 默认排除内容是2个数字以上的控件

blackList:

- "//*[contains(@resource-id,'com.acp.main:id/tvBottomTab4')]//android.widget.TextView"
- //*[@resource-id='com.acp.main:id/module_tj1_name']    
- //*[@resource-id='com.acp.main:id/module_tj1_description']

该段配置的 最后两条

是点击首页天天盈球区域的那两个文字描述的元素 resource-id , 这两个文字描述都将进入天天盈球页内。通过设置
黑名单过滤掉,不点击这两个文字描述元素,这样就进不了天天盈球页内了。如果只留一个 ,遍历将顺序遍历到第二个文字描述,这样还是会进入盈球页内。所以此处要过滤掉两处位置才不会进入盈球页内。

经验

可以先默认不带 /conf/acp.yml 任何配置文件 让 appcrawler 自己全部遍历,可以设置遍历时间参数 maxTime: 来控制遍历最大时间,这样可基本遍历到所有界面和界面内。

遍历完成后在结果目录 /result/ 你将会看到一共有这么四类文件:

1 dom 文档对象树文件

2 图片文件

3 树形的思维导图

4 appcrawler.log 日志文件

这四类文件非常有用处:

1 图片文件是按点击顺序编号的,你从第一个开始看起,就会看到 appcrawler 遍历的执行顺序,和点击了哪些元素。这样可以帮助我们观察,然后基于自己遍历流程需要,进行过滤和优先设置。

2 dom 文件也是按点击顺序编号的且和图片点击顺序一致。在配置编辑配置遍历顺序时,可以从这里来分析元素的 xpath 或其他 tag 属性来进行定位。

作者工具的思路是这样的,先去 getpagesource 获取整个界面的元素(它先将界面当做或转换为 url 类似的),并捕获为 dom 文件,这样它根据整个 dom 里的 Xpath 等属性来去做判断和是否点击遍历。所以这个很重要,自己要去定制配置的话,必须依靠该 dom 去分析。

3 树形的思维导图 是被遍历过元素的记录树形图

4 appcrawler.log 是 appcrawler 的执行过程日志。里边很详细的记录了所有做的事,且都有 index = 12 这类点击顺序的索引标识,这个索引数字标识和你的 dom 文件和图片截图中的数字标识都是一一对应的。该日志有所有的遍历执行的动作记录,对分析遍历顺序和遍历过程也有帮助。

见下图:

纠正

今天来补充纠正下两个参数的含义
https://github.com/seveniruby/AppCrawler/blob/master/doc/%E9%81%8D%E5%8E%86%E6%8E%A7%E5%88%B6.md
在这里我们看到了解释是这样的:
firstList 表示优先遍历元素特征
lastList 表示最后应该遍历的元素特征
那么按字面意思,我正好和作者的意思理解反了,实际作者是这么解释的。
lastList “哪一个最重要,是大类目功能的 放在最后去点击” 意思就是大多数 APP 底部都有一些大类目的功能切换,比如雪球 APP 底部的 “首页 自选 动态 行情 开户” 这些都是大类目功能。 所以之前我文章中的用法就反了,理解错 first 和 last 了。如果你想控制 APP 底部大类目的切换遍历顺序,应该讲配置写进 lastList。
firstList 这里的 first 呢 是指比如进入到一个大类目界面下了 比如 自选,那么这个自选界面内的哪些优先点击 是配置一个大类目功能界面内的优先。比如这个界面内优先遍历界面内的哪些元素。

所以作者的 xueqiu_private.yml

firstList:
- "//*[contains(name(), 'Popover')]//*"
- "//*[contains(name(), 'Window')][3]//*"
- "//*[contains(name(), 'Window')][2]//*"
selectedList:
#android非空标签
- //*[@clickable="true"]//android.widget.TextView[string-length(@text)>0 and string-length(@text)<20]
- //android.widget.EditText
#ios
- //*[contains(name(), 'Text') and string-length(@value)>0 and string-length(@value)<20 ]
#通用的button和image
- //*[contains(name(), 'Button')]
- //*[contains(name(), 'Image')]
#todo:如果多个规则都包含相同控件, 如何排序
#处于选中状态的同级控件最后点击
lastList:
- //*[contains(@resource-id, 'header')]//*
- //*[contains(@resource-id, 'indicator')]//*
#股票 组合
- //*[../*[@selected='true']]
#港股 美股
- //*[../../*/*[@selected='true'] and @resource-id='']
#tab标签
- //*[../../*/*[@selected='true'] and contains(@resource-id, 'tab_')]
#ios 沪深 港股等栏目
- //*[../*[@value='1']]
#ios 底层tab栏
- //*[contains(name(), 'Button') and ../*[contains(name(), 'Button') and @value='1']]
#tab低栏
- //*[contains(@resource-id,'tabs')]//*

是这样写的。看界面就能理解遍历顺序优先意图了。

接下来 我们来自己看下对比下跑完一次遍历后的 result 结果文件。在对比配置写法,来研究遍历顺序控制。
我们在 result 结果文件 找到首页界面的 dom 打开。
我们看这句 Xpath 表达式,应该是底部的大类目功能 tab 切换标签,也就是界面上的底部 首页 自选 动态 行情 开户 五个 tab 大类目
#tab 标签

好 我们搜索 tab_ 共找出 10 个并且且只搜出这 10 个 ,都是类似 含有 resource-id 属性 并且值是 含有 tab_ 匹配的。 说明该句可以定位到底部大类目功能 tab 标签。

同时加上了另一个属性@selected='true' 来用 and 两个属性满足的条件 作为配置。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 19 条回复 时间 点赞

麻烦问一下 一个界面 如何等待界面刷新 某一个界面等待 3 分钟再往下遍历

今天来补充纠正下两个参数的含义
https://github.com/seveniruby/AppCrawler/blob/master/doc/%E9%81%8D%E5%8E%86%E6%8E%A7%E5%88%B6.md
在这里我们看到了解释是这样的:
firstList 表示优先遍历元素特征
lastList 表示最后应该遍历的元素特征
那么按字面意思,我正好和作者的意思理解反了,实际作者是这么解释的。
lastList “哪一个最重要,是大类目功能的 放在最后去点击” 意思就是大多数 APP 底部都有一些大类目的功能切换,比如雪球 APP 底部的 “首页 自选 动态 行情 开户” 这些都是大类目功能。 所以之前我文章中的用法就反了,理解错 first 和 last 了。如果你想控制 APP 底部大类目的遍历顺序,应该讲配置写进 lastList。
firstList 这里的 first 呢 是指比如进入到一个大类目界面下了 比如 自选,那么这个自选界面内的哪些优先点击 是配置一个大类目功能界面内的优先。

老马 IDEA debug scala sbt project AppCrawler 中提及了此贴 18 Jan 19:11
Author only
老马 回复

能详细说下 list 里面元素的写法吗?- //[contains(@resource-id, 'indicator')]// 类似这种的,写了几个但是感觉没有用。

xpath=//[@index=\"2\"]/[@content-desc=\"更多\" and @resource-id=\"com.taobao.taobao:id/uik_action_overflow\" and @index=\"2\"]/[@resource-id=\"com.taobao.taobao:id/uik_public_menu_action_fr\" and @index=\"0\"]/[@text=\"ꈝ\" and @resource-id=\"com.taobao.taobao:id/uik_public_menu_action_icon\" and @index=\"0]

比如上面这个 “更多”,想加到 blacklist 里面,我应该怎么写?

关于 defineurl 和 baseurl 还是不太理解其作用,可不可以再详细解释下

想问一下,我的 android 手机安装好 xueqiu.apk 之后,运行命令 Java -jar appcrawler-2.1.3.jar -p android -o temp_xueqiu/ --capability appPackage=com.xueqiu.android,appActivity=com.xueqiu.android.common.MainActivity 之后,出现如下错误,是什么原因呢,应该如何解决?

2018-07-22 11:37:51 INFO [Crawler.setupAppium.262] use AppiumClient
Exception in thread "main" org.openqa.selenium.WebDriverException: An unknown server-side error occurred while processing the command. Original error: Cannot start the 'com.xueqiu.android' application. Original error: Error executing adbExec. Original error: 'Command '/Users/wangpingping06/Library/Android/sdk/platform-tools/adb -P 5037 -s 721QEBRD2E2VK shell am start -W -n com.xueqiu.android/com.xueqiu.android.common.MainActivity -S -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -f 0x10200000' exited with code 1'; Stderr: 'java.lang.SecurityException: Permission Denial: starting Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.xueqiu.android/.common.MainActivity } from null (pid=31171, uid=2000) not exported from uid 10316
    at android.os.Parcel.readException(Parcel.java:1684)
    at android.os.Parcel.readException(Parcel.java:1637)
    at android.app.ActivityManagerProxy.startActivityAndWait(ActivityManagerNative.java:3454)
    at com.android.commands.am.Am.runStart(Am.java:638)
    at com.android.commands.am.Am.onRun(Am.java:394)
    at com.android.internal.os.BaseCommand.run(BaseCommand.java:51)
    at com.android.commands.am.Am.main(Am.java:124)
    at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
    at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:281)'; Code: '1' (WARNING: The server did not provide any stacktrace information)
Command duration or timeout: 10.55 seconds
Build info: version: 'unknown', revision: 'unknown', time: 'unknown'

有点好奇 triggerActions 这个选项,是不是只能用于用户名密码?
假设我有一个 activity,是先点击夜间模式才能进入的,然后我又设置了同类 tag 不超过 3 个,但是夜间模式放在了最后一个。
那么我可不可以利用 triggerActions 来直接点击到夜间模式,但是还是跳过同类 tag 的中间的多个重复 tag 呢?

楼主你好,为什么我配置文件一加上那些 list 就直接报错的?

selectedList:
- //*[@clickable="true"]//android.widget.TextView[string-length(@text)>0 and string-length(@text)<20]

报错:

2018-12-19 11:46:27 INFO [AppCrawler$.203.parseParams] Find Conf D:\appcrawler\conf\a
cp.yml
Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputExceptio
n: Cannot construct instance of `com.testerhome.appcrawler.Step` (although at least o
ne Creator exists): no String-argument constructor/factory method to deserialize from
 String value ('//*[@clickable="true"]//android.widget.TextView[string-length(@text)>
0 and string-length(@text)<20]')
 at [Source: (StringReader); line: 100, column: 3] (through reference chain: com.test
erhome.appcrawler.CrawlerConf["selectedList"]->com.fasterxml.jackson.module.scala.des
er.BuilderWrapper[0])
        at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(Mismatche
dInputException.java:63)
        at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(
DeserializationContext.java:1342)
        at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstant
iator(DeserializationContext.java:1031)
        at com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFa
llbacks(ValueInstantiator.java:371)
        at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromSt
ring(StdValueInstantiator.java:323)
        at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromS
tring(BeanDeserializerBase.java:1366)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(Be
anDeserializer.java:171)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDese
rializer.java:161)
        at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserializ
e(CollectionDeserializer.java:286)
        at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserializ
e(CollectionDeserializer.java:245)
        at com.fasterxml.jackson.module.scala.deser.SeqDeserializer.deserialize(SeqDe
serializerModule.scala:78)
        at com.fasterxml.jackson.module.scala.deser.SeqDeserializer.deserialize(SeqDe
serializerModule.scala:61)
        at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet
(MethodProperty.java:127)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(B
eanDeserializer.java:288)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDese
rializer.java:151)
        at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.
java:4001)
        at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:29
92)
        at com.testerhome.appcrawler.TData$.fromYaml(TData.scala:63)
        at com.testerhome.appcrawler.CrawlerConf.load(CrawlerConf.scala:195)
        at com.testerhome.appcrawler.AppCrawler$.parseParams(AppCrawler.scala:204)
        at com.testerhome.appcrawler.AppCrawler$.main(AppCrawler.scala:91)
        at com.testerhome.appcrawler.AppCrawler.main(AppCrawler.scala)
匿名 #10 · January 21, 2019
大儿科 回复

请问这个问题解决了吗? yaml 的格式没有问题啊

chma 回复

请问这个问题解决了吗,我也遇到了这个问题

大儿科 回复

请问你的问题解决了吗?我这边也报错

jia 回复

我也遇到了这个问题,有解决办法吗

沐木 回复

请问你的问题解决了吗?我也遇到了。。。

MLL 回复

我遇到这个问题的原因是:我把 appActivity 写错了,虽然之前也可以遍历,但一加 list 参数就报错。

大儿科 回复

解决了嘛?怎么解决的,我也遇到这个问题!

jia 回复

解决了嘛?我也遇到了。。。

ttltt 回复

问题解决了,因为元素缺少标识 xpath。加上就 ok 了。

请教一下大佬们 为什么我遍历之后的文件夹里面只有截图和日志没有,html 文件呢

20Floor has deleted

请问下能讲下 diff 功能怎么使用?

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up