前言:
最近需要在手机APP上完成每日答题任务。经过一番研究,我发现 Autojs+百度OCR文字识别可以实现安卓手机自动答题,由于我本人也是在工作之余初学Autojs,欢迎各位大神在评论区留言分享。
演示视频:
开发环境 : autojs pro 8
APP : 某个学习软件。
基于autojs实现安卓手机APP自动答题
需求分析:
首先,为什么我提出来用百度OCR文字识别技术?
autojs可以通过Uiselector.text()抓取文本呀?
这是因为:
1、我答题的选项是图片。题目是可以抓取下来,但是作答选项,是个图片,而且每天作答的时候,它的位置来回换。
每天答题的时候,查看选项时我的心情,就像下图的表情包一样,十分复杂, 久久思索,我基本上要看6-10秒,去回答一道题。
2、百度OCR可以根据提交的截图,按照一行一行的结果,返回某段文字在屏幕中的坐标信息。答题选项,例如正确和错误,都是两行,不在同一行,处理起来简单。
3、答题没有时间限制,后期经过我的测试,答题速度可以实现3秒一题。
4、autojs可以截图。
5、每天答的题都是重复的,不用做数据库。题库是固定的97道,连每天的作答顺序都是固定的,只是选项来回变,所以我在程序里把答案都按顺序写好了。
所以说,如果题库你没搞定,做答的题还有答题时间限制,各位大哥大姐们,就不要花时间看正文了。
正文:
1、免费申请百度智能云(限新用户,且需实名认证)
由于要用到百度OCR文字识别提供的API,就需要先申请个百度智能云账号。
https://login.bce.baidu.com/?account=&redirect=http%3A%2F%2Fconsole.bce.baidu.com%2Fai%2F%3F_%3D1597892791979#/ai/ocr/overview/index
用任意一款百度的产品、如百度网盘扫一下登录二维码,只要百度网盘实名认证,它这边就数据互通认证过了。
新用户进去之后,找到领取免费资源,可以领取1000次文字调用。
然后,选择创建应用。记住API Key 和 Secret Key。
2、制作题库
1、把97道题写成了一个字符串,以@作为分隔符,把答案字符串转化成数组。
function ans(){
var str ="正确@错误@错误@正确@正确@正确@错误@错误@正确@错误@错误@错误@正确@正确@错误@正确@错误@正确@错误@错误@正确@错误@错误@正确@错误@错误@错误@错误@错误@错误@正确@正确@正确@错误@错误@错误@错误@正确@错误@错误@正确@错误@错误@错误@错误@错误@正确@错误@错误@错误@正确@正确@正确@错误@错误@错误@错误@正确@错误@错误@正确@正确@错误@错误@错误@错误@正确@错误@错误@错误@错误@正确@错误@正确@错误@错误@错误@正确@错误@错误@错误@正确@正确@正确@正确@错误@错误@错误@错误@错误@正确@正确@错误@错误@错误@正确@正确";
var splitAdd = str.split("@");//97道题,用@分隔成一个个数组
return splitAdd;//split()函数,有兴趣的搜扫
}
用的时候,直接for循环,调用就行了,如下图:
3、截图与读取
autojs知识 ,captureScreen()、 images.read()
用的时候,先写个函数,向手机申请一下截图权限,这样97道题,一道一道自动截图,方便。
//1、先获取截图
function 获取截图权限(){
console.log("正在请求截图");
if(!requestScreenCapture())
{
toast("请求截图失败");//获取截图权限;
exit();
}else{
console.log("准备答题");
}
}
//2、用编号i保存,方便for循环
captureScreen("/sdcard/" +i+ ".jpg");//2、截图第i张
var img = images.read("/sdcard/"+ i +".jpg");//3、读取第i张
这里的imges对象,调用结束的时候,一定要记得做recycle()回收,否则容易导致内存泄露。
4、传图片给百度OCR,并识别返回结果
传图片,涉及的知识,就图片经过base64编码,传给百度API。
至于百度为什么愿意帮你文字识别,就涉及到access_token。
这里我不细说,百度文字识别库里有教程。
所以,要用到步骤1中的向百度申请的API Key 和 Secret Key。
如下图,很多人评论说,
如果autojs 报错误:: TypeError Cannot call method “forEach” of undefined
是怎么回事。
一是你的API Key 和 Secret Key,没有填写自己的
二是百度OCR是有1000次等限制,到上限的时候,它会给你传个空字符,建议阅读一下官方文档
获取百度识图结束后,百度会将识别结果,返回一个json对象:包含文字内容word和所在位置location,这两个变量。
如果对这一部分内容想深入研究,比如识别发票什么的,建议进入百度智能云官方网站,仔细阅读官方文档。
//百度智能云 文字识别OCR 接口调用
function Baidu_ocr(img){
console.log("调用百度ocr");
var img64 = images.toBase64(img, "jpg", 100);//1、图片经过base64编码,100代表品质,0~100
/**** 获取access_token ******/
var API_Key=""; //
var Secret_Key="";
//2、向授权服务地址发送请求
var getTokenUrl="https://aip.baidubce.com/oauth/2.0/token";
var token_Res = http.post(getTokenUrl, {
grant_type: "client_credentials",
client_id: API_Key,
client_secret: Secret_Key,
});
var access_token=token_Res.body.json().access_token;
/**** 调用ocr ******/
//3、调用文字识别普通版,新人1000次(含位置信息)
var ocrUrl = "https://aip.baidubce.com/rest/2.0/ocr/v1/general";
var ocr_Res = http.post(ocrUrl, {
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
access_token: access_token,
image: img64,
});
var json = ocr_Res.body.json();
/**** 返回结果 ******/
return json;//4、返回结果
}
5、识别文字与题库中的答案进行比对,实现找字点击
由于识别的文字,返回的是数组;
制作的题库,也是数组。
通过for循环比对的时候,先要把两个数组,转换成字符串,这样连个字符串比对就容易很多了。
例如,截图的答案是“A、正确”,我的题库只有“正确”。
那只要截图中的文字,包含,正确,就行。
就不用equal()函数了,用indexOf()函数。
//1、开始遍历题目
function 判断(){
for(var i = 0; i <97; i++){
captureScreen("/sdcard/" +i+ ".jpg");//2、截图第i张
sleep(20);
var ansmat = ans();//3、拿出自己制作的题库
var img = images.read("/sdcard/"+ i +".jpg");//4、读取截图
sleep(20);
var res = Baidu_ocr(img);//5、发给百度OCR
img.recycle();//6、回收发送的图片
BaiDu_Ocr_Click(ansmat[i],res);//7、根据百度识图结果,进行图片点击
img.recycle();//8、再回收一次,防止内存泄露
sleep(50);
确定点击();
}
}
//7、根据百度识图结果,进行图片点击
function BaiDu_Ocr_Click(ansmat,res){
var isClick = false;//7.1、默认否
var a = res.words_result;//7.2、读取百度返回的结果
// console.log(a[4].words); //试试读出来是啥题
a.forEach(function(w){
//7.3、如果选项包含题库答案,就根据百度OCR返回的结果中提供的left、top参数点击这个坐标。
if (w.words.indexOf(ansmat)!= -1) {
// log("此题选"+w.words); //试试点的是啥题
isClick = click(w.location.left, w.location.top); //点击指定的文字
return;
}
})
return isClick;//7.4、返回true,方便下一题
}
最终效果
在手机5G网速的情况下,5分钟,答97道,平均3.1秒一道题。
其实最占用时间的,就是传图片给百度的过程,图片质量参数越低,好像识别结果返回到手机上,就越快。
var img64 = images.toBase64(img, "jpg", 50);//1、图片经过base64编码,100代表品质,0~100
完整代码
auto.waitFor(); //监听无障碍服务是否启动
sleep(2000);
setScreenMetrics(1080,2340);//设置脚本运行环境的,屏幕宽高
获取截图权限();
console.log('前台服务: ' + $settings.isEnabled('foreground_service'))
$settings.setEnabled('foreground_service', true);
判断();
结束答题();
sleep(3000);
exit();
function 获取截图权限(){
console.log("正在请求截图");
if(!requestScreenCapture())
{
toast("请求截图失败");//获取截图权限;
exit();
}else{
sleep(200);
console.log("准备答题");
}
}
function 判断(){
for(var i = 0; i <105; i++){
captureScreen("/sdcard/" +i+ ".jpg");//截图第i张
sleep(20);
console.log("获取第"+ (i+1) +"题");
var ansmat = ans();
var img = images.read("/sdcard/"+ i +".jpg");
sleep(20);
var res = Baidu_ocr(img);//调用百度识图OCR的API
img.recycle();//回收
BaiDu_Ocr_Click(ansmat[i],res);
img.recycle();//回收
sleep(50);
确定点击();
}
}
function ans(){
var str ="正确@错误@错误@正确@正确@正确@错误@错误@正确@错误@错误@错误@正确@正确@错误@正确@错误@正确@错误@错误@正确@错误@错误@正确@错误@错误@错误@错误@错误@错误@正确@正确@正确@错误@错误@错误@错误@正确@错误@错误@正确@错误@错误@错误@错误@错误@正确@错误@错误@错误@正确@正确@正确@错误@错误@错误@错误@正确@错误@错误@正确@正确@错误@错误@错误@错误@正确@错误@错误@错误@错误@正确@错误@正确@错误@错误@错误@正确@错误@错误@错误@正确@正确@正确@正确@错误@错误@错误@错误@错误@正确@正确@错误@错误@错误@正确@正确";
var splitAdd = str.split("@");
return splitAdd;
}
function 确定点击(){
var x = text("确定").findOne(300);
if(x){
x.click();
sleep(10);
// console.log("完成答题");
}
sleep(300);
var y = text("下一题").findOne(3000);
if(y){
y.click();
sleep(20);
console.log("答错此题");
}
}
function 结束答题(){
var x = text("完成").findOne(3000);
if(x){
x.click();
console.log("结束");
sleep(1000);
captureScreen("/sdcard/判断题截图.jpg");
console.log("已截图");
}
sleep(3000);
back();
}
//百度智能云 文字识别OCR 接口调用
function Baidu_ocr(img){
console.log("调用百度ocr");
/**** 将图片编码 ******/
var img64 = images.toBase64(img, "jpg", 100);//转换截屏图片
/**** 获取access_token ******/
var API_Key=" ";//自己的
var Secret_Key=" ";//自己的
//向授权服务地址发送请求
var getTokenUrl="https://aip.baidubce.com/oauth/2.0/token";
var token_Res = http.post(getTokenUrl, {
grant_type: "client_credentials",
client_id: API_Key,
client_secret: Secret_Key,
});
var access_token=token_Res.body.json().access_token;
/**** 调用ocr ******/
//文字识别普通版(含位置信息)
var ocrUrl = "https://aip.baidubce.com/rest/2.0/ocr/v1/general";
var ocr_Res = http.post(ocrUrl, {
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
access_token: access_token,
image: img64,
});
var json = ocr_Res.body.json();
/**** 返回结果 ******/
return json;
}
function BaiDu_Ocr_Click(ansmat,res){
var isClick = false;
var a = res.words_result;
// console.log(a[4].words);
a.forEach(function(w){
if (w.words.indexOf(ansmat)!= -1) {
// log("此题选"+w.words);
isClick = click(w.location.left, w.location.top); //点击指定的文字
return;
}
})
return isClick;
}