薅羊毛 | 揭秘闲鱼方案,一部手机,实现随时随地薅羊毛
image
阅读文本大概需要 15 分钟。
1、目标场景
上一篇文章 过后,很多人在后端给我留言,说上次的方案不够智能,每次操作都要连上电脑操作,一旦离开 PC,就没法愉快地薅羊毛了。问我有没有便捷的方案?
答案是一定的。万能的闲鱼有大神已经实现了。那他们是如何实现的?
本篇文章以「全民小视频」为例,在只有一部手机的情况下,利用 Android 单元测试脚本「UIAutomator2」来实现这一操作。
2、准备工作
因为需要编写 Android 单元测试脚本,所以需要在 PC 端配置好 Android 开发环境,并准备一部已「root」的 Android 设施,提前下载好 Android 开发工具:Android Studio。
ps:以上操作是必备的。对 Android 开发不熟习的童鞋可以自行 Google 一下。
另外,使用源文件直接调用测试脚本需要对当前应用进行「签名」,所以需要提前使用 AS 创立一个签名文件「也可以直接使用源码中提供的签名文件:deal」,而后在 app/gradle.build 文件中配置好 Debug 和 Release 模式的签名。
android { # 签名文件 signingConfigs { release { keyAlias 'xag' keyPassword 'xingag' storeFile file('./../deal') storePassword 'xingag' } } # 配置签名 buildTypes { debug { signingConfig signingConfigs.release minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } release { signingConfig signingConfigs.release minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }}
最后在 app/gradle.build 文件中增加单元测试脚本依赖库:uiautomator。
dependencies { # 新添加uiautomator依赖 implementation 'com.android.support.test.uiautomator:uiautomator-v18:2.1.3'}
ps:因为很多 App 厂商针对模拟器会做反作弊操作,所以最好使用一部真机来测试。
3、编写脚本
我们首先使用 AS 创立一个新的项目。
image
接着在测试脚本目录下「app/src/androidTest/java/」新建一个类 GetDeal,让它使用 AndroidJUnit4 的方式运行。
@RunWith(AndroidJUnit4.class)public class GetDeal{}
在 @BeforClass 和 @Before 注解的方法中初始化「UiDevice」,使用UiDevice 类获取到当前设施的宽和高。
# 只会被调用一次@BeforeClasspublic static void init(){ # 初始化UiDevice mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());}//每一个测试case被调用之前都会运行@Beforepublic void start(){ //获取当前设施屏幕高度、宽度 device_width = mDevice.getDisplayWidth(); device_height = mDevice.getDisplayHeight();}
而后,使用 @Test 注解创立一个测试 Case。
因为 UIAutomator2 中可以通过 InstrumentationRegistry 拿到「上下文 Context」,获取到目标应用的包名后,即可以顺利使用 Intent 的方式打开目标 App。
# 全民小视频包名private String mPackageName = "com.baidu.minivideo";# 获取到上下文Context mContext = InstrumentationRegistry.getContext();# 指明用意Intent myIntent = mContext.getPackageManager().getLaunchIntentForPackage(sPackageName); # 启动全民小视频appmContext.startActivity(myIntent);
与其余测试框架一样,第一次打开 App 都需要做一个显式等待操作,直到首页元素加载完全。
获取元素的属性值需要借助 android sdk 文件夹自带的工具:「uiautomatorviewer」。
image
# 等待首页中的某个元素完全加载出来# 超时时间:10sboolean result = mDevice.wait(Until.hasObject(By.res("com.baidu.minivideo:id/fragment_index_recycler")), 10 * 1000);if (result){ //后面的操作}else{ Log.d("xag", "超时了~");}
接着,我们获取到首页列表元素的第一个子元素,执行点击操作,进入到视频播放页面。
//主页列表元素UiObject2 rv = mDevice.findObject(By.res("com.baidu.minivideo:id/fragment_index_recycler"));//列表元素下面的子元素列表if (rv.getChildCount() > 0){ //获取第一项元素,点击进入 UiObject2 first_video_element = rv.getChildren().get(0); first_video_element.click();
为了应对某些 App 的反作弊行为,这里需要对每一条视频的播放时间取一个随机数据。
/*** * 产生随机数 * @param min * @param max * @return*/public static int geneRandom(int min, int max){ Random random = new Random(); return random.nextInt(max) % (max - min + 1) + min;}int wait_time = NumUtils.geneRandom(10, 40);Log.d("xag", "这个视频播放时间:" + wait_time + "s");
使用 uiautomatorviewer 工具可以获取到当前视频的少量元素属性,而后再使用 UiDevice 类获取到视频的基本信息,包含:视频标题、视频作者、播放时长。
/*** * 获取当前视频信息 * @param play_time 视频播放时间*/private VideoItem get_current_video_info(int play_time){ # 视频作者 UiObject2 author_element = mDevice.findObject(By.res("com.baidu.minivideo:id/detail_author_name")); # 视频标题 UiObject2 content_element = mDevice.findObject(By.res("com.baidu.minivideo:id/detail_title")); String author = (null == author_element) ? "" : author_element.getText(); String content = (null == content_element) ? "" : content_element.getText(); VideoItem item = new VideoItem(play_time, author, content); return item; }
当一个视频播放时长到了之后,我们利用 UiDevice 的 swipe 函数模拟向上滑动到下一个视频播放界面。
同样,这里对起始坐标和结束坐标做肯定数据内的「随机」解决,保证每次滑动的起始坐标、结束坐标都不一样。
/*** * 下一个视频 */private void play_next_video(){ //手机按下的坐标和抬手的坐标 int top = NumUtils.geneRandom(20, 150); int bottom = device_height - top; int top_x = NumUtils.geneRandomWithOffset(device_width / 2, 10); int bottom_x = NumUtils.geneRandomWithOffset(device_width / 2, 10); Log.d("xag", "滑动底部坐标:" + bottom_x + "/" + bottom + ";顶部坐标:" + top_x + "/" + top); # 获取到下一个视频界面 mDevice.swipe(bottom_x, bottom, top_x, top, step); mDevice.waitForIdle(timeout); }
循环执行以上操作,即可以实现从打开一个 App,到切换页面、滑动页面等一系列常见操作。
4、调用测试脚本
最后一个步骤是通过一个 App 应用调用上面编写的测试脚本。
首先,我们需要在默认项目中的布局文件:activity_main.xml 中加入一个按钮控件。
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="10dip" tools:context=".MainActivity"> <Button android:id="@+id/start_qmxsp_tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="全民小视频" android:textSize="20sp" /></LinearLayout>
接着在主界面 MainActivity 中增加点击的监听事件。
# 按钮start_qmxsp_tv = findViewById(R.id.start_qmxsp_tv);# 为按钮增加监听事件start_qmxsp_tv.setOnClickListener(this);# 回调@Overridepublic void onClick(View v){ switch (v.getId()) { case R.id.start_qmxsp_tv: # 回调测试case openQmxspMet(); break; default: break; }}
通过「am instrument」语法,加上测试 Case 所在的包名及测试 Case 类名、方法名生成一条命令。
# 测试Case类所在的包名String pkgName = "com.xingag.qmxsp";// 测试Case的类名String clsName = "GetDeal";// 测试Case的方法名String mtdName = "start_qmxsp";// 生成一条命令String command = "am instrument -w -r -e debug false -e class " + pkgName + "." + clsName + "#" + mtdName + " " + pkgName + ".test/android.support.test.runner.AndroidJUnitRunner";
而后利用「Runtime.getRuntime().exec(“su”)」获取到设施 Root 权限之后,即可以执行上面的命令了。
//获取root权限process = Runtime.getRuntime().exec("su"); pw = new PrintWriter(process.getOutputStream());//执行命令pw.println(command);pw.flush();result = process.waitFor();
需要注意的是,执行上面的命令是一个「耗时」的操作,必需放在子线程中执行。
new Thread() { @Override public void run() { super.run(); String command = generateCommand("com.xingag.qmxsp", "GetDeal", "start_qmxsp"); CMDUtils.CMD_Result rs = CMDUtils.runCMD(command, true, true); } }.start();
最后,可以直接运行项目后,会在手机上生成一个应用;或者者可以通过 AS 工具生成一个带有签名的应用 APK,而后再利用手机助手安装到手机上。
image
5、结果结论
打开应用,而后点击界面上的按钮,紧接着会执行测试脚本,按照测试 Case 中写好的脚本步骤执行一系列操作。
image
本篇只是以全民小视频一个 App 为例,使用 UIAutomator2 脚本实现全自动薅羊毛的操作。
假如想要实现其余平台,只要编写对应的测试 Case,按顺序依次执行测试脚本,即可以实现一部手机随时随地薅多个平台羊毛的操作。
我已经将一律源码,包含一个测试 apk 上传到后端上,公众号回复「 **薅羊毛2 **」就可取得。
假如你觉得文章还不错,请大家点赞分享下。你的一定是我最大的鼓励和支持。
推荐阅读:
薅羊毛 | 让Python每天帮你薅一个早餐钱
本文首发于公众号「 AirPython 」,公众号后端回复「 薅羊毛2 」就可获取完整代码。
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 薅羊毛 | 揭秘闲鱼方案,一部手机,实现随时随地薅羊毛