AirScript教程
前言
AirScript教程: 通过学习本节内容你可以了解常用的AirScript工具和开发过程,更高级的如:IT自动化测试,办公自动化,App与游戏脚本,数据爬虫等内容请直接阅读参考文档中提供的开发文档资料.
参考文档:
环境配置与工具准备
- 操作系统:Windows10
- 手机端安装APP:AirScript
移动端安装APP
在 AirScript官网 下载并安装APP到手机上,会提示开启 无障碍选项
、投屏
、存储
等功能,APP内置服务器,供开发者访问,免去开发者的环境配置,极大降低研发成本。移动端的APP相当于一个开发和运行环境,书写的代码就保存在软件中,不过书写代码还是通过PC端开发更方便些。
移动端连接PC
两种方式,局域网或者公网,一般局域网开发就可以了。
局域网
- 打开APP后,点击
开发者
,在开发地址
->局域网IP
栏目中即可看到局域网IP地址,格式为192.168....*
- 在电脑端(windows,mac,linux) 等设备的浏览器地址栏中键入该
IP地址
即可实现连接手机,新建个项目后就可以开始研发。
tips:确认手机和电脑处于同一个局域网(PS:连接同一个WIFI)
通过公网
- 打开APP后,点击
开发者
->开发地址
->公网IP
,点击公网IP
开关,给予VPN权限 - 在电脑端(windows,mac,linux) 等设备中,在浏览器地址栏键入公网地址即可连接开始研发,格式
http://ide.airscript.cn/.*
。
简单动作
模拟点击坐标
click(x,y,dur)
dur
参数单位是ms
,默认20ms
click(x,y,dur)示例 1
2
3
4from airscript.action import click # 依赖
click(100,200) # 点击坐标为(100,200)位置20ms
click(100,200,3000) # 点击坐标为(100,200)位置3000ms后释放
模拟点击坐标对象
click(point,dur)
point
是android.graphics.Point点对象,可以直接将坐标包装为一个Point对象来使用click(point,dur)示例 1
2
3
4
5
6from airscript.action import click
from android.graphics import Point
p = Point(100,200) # p是个Point对象,Point对象用坐标初始化
click(p)
click(p,3000)
模拟触摸
tips:一个触摸动作的设计应包含按下、移动、抬起三个部分,缺一不可,但可以多
touch.*
touch示例 1
2
3
4
5
6from airscript.action import touch
touch.down(100,200) # 按下不松
touch.move(200,200) # 移动到
touch.move(300,300,1000) # 移动过程花费1000ms
touch.up(400,300) # 移动到目标位置后抬起
模拟滑动
slide(x,y,x1,y1,dur)
这个用的比较多,最好把依赖和方法使用方式记住slide(x,y,x1,y1,dur)示例 1
2
3
4from airscript.action import slide
slide(100,200,300,400) # (100,200)滑动到(300,400)耗时20ms
slide(100,200,300,400,1000) # 耗时1000ms
模拟手势
模拟复杂的人手操作,如单指画圆、双指捏合、多指操控等,先大概了解AirScript中模拟手势是什么个原理,具体要用的时候跟着示例编写即可
1 | from airscript.action import gesture # 手势包 |
tips: 定义一个手势必须包含创建路径、路径方法、手势执行三个部分.
创建路径
path(startTime,duration,willContinue)
创建新的path路径,规划决定手势如何移动构造参数 类型 描述 startTime
number 可选,延迟多久开始绘制路径,单位ms,默认0 duration
number 可选,路径绘制的时常,单位ms,默认20 willContinue
boolean 可选,在路径结束后,手指是否抬起,默认 false
路径方法
airscript.action.path
实现了android.graphics.Path
中的大部分绘制方法,参数传递参考: android.graphics.Path
常用方法:moveTo()
lineTo()
手势执行
- gesture.perform(path[],callback)
tips:路径规划后,必须执行手势才可看到效果参数 类型 描述 path[]
path数组 执行的一组路径,当无callback参数时,path可改为多参传递,如 (path,path)
callback
class 可选,手势执行完成后的回调类,必须存在 onCompleted()
和onCancelled()
两个回调函数
手势示例
1 | from airscript.action import gesture |
更多示例见官方开发文档,使用方法不用背,用的时候照着文档现编就行。
模拟文本输入
input(msg,Selector...)
在页面的文本输入框控件
中输入文本参数 类型 描述 msg
string 输入的文本信息 Selector...
Selector
可变参数可选,控件选择器,如果不填写该参数,则会获取当前屏幕上获取焦点的输入框,进行输入
1 | from airscript.node import Selector |
模拟功能按键按下
模拟部分功能键按下
模拟按键 | 描述 |
---|---|
key.home() |
模拟HOME按键 |
key.back() |
模拟返回按键 |
key.notifactions() |
模拟呼出通知栏 |
key.lockscreen() |
模拟锁屏 |
key.screenshot() |
模拟截屏 |
key.recents() |
模拟呼出任务栏 |
1 | from airscript.action import key |
airscript.action.key
只能模拟部分功能键,如果您想尝试模拟键盘按键,比如 A
,B
等. 请尝试通过 android debug bridge(adb)
来模拟.
捕获用户行为
捕获用户点击坐标返回一个 airscript.action.Point
对象
Catch 类的方法 |
描述 | 取值 |
---|---|---|
click() |
捕获用户点击,返回Point 对象 |
空 |
msg(str) |
捕获页面展示的信息 | 字符串string |
shine(siShine) |
捕获页面是否闪屏 | boolean false/true |
1 | from airscript.action import Catch |
hid虚拟外设动作
hid虚拟外设动作类,此模块可在无障碍模式下使用hid
1 | from airscript.action import hid |
使用前请确保已经绑定设备成功
hid支持多种动作和模拟输入
hid.click(x,y,dur)
hid.slide(x,y,x1,y1,dur)
hid.key(keycode...)
hid支持所有键盘按键,在hid.key()
中填入 keycode
参数即可
keycode
参数详解:
类型 | keycode |
---|---|
字母键 | a -A键,b -B键… |
数字键 | 1 -数字1,2 -数字2… |
功能键 | enter ,esc ,backspace ,tab ,space ,capsLock ,f1 …f12 ,printScreen ,scrollLock ,pause |
导航键 | insert ,home ,pageUp ,delete ,end ,pageDown ,rightArrow ,leftArrow ,downArrow ,upArrow |
符号键 | minus -减号,equal -等号,leftBracket -左方括号,rightBracket ,backslash -反斜杠,semicolon -分号,quote -单引号,grave -重音符键,comma -逗号,period -句号,slash -斜杠 |
小键盘 | numLock ,keypadDivide -小键盘除号,keypadMultiply -小键盘乘号,keypadSubtract -小键盘减号,keypadAdd ,keypadEnter ,keypad0 …keypad9 ,keypadDecimal -小键盘小数点 |
控制键 | Shift ,Ctrl ,Alt ,Command ,id -要操作的设备的唯一标识符 |
tips:Shift
,Ctrl
,Alt
,Command
这几个键不传入时默认值为 false
表示不按下,传入时相当于值为 true
表示按下.
选择器
通过选择器 Selector
检索获取指定控件 Node
,对控件进行操作,是较为常用的一种寻找操作对象的方法,有点类似CSS中的控件寻找。通过在选择器中自定义选择规则来实现特定选择。
对控件的支持是全分辨率的,即在一台设备上开发多台设备兼容
1 | from airscript.node import Selector |
选择器模式
Selector(mode)
mode 描述 0
简单模式,只展示重要控件 1
复杂模式,展示全部控件,层级较深 2
简单模式下忽略系统控件 3
复杂模式下忽略系统控件
1 | from airscript.node import Selector |
一般的使用场景中只需要用Selector()
这种简单模式即可,而且开发时使用的是可视化界面,可以通过控件检索功能手动生成选择语句,不用自己代码书写定义,也就是说了解即可。
缓存模式
Selector.cache(iscache)
参数 描述 — iscache
是否开启缓存 True
/False
开启缓存后选择器会将界面的控件缓存,之后的查找都将在缓存的控件中进行,加快了速度,但是缓存开启后无论界面变化,始终使用的是缓存的控件,注意使用场合*
cache()
是Selector
类的静态方法,直接通过类名调用,而后面的find()
方法是需要通过Selector()
对象调用的,例如:Selector.cache(True)
,Selector().find()
,使用时注意区分
Selector find
这部分了解格式就行,用的时候也是和选择器模式一样在可视化界面手动生成查询代码,不用编写。
Selector().find()
返回控件的方法,返回结果分情况:- 当找到一个满足规则控件时返回此控件(类型
Node
) - 当找到多个满足规则控件时返回第一个
- 当没找到符合规则的控件时返回
null
- 当找到一个满足规则控件时返回此控件(类型
1 | from airscript.node import Selector |
Selector().find_all()
返回控件的方法,返回结果分情况:- 找到控件返回所有控件(类型
Node[]
) - 没找到控件返回
null
- 找到控件返回所有控件(类型
使用方法同 Selector().find()
选择器-约束规则
通过指定的约束规则来使用选择器选出指定的控件
通常我们不是从0到1地编写查询代码,而是在可视化开发界面通过 控件检索
工具获取控件属性,生成对应的 Selector
规则语句直接插入到代码中来查找控件,下面的表格中需要了解的是参数的含义,重点关注关系类约束,如child()
、parent()
、brother()
等,其他的看看就行,用到的时候再来查,不要背这个表啊,会累出白头发的。
约束方法 | 备注 |
---|---|
Selector().id(string) |
约束 id |
Selector().text(string) |
约束 文本 |
Selector().type(string) |
约束 类型 |
Selector().desc(string) |
约束 描述 |
Selector().hintText(string) |
约束 默认文本,通常在 EditText 中会有此属性 |
Selector().packageName(string) |
约束 包名,包名通常为 APP的ID |
Selector().childCount(number) |
约束 孩子数量 |
Selector().inputType(number) |
约束 可输入类型,通常在 EditText 中会存在输入类型 |
Selector().drawingOrder(number) |
约束 绘制排序,同一级别控件的第几个 |
Selector().depth(number) |
约束 控件深度,深度是指控件的层级 |
Selector().maxTextLength(number) |
约束 最大字符长度 |
Selector().clickable(boolean) |
约束 是否可点击 |
Selector().checkable(number) |
约束 是否可选中 |
Selector().checked(number) |
约束 是否选中 |
Selector().editable(number) |
约束 是否可编辑 |
Selector().enabled(number) |
约束 是否可访问 |
Selector().dismissable(number) |
约束 是否可取消 |
Selector().focusable(number) |
约束 是否可以获取焦点 |
Selector().focused(number) |
约束 是否已经获取了焦点 |
Selector().longClickable(number) |
约束 是否可以长按 |
Selector().visible(boolean) |
约束 是否用户可见 |
Selector().parent(number) |
约束 获取第N个父元素 |
Selector().child(number) |
约束 获取第N个孩子 |
Selector().brother(number) |
约束 获取第N个兄弟 |
1 | from airscript.node import Selector |
tips: 通过选择器约束规则找到的内容要调用 find()
或 find_all()
方法才能返回控件Node
- 约束规则参数为正则的约束属性,类似
Selector().id(str)
,根据id
检索,参数str
可直接为检索内容,也可以为正则表达式,两者都是用"
包裹起来的字符串,因此特别要注意:如果你将一段文本作为字符串检索内容,结果没找到控件,那就要检查字符串中是否包含了正则表达式中的符号,如果单纯使用字符串要将符号用转义字符\
转义 - 我们可以通过
node = Selector().id(str).find()
这种这种形式获取控件Node
,也可以通过已经获取的控件来使用其属性,例如:print(node.id)
- 选择器可以多个连用,例如:
node = Selector().id(str).clickable(True).find()
number
类型参数可通过控件检索工具
查看具体数值
关系约束规则
关系类约束是这些约束规则里最需要掌握的,别的可以不太了解,但是关系类一定要熟悉,因为我们在定位控件的时候往往采用的都是相对位置,就要靠关系约束规则。
Selector().parent(i)
获取父元素参数i 备注 不填i
获取第i个父元素,默认获取所有父元素 1
获取父元素 2
获取爷爷元素 3
获取太爷爷元素 1,3
获取第1和第3个父元素,即父元素和太爷爷元素 1.3
获取第1~3之间的所有父元素,即父、爷爷、太爷爷元素
1 | from airscript.node import Selector |
Selector().child(i)
获取孩子控件参数 i
备注 i
不填获取所有孩子控件 i
为正整数获取第i个孩子控件 i
为负整数获取倒数第1个孩子 i
为正小数(例如:1.3)获取第1~第3之间的所有孩子 i
为负小数(例如:-1.3):获取倒数第1-第3之间的所有孩子
tips: 一个控件只可以有一个直接父控件,因此 parent()
方法可以直接获取多级父控件而不混淆。一个控件可以有很多直系孩子控件,因此 child()
方法可以直接获取多个直系孩子,甚至指定获取倒数第几个孩子,而不产生混淆
Selector().brother(n)
获取兄弟元素,即同层级控件
参数n |
备注 |
---|---|
n 不填 |
默认不填:获取所有兄弟控件 |
1 |
获取第一个兄弟控件 |
1,2 |
获取第1和第2个兄弟控件 |
1.4 |
获取1-4之间的所有兄弟控件 |
0.1 |
获取当前控件的下一个兄弟控件 |
-0.1 |
获取当前控件的上一个兄弟控件 |
-1 |
获取倒数第1个兄弟控件 |
1 | from airscript.node import Selector |
选择器-动作
在通过选择器约束规则找到控件后可直接对控件施加动作,如:控件点击,滑动,输入等
执行动作方法 | 描述 |
---|---|
Selector().click() |
控件点击 |
Selector().long_click() |
控件长按 |
Selector().slide(number) |
控件滑动 |
Selector().input(string) |
控件输入 |
控件点击
Selector().click()
控件点击是透传点击,通过系统操作控件底层执行点击。 而屏幕坐标点击则是通过模拟手势操作屏幕。对于透传点击,哪怕界面上看不见控件,但存在索引依然可以点击。透传点击忽视坐标分辨率,因此可以达到全部分辨率适配的效果。
1 | from airscript.node import Selector |
控件长按
Selector().long_click()
控件长按,使用方法和控件点击类似
控件滑动
Selector().slide(ori)
控件滑动,只支持向前滑动、向后滑动。步长默认一屏,无法自定义步长。
参数ori |
描述 |
---|---|
-1 |
向前滑动 |
1 |
向后滑动 |
控件输入
Selector().input(msg)
控件输入字符串类型 msg
,当存在可输入文本的控件时才可输入。无需获取焦点,直接输入。
1 | from airscript.node import Selector |
控件属性
下表展示的是控件的属性含义,不用背,用的时候查就行,而且可视化控件检索工具使用的时候属性会给默认提示的,结合控件数值就能理解了。
属性id | 描述 |
---|---|
ID |
控件ID,部分APP中ID属性,随手机安装可能动态变化,谨慎使用 |
text |
控件的文本 |
type |
控件的类型 |
desc |
控件的描述 |
hintText |
控件的默认展示文本 |
packageName |
控件所属包名 |
rect |
控件在屏幕中的位置 |
childCount |
子控件数量 |
inputType |
输入类型 |
maxTextLength |
控件最大文本长度 |
clickable |
是否可点击 |
checkable |
是否可选中 |
checked |
是否已选中 |
editable |
是否支持编辑 |
enabled |
是否可访问 |
visible |
是否针对用户展示 |
dismissable |
是否可取消 |
focusable |
是否可以获取焦点 |
focused |
是否已获取了焦点 |
longClickable |
是否可以长按 |
rect属性和方法
rect属性方法 | 描述 |
---|---|
rect.left |
x坐标 |
rect.top |
y坐标 |
rect.width() |
控件的宽度 |
rect.height() |
控件的高度 |
rect.centerX() |
控件的中心坐标X |
rect.centerY() |
控件的中心坐标Y |
二次检索
二次检索的应用场景也挺多的,例如:我想先判断一次检索的控件存在时,再对找到的控件进行二次检索
Node find
Node().find(selector)
注意是要通过对象调用find(selector)
方法注意二次检索需要使用一次检索的返回结果(二次检索示例 1
2
3
4
5
6
7
8from airscript.node import Selector
node = Selector().id("com.aojoy.airscript:id/search_query_section").type("FrameLayout").find()
if node:
# 在node控件中查找它的倒数第一个子控件
nchild = node.find(Selector().child(-1))
if nchild:
print(nchild)Node
类型的控件)来调用find(selector)
方法,同时注意其参数selector
是Selector
类型不用调用find()
或find_all()
方法,返回类型直接就是Node
类型控件Node().find_all()
Node().find_all()
和Node().find()
方法同Selector.find()
参数用法相同
获取关系控件
Node().parent(*n)
、Node().child(*num)
、Node().brother(*n)
都是用于二次检索时获取关系控件的方法,参数用法同关系约束规则用法相同
控件二次操作
Node().click()
、Node().long_click()
、Node().slide(ori)
、Node().input(content)
都是对控件二次操作的方法,参数同动作相同
选择器实战
为了巩固学习效果,最好根据自己的需要尝试书写一个项目,在实战中应用学到的选择器,根据不同场景变换使用方式,下面是我实现的一个项目,能在某看小说APP中刷日常任务
1 | # __init__.py 为初始化加载文件 |
本项目只用于学习参考,不得用于商业用途或违规传播,否则后果自负。
图像
android.graphics.Bitmap
是AirScript中用于图像处理的数据类型,图像来源两种:截屏或文件
截屏
默认全屏截屏,可指定截屏范围,用法如下:
1 | from airscript.screen import Screen |
图片文件
读取图片文件
从文件读取的图片也是转为Bitmap
对象
Screen.file2Bitmap(path,sampleSize)
参数 类型 可选性 描述 path
string
必选参数 图片路径 sampleSize
int
可选参数 采样尺寸,如参数为2:每隔2行,2列采集1行,结果为原图的1/4大小
1 | from airscript.screen import Screen |
保存到图片文件
参数 | 类型 | 可选性 | 描述 |
---|---|---|---|
path |
string | 必选 | 保存路径 |
bitmap |
Bitmap | 可选 | Android 图像,默认全屏截图 |
quality |
int | 可选 | 截图的清晰度 1~100,默认:100原图,20:20%原图分辨率 |
1 | from airscript.screen import Screen |
找色
根据一组颜色找到满足限制的点,找色部分的几个函数都是使用可视化开发界面中的找色工具直接生成,这部分只要理解函数的作用、参数意义、找色原理即可。
找色原理
FindColors(colors)
构造找色对象
颜色参数,由多组坐标与颜色构成,每一组颜色之间用|
分割,例如:’426,346,#05D395|502,351,#05D015|676,569,#05D294’
上述含义:在#05D395
这个颜色的相对位置(x=502-426,y=351-346)
有一个颜色值#05D015
,并且 在#05D395
这个颜色的相对位置(x=676-426,y=569-346)
存在一个颜色值#05D294
,找出满足此条件的点。参数 类型 必须 描述 colors
string 必填 颜色参数,一般使用编辑器中的找色工具生成 范围
FindColors(colors).rect(x,y,x1,y1)
指定一个矩形范围,缺省rect参数(不加rect函数)时默认全屏找色间隔
FindColors(colors).space(num)
指定找色结果点的间距,当找到的两个点间隔小于space参数值时合并两个点,缺省space(不加space函数)时默认space值为5
,参数单位是像素方向
FindColors(colors).ori(num)
指定找色的方向,缺省ori(不加ori函数)时默认为2
,具体参数含义见官方文档中示意图偏色
FindColors(colors).diff(rgb)
设置偏色返回结果
FindColors(colors).find()
FindColors(colors).find_all()
返回结果类型分别是Point类型和Point[]类型找色示例 1
2
3
4
5
6
7
8from airscript.screen import FindColors
# 构造一个FindColor对象,并执行查找
points = FindColors('931,549,#EFEFEF|932,496,#EEEEEE|964,523,#EFEFEF|862,475,#0B0B16').find_all() # 全屏查找,全采用默认参数
if points:
print('共查到%d个结果'%len(points))
for point in points:
print(point.x,point.y)
找图
通过局部图片,在屏幕中找到该图片的位置信息,支持全分辨率(局部图片尺寸不变,屏幕如何缩放,均可以找到位置信息)
- 构造找图对象
FindImages(part_img)
创建一个找图对象参数 类型 必须 描述 part_img
string 必填 局部图片的绝对路径
如果没有局部图片的绝对路径可使用下面方法由相对路径生成绝对路径
1 | # 导包 |
范围
FindImages(part_img).rect(l,t,r,b)
指定屏幕找图范围匹配度
FindImages(part_img).confidence(num)
指定找图结果的可信度,过滤掉可信度低的结果参数 类型 必须 描述 num
int 必填 可信度,0~1,1为100%准确,0.8为80%的准确度 查找模式
返回一个结果
查找模式 备注 find
先使用 find_template
查找,如果找不到再用find_sift
查找find_sift
支持全分辨率的单图查找模式 优点:全分辨率 缺点:速度慢 find_template
优点:速度快 缺点:不支持全分辨率 返回一组结果
查找模式 备注 find_all
先使用 find_all_template
查找,如果找不到在用find_all_sift
持全分辨率的查找模式 优点:全分辨率 缺点:速度慢 find_all_template
优点:速度快 缺点:不支持全分辨率
.find()
返回结果类型为Object对象
1 | //结果对象案例 |
.find_all()
返回结果类型为Object对象列表
1 | //结果对象案例 |
.find()
使用返回结果示例
1 | # 导包 |
1 | # 导包 |
后记
常用的功能就是上面这些,更多高级功能可在官方文档中学习,包括区域颜色量、目标检测、文字识别、比色、二维码识别、界面设计等