作为一个资深的大众点评VIP用户,我深知"霸王餐"活动的吸引力。每次打开APP看到琳琅满目的免费试吃活动,都忍不住想全部报名。但手动操作实在太费时费力了——要一个个点开活动页面,填写报名信息,再返回继续下一个。有时候因为操作太频繁,还会被系统误判为异常行为。
这就是我决定开发自动化脚本的初衷。通过AutoJS这个轻量级工具,我成功实现了:
实测下来,原本需要半小时的手动操作,现在3分钟就能搞定,而且成功率更高。最重要的是,终于不用再盯着手机重复点击了。
在开始开发前,我对比了几种常见的自动化方案:
| 工具名称 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Airtest | 图形化操作简单 | 需要连接电脑 | 游戏自动化 |
| Appium | 功能强大 | 配置复杂 | 专业测试 |
| AutoJS | 纯手机端运行 | 功能相对简单 | 日常自动化 |
最终选择AutoJS主要基于以下几点考虑:
特别适合像霸王餐报名这种简单的重复性操作。而且它的触摸模拟非常自然,不容易被系统检测到。
在动手写代码前,有几个关键步骤不能跳过:
我花了些时间记录手动报名的完整流程:
每个步骤都要记录下界面元素的变化和操作方式。这个分析过程很重要,能帮助我们发现潜在的坑点。
AutoJS主要通过控件的属性来定位元素。我们需要获取:
可以使用AutoJS自带的"布局分析"功能查看这些信息。建议多截图保存,方便后续调试。
有些操作需要特别注意:
这些细节处理不好,脚本很容易失效。后面会具体讲解如何实现。
现在进入最关键的代码部分。我会拆解每个功能模块,解释实现思路和注意事项。
javascript复制auto.waitFor();
const appname = '大众点评';
app.launchApp(appname);
console.log(device.width,device.height);
// 进入免费试频道
id('main_listview').waitFor();
let list = id('main_listview').findOne();
list.children()[1].children()[3].findOne(className('android.widget.ImageButton')).click();
这段代码有几个关键点:
auto.waitFor()确保无障碍服务已开启app.launchApp()启动指定APP注意不同手机型号的控件结构可能不同,需要适当调整children()的索引值。
javascript复制console.log('进入vip专享活动频道');
while (text('VIP专享活动').findOne().parent().findByText("全部").size() == 0);
let allBtn = text('VIP专享活动').findOne().parent().findByText("全部").get(0).parent();
let allBtnBounds = allBtn.bounds();
press(allBtnBounds.left, allBtnBounds.top, 100);
这里有个重要技巧:
press()而不是click(),因为某些按钮有长按校验javascript复制console.log('开始搜索可点击的免费抽按钮');
let btn = text("免费抽").findOne(3000);
if(btn){
let bounds = btn.bounds();
press(bounds.left, bounds.top, 1);
let isOk = text('免费试活动详情').findOne(3000);
if(isOk){
console.log('点击免费抽按钮成功');
let bmBtn = text("我要报名").findOne(4000);
if(bmBtn){
bmBtn.click();
}else{
let wybmBtn = text("已报名,看看其他活动").findOne();
if(wybmBtn){
console.log('已报名,返回免费试首页');
wybmBtn.click();
}
}
}else{
console.log('点击免费抽按钮失败');
}
}else{
console.log('没有找到免费抽按钮');
}
这段代码实现了:
建议添加足够的等待时间和日志输出,方便排查问题。
javascript复制text('确认报名').waitFor();
// 判断是否有分店
if(text("请选择分店").exists()){
click("请选择分店",0);
className('android.widget.ListView').waitFor();
className('android.widget.ListView').findOne().child(1).click();
}
// 判断是否有套餐
if(text("请选择套餐").exists()){
click("请选择套餐",0);
className('android.widget.ListView').waitFor();
className('android.widget.ListView').findOne().child(1).click();
}
while(!click("确认报名",0));
text('报名成功').waitFor();
while(!click("回到首页",0));
这部分处理了两种常见情况:
默认都选择第二个选项(通常是最热门的)。如果业务需要,可以扩展更智能的选择逻辑。
经过一段时间的实际使用,我总结出几个提升脚本稳定性的技巧:
简单的scroll()方法容易被检测到,建议改用gesture()模拟真人滑动:
javascript复制function naturalSroll(startY, endY){
let steps = 50;
let interval = 5;
let points = [];
for(let i=0;i<steps;i++){
points.push([device.width/2, startY+(endY-startY)*i/steps]);
}
gesture(interval, points);
}
这个函数模拟了带加速度的滑动轨迹,实测效果更自然。
在关键操作间添加随机延迟,避免固定节奏:
javascript复制function randomDelay(){
sleep(random(300, 1500));
}
网络不稳定时,需要完善的错误处理:
javascript复制function safeClick(selector, maxRetry=3){
for(let i=0;i<maxRetry;i++){
try{
if(click(selector)) return true;
}catch(e){
console.warn(e);
}
sleep(1000);
}
return false;
}
如果需要管理多个账号,可以这样扩展:
javascript复制let accounts = [
{name:"账号1", username:"xxx", password:"xxx"},
{name:"账号2", username:"xxx", password:"xxx"}
];
accounts.forEach(account=>{
console.log("处理账号:"+account.name);
// 登录逻辑
// 报名逻辑
});
经过3个月的持续使用和优化,这个脚本已经非常稳定:
但使用时需要注意:
如果遇到脚本失效的情况,通常只需要调整元素定位部分就能修复。建议保存历史版本,方便快速回滚。