Python+ADB实现安卓自动化测试:轻量级脚本模拟用户刷视频行为

Python+ADB实现安卓自动化测试:轻量级脚本模拟用户刷视频行为

1. 项目概述与核心价值

最近在搞安卓应用测试,特别是那些重度依赖用户交互的App,比如短视频平台,手动点点点不仅效率低,还容易出错。我就琢磨着,能不能用Python写个脚本,让它像真人一样去操作手机,自动刷视频、点赞、评论?这其实就是自动化测试里模拟用户行为的一个典型场景。经过一番折腾,我发现ADB(Android Debug Bridge)配合Python的subprocess模块,是实现这个想法最直接、最轻量的方案,完全不需要引入Appium这样的大型框架,特别适合快速验证功能、执行重复性任务或者做一些简单的数据采集。

这个方案的核心价值在于“轻快准”。,是指环境依赖少,基本上有Python和ADB工具就行;,是指脚本开发调试速度快,ADB命令直通系统底层,响应迅速;,是指对屏幕坐标、组件属性的控制精度高,能模拟出非常接近真人的操作序列。对于测试工程师、爬虫开发者或者任何需要批量操作安卓手机的朋友来说,掌握这套方法,就相当于拥有了一支不知疲倦的“数字手指”,能7x24小时替你完成那些枯燥的点击和滑动。接下来,我就以让脚本自动刷某个视频App为例,拆解整个从环境搭建到脚本优化的实战过程。

2. 环境准备与核心工具解析

工欲善其事,必先利其器。在开始写代码之前,我们需要把“战场”布置好。这里主要涉及两样东西:Python环境和ADB工具链。别看听起来简单,里面有不少细节直接决定了后续脚本的稳定性和可移植性。

2.1 Python环境与必要库

Python是我们的指挥中枢。我强烈建议使用Python 3.7及以上的版本,因为它在异步支持和标准库稳定性上更好。不需要安装什么特殊的自动化测试框架,核心就用Python自带的subprocess库来调用ADB命令。当然,为了让代码更优雅,我们可以安装Pillow库来处理截图,用opencv-python来做简单的图像识别(进阶需求),用schedule库来做定时任务。但最基础的版本,只需要Python本身。

注意:如果你电脑上有多个Python版本,务必确认你使用的pip和python命令指向的是同一个版本。在命令行输入python --versionpip --version查看,避免库装错了地方。

安装核心辅助库的命令很简单:

pip install Pillow schedule

至于opencv-python,因为体积较大且安装可能遇到问题,我们初期可以先不装,用更简单的坐标定位法。

2.2 ADB工具配置详解

ADB是谷歌官方提供的安卓调试桥,它是我们与手机通信的“电缆”。配置ADB有三步:

  1. 下载ADB工具包:最简单的方法是下载Android SDK Platform-Tools。你可以去安卓开发者官网找,或者在一些可靠的软件下载站搜索“Platform-Tools rxx.x.x”。下载后得到一个文件夹,里面就有adb.exe(Windows)或adb(Mac/Linux)。

  2. 配置系统环境变量:这是为了让你的Python脚本或命令行在任何位置都能调用adb命令。将上一步中adb工具所在的目录路径,添加到系统的PATH环境变量中。添加完成后,打开一个新的命令行窗口,输入adb version,如果能看到版本号信息,说明配置成功。

  3. 连接手机并开启调试模式:用USB数据线连接安卓手机和电脑。在手机上进入【设置】->【关于手机】,连续点击“版本号”7次,开启“开发者选项”。然后进入【开发者选项】,找到并开启“USB调试”。首次连接时,手机会弹出“允许USB调试吗?”的对话框,勾选“始终允许”并确认。

连接成功后,在电脑命令行输入adb devices。如果看到设备列表中出现你的设备序列号,后面跟着device字样(而不是unauthorized),恭喜你,通道已经打通了。

实操心得:很多连接问题出在驱动上。如果adb devices显示unauthorized,检查手机端的授权弹窗;如果设备根本不在列表里,可能是电脑缺少对应的USB驱动,可以去手机厂商官网下载对应的驱动安装。使用一些第三方手机助手软件,有时也能自动安装好驱动。

3. 核心思路与ADB命令精讲

我们的目标是模拟用户。用户刷视频App的核心动作无非是:启动App、滑动屏幕、点击点赞/评论、偶尔点进详情页。对应到ADB命令,我们需要掌握几个最关键的“武器”。

3.1 控制逻辑:Python如何驱动ADB

Python通过subprocess模块来执行系统命令。基本模式是:subprocess.run([‘adb’, ‘shell’, ‘具体的手机端命令’], capture_output=True, text=True)。这样,Python就能获取到ADB命令执行后的结果,进而做判断和决策。整个脚本的逻辑就是一个**“感知-决策-执行”**的循环:通过ADB获取当前屏幕信息(感知),根据预设规则判断该做什么(决策),再发送相应的ADB操作命令(执行)。

3.2 必备ADB命令库

下面这些命令是我们脚本的基石,务必理解其用法和输出。

  • 模拟点击:input tap <x> <y>这是最常用的命令。<x><y>是屏幕坐标。坐标原点(0,0)在屏幕左上角。你需要先获取要点击位置的坐标。
  • 模拟滑动:input swipe <x1> <y1> <x2> <y2> [duration(ms)]模拟从点(x1, y1)滑动到点(x2, y2)。可选的duration参数表示滑动过程持续的毫秒数,值越大滑动越慢。刷视频上滑通常用这个。
  • 模拟按键:input keyevent <键值>例如input keyevent 4模拟返回键,input keyevent 3模拟Home键,input keyevent 26模拟电源键。键值表可以网上搜索“Android keyevent code”。
  • 启动应用:am start -n <包名/活动名>这是启动App的关键。包名是应用的唯一标识,活动名是应用启动的第一个界面。获取方式:打开目标App,然后在电脑命令行输入adb shell dumpsys window | findstr mCurrentFocus(Windows)或adb shell dumpsys window | grep mCurrentFocus(Mac/Linux)。输出类似mCurrentFocus=Window{... com.ss.android.ugc.aweme/com.ss.android.ugc.aweme.splash.SplashActivity},那么com.ss.android.ugc.aweme就是包名,com.ss.android.ugc.aweme.splash.SplashActivity就是活动名。
  • 获取当前屏幕截图:screencap -p /sdcard/screen.png将当前屏幕截图保存到手机存储。然后可以用adb pull /sdcard/screen.png .命令拉到电脑上分析。
  • 获取屏幕分辨率:wm size输出类似Physical size: 1080x2340。这个信息至关重要,因为坐标是相对于分辨率的。如果你的脚本要在不同分辨率的手机上运行,就需要根据这个分辨率来动态计算坐标比例。

注意事项:不同手机厂商的ROM可能对ADB命令的支持有细微差异。例如,有些手机需要额外的权限才能执行input命令。如果遇到命令无效的情况,可以尝试在命令前加上adb shell,并以root权限执行(如果手机已root)。但大多数情况下,开启USB调试后,上述基础命令都是可用的。

4. 实战:构建自动刷视频脚本

理论说得再多,不如动手写一遍。我们以模拟刷短视频为例,构建一个基础脚本。这个脚本会完成:打开App,不断上滑刷新视频,随机间隔点赞,运行一段时间后自动停止。

4.1 脚本框架与基础函数封装

首先,我们把常用的ADB操作封装成Python函数,让主逻辑更清晰。

import subprocess import time import random import os def run_adb_command(cmd): """执行ADB命令并返回结果""" # 注意:这里cmd应该是一个列表,例如 ['adb', 'shell', 'input', 'tap', '500', '1000'] result = subprocess.run(cmd, capture_output=True, text=True, shell=True) return result.stdout.strip() def get_screen_resolution(): """获取手机屏幕分辨率""" output = run_adb_command(['adb', 'shell', 'wm', 'size']) # 输出示例:Physical size: 1080x2340 if 'x' in output: resolution = output.split(':')[-1].strip() width, height = map(int, resolution.split('x')) return width, height else: print("无法获取分辨率,使用默认值 1080x2340") return 1080, 2340 def tap_screen(x, y): """在指定坐标(x, y)模拟点击""" run_adb_command(['adb', 'shell', 'input', 'tap', str(x), str(y)]) def swipe_up(duration=300): """从屏幕底部中央向上滑动,模拟刷视频""" width, height = get_screen_resolution() start_x = width // 2 start_y = height * 4 // 5 # 起始点在屏幕下方4/5处 end_x = start_x end_y = height // 5 # 结束点在屏幕上方1/5处 run_adb_command(['adb', 'shell', 'input', 'swipe', str(start_x), str(start_y), str(end_x), str(end_y), str(duration)]) def launch_app(package, activity): """启动指定的应用程序""" run_adb_command(['adb', 'shell', 'am', 'start', '-n', f'{package}/{activity}']) def take_screenshot(filename='screen.png'): """截取手机屏幕并拉到电脑当前目录""" phone_path = f'/sdcard/{filename}' run_adb_command(['adb', 'shell', 'screencap', '-p', phone_path]) run_adb_command(['adb', 'pull', phone_path, '.']) print(f"截图已保存为 {filename}")

4.2 核心循环逻辑与行为模拟

接下来,我们编写主函数,将上述操作串联成一个自动化流程。

def main(): # 1. 准备工作 print("=== 安卓自动刷视频脚本启动 ===") screen_width, screen_height = get_screen_resolution() print(f"检测到屏幕分辨率: {screen_width}x{screen_height}") # 以某短视频App为例,包名和活动名需要根据实际情况替换 # 获取方法见上文 3.2 节 app_package = "com.ss.android.ugc.aweme" app_activity = "com.ss.android.ugc.aweme.splash.SplashActivity" # 2. 启动App print(f"正在启动 {app_package}...") launch_app(app_package, app_activity) time.sleep(5) # 等待App完全启动 # 3. 定义点赞坐标(示例坐标,需根据你的手机和App界面调整!) # 点赞按钮通常位于屏幕右侧。这里假设在 (屏幕宽度-100, 屏幕高度/2) 附近 like_button_x = screen_width - 100 like_button_y = screen_height // 2 # 4. 主循环:刷视频 total_swipes = 50 # 计划刷的视频数量 swipe_count = 0 try: while swipe_count < total_swipes: print(f"\n--- 第 {swipe_count + 1} 个视频 ---") # 随机观看一段时间(模拟真人阅读) watch_time = random.uniform(3, 8) print(f"观看中... ({watch_time:.1f}秒)") time.sleep(watch_time) # 随机决定是否点赞(30%概率) if random.random() < 0.3: print(f"执行点赞 @ ({like_button_x}, {like_button_y})") tap_screen(like_button_x, like_button_y) time.sleep(0.5) # 等待点赞动画 # 上滑,刷到下一个视频 print("上滑到下一个视频") swipe_up(duration=400) # 400毫秒的滑动,比较自然 swipe_count += 1 # 随机间隔,避免过于规律 interval = random.uniform(1, 3) time.sleep(interval) except KeyboardInterrupt: print("\n用户中断脚本执行。") finally: print("=== 脚本运行结束 ===") if __name__ == "__main__": main()

4.3 坐标获取与适配技巧

脚本里最头疼的就是坐标(like_button_x, like_button_y)怎么定。硬编码的坐标换台手机或者App界面改版就失效了。这里分享两个方法:

  1. 开发者选项-指针位置:在手机【开发者选项】里,开启“指针位置”。屏幕上会显示当前触摸点的坐标。你手动点一下点赞按钮,就能看到坐标值。把这个值填到脚本里。这是最准确的方法。
  2. 截图比例计算:先手动截图(adb shell screencap)拉到电脑上,用画图软件打开,鼠标悬停在点赞按钮上,看软件显示的坐标。这个坐标是基于截图图片的。然后,你需要根据手机真实分辨率截图图片分辨率进行等比换算。公式是:真实X = (图片X / 图片宽度) * 手机真实宽度

实操心得:对于固定位置的按钮(如点赞、评论、分享),用方法一获取一次坐标即可。但为了脚本的健壮性,最好能写一个坐标配置文件,针对不同分辨率预设几套坐标方案,脚本运行时根据检测到的分辨率自动选择。

5. 进阶:让脚本更智能与健壮

基础脚本只能按固定坐标操作,很脆弱。要让它更接近真人,更稳定,我们需要引入一些进阶策略。

5.1 基于图像识别的元素定位(初阶)

完全依赖坐标会“刻舟求剑”。我们可以用图像匹配来寻找按钮。比如,我们提前保存一个“点赞爱心”的小图标like_icon.png。每次操作前,先截取当前屏幕,然后在截图中寻找这个图标的位置。

这里我们可以使用opencv-python的模板匹配功能。虽然安装稍麻烦,但定位精度和适应性高很多。

import cv2 import numpy as np def find_icon_on_screen(icon_path, threshold=0.8): """ 在手机当前屏幕中查找图标 :param icon_path: 图标模板图片路径 :param threshold: 匹配阈值,0.8表示80%相似度以上认为匹配 :return: 匹配区域的中心坐标 (x, y),未找到返回None """ # 1. 获取当前屏幕截图 take_screenshot('current_screen.png') screen_img = cv2.imread('current_screen.png') icon_img = cv2.imread(icon_path) # 2. 进行模板匹配 result = cv2.matchTemplate(screen_img, icon_img, cv2.TM_CCOEFF_NORMED) min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) # 3. 判断是否匹配成功 if max_val >= threshold: icon_h, icon_w = icon_img.shape[:2] center_x = max_loc[0] + icon_w // 2 center_y = max_loc[1] + icon_h // 2 print(f"找到图标,置信度{max_val:.2f},中心坐标({center_x}, {center_y})") return center_x, center_y else: print(f"未找到图标,最高置信度{max_val:.2f}") return None # 在主循环中使用 like_icon_center = find_icon_on_screen('like_icon.png') if like_icon_center: tap_screen(like_icon_center[0], like_icon_center[1]) else: print("未找到点赞按钮,可能界面已变化,执行上滑跳过。") swipe_up()

5.2 异常处理与流程恢复

自动化脚本最怕遇到意外:网络卡顿导致界面加载慢、弹出广告、手机锁屏等等。我们必须让脚本具备一定的容错和自我恢复能力。

def safe_operation(operation_func, *args, max_retries=3, **kwargs): """带重试机制的安全操作""" for i in range(max_retries): try: result = operation_func(*args, **kwargs) return result except Exception as e: print(f"操作失败 (尝试 {i+1}/{max_retries}): {e}") time.sleep(2) # 等待后重试 if i == max_retries - 1: print("操作重试多次仍失败,执行恢复流程。") # 恢复流程:例如按一次返回键,然后重新尝试 run_adb_command(['adb', 'shell', 'input', 'keyevent', '4']) # 返回键 time.sleep(2) raise # 或者执行其他恢复逻辑 # 在主循环中,将关键操作包裹起来 try: safe_operation(swipe_up, duration=400) except Exception as e: print(f"上滑操作最终失败: {e}") # 更激进的恢复:回到桌面再重新打开App run_adb_command(['adb', 'shell', 'input', 'keyevent', '3']) # Home键 time.sleep(2) launch_app(app_package, app_activity) time.sleep(5)

5.3 多设备管理与并发控制

如果你有多台测试机,可以同时运行脚本。关键在于为每台设备指定ADB命令。连接多台设备后,adb devices会列出多个序列号。执行命令时需要加上-s <序列号>参数。

device_serial = "你的设备序列号" def run_adb_command_for_device(cmd, serial): # 在命令中插入 -s 参数 full_cmd = ['adb', '-s', serial] + cmd result = subprocess.run(full_cmd, capture_output=True, text=True, shell=True) return result.stdout.strip()

你可以写一个设备列表,然后用concurrent.futures库的ThreadPoolExecutor来并发执行任务,效率倍增。

6. 常见问题排查与优化实录

在实际操作中,你肯定会遇到各种坑。下面是我踩过的一些,以及解决办法。

6.1 ADB连接不稳定或设备离线

  • 现象:脚本运行一段时间后,adb devices显示设备offline,或者命令无响应。
  • 排查
    1. 检查USB线:劣质或接触不良的USB线是罪魁祸首,换一根原装或质量好的线。
    2. 检查电源管理:有些电脑USB口会休眠,在设备管理器中禁用USB选择性暂停设置。
    3. 重启ADB服务:在命令行执行adb kill-server然后adb start-server
    4. 重新插拔手机:物理重连有时能解决驱动层面的临时故障。
  • 优化:在脚本关键节点(如循环开始)加入连接状态检查,如果发现设备离线,尝试自动重启ADB服务。

6.2 坐标点击无效或错位

  • 现象input tap命令执行了,但手机没反应,或者点错了地方。
  • 排查
    1. 坐标计算错误:确认获取坐标的方法是否正确。使用“指针位置”功能最可靠。
    2. 屏幕旋转:如果手机屏幕旋转了,坐标体系会变。可以在脚本开始时用adb shell settings put system accelerometer_rotation 0锁定旋转,或用adb shell dumpsys display | grep mCurrentOrientation获取当前方向来动态计算。
    3. 导航栏/状态栏:有些坐标计算是否包含了状态栏的高度?全屏应用和普通应用的坐标原点可能不同。
  • 优化:采用基于图像识别的定位方法,从根本上避免对绝对坐标的依赖。

6.3 脚本被系统或App杀死

  • 现象:脚本在后台运行一段时间后自动停止。
  • 排查
    1. 手机省电策略:在手机设置中,将你使用的Python IDE(如PyCharm)或命令行工具,以及目标测试App,加入“后台无限制”或“电池优化白名单”。
    2. App后台活动限制:有些安卓系统会严格限制后台App的活动。确保测试App在前台运行。
  • 优化:让脚本定时模拟一些轻微操作(如轻微移动一下鼠标或按一下音量键),防止系统判定为无活动。

6.4 操作频率过快被识别为机器人

  • 现象:App弹出验证码,或直接限制账号功能。
  • 排查:你的操作间隔太规律、太快,不像真人。
  • 优化
    1. 随机化:所有等待时间(time.sleep)都用random.uniform(a, b)生成一个区间内的随机值。
    2. 加入人性化抖动:在滑动操作中,加入微小的轨迹偏移,而不是完美的直线。
    3. 模拟复杂手势:偶尔模拟长按、双击等操作。
    4. 控制总时长:不要7x24小时不间断运行,模拟人类的作息,运行几小时就休息一段时间。

这套Python + ADB的自动化方案,其魅力在于用极简的工具组合,实现了强大的自动化能力。它可能没有Appium那样全面的元素定位和跨平台能力,但在执行速度、资源占用和对系统底层控制的直接性上,有着无可比拟的优势。对于固定流程的重复任务、竞品体验测试、或者一些轻量级的爬虫场景,它都是一个高效而优雅的选择。最关键的是,整个开发和调试过程非常直观,所见即所得,能让你快速获得正反馈,并深入理解安卓应用的交互本质。