Python + uiautomator2 手机自动化控制教程
简介
uiautomator2 是比 ADB 更强大的 Android 自动化框架,支持元素定位、控件操作、应用管理等高级功能。本教程适合需要更精细控制的开发者。
一、环境准备
1.1 前置要求
- Python 3.6 或更高版本
- Android 手机(需开启开发者模式和 USB 调试)
- USB 数据线
- 已安装 ADB 工具(参考第一篇教程)
1.2 检查 Python 环境
python --version
# 应显示 Python 3.6 或更高版本
1.3 检查 ADB 连接
adb devices
# 应显示已连接的设备
二、安装 uiautomator2
2.1 安装 Python 库
pip install uiautomator2
使用国内镜像加速:
pip install uiautomator2 -i https://pypi.tuna.tsinghua.edu.cn/simple
2.2 初始化手机环境
重要步骤: 首次使用需要在手机上安装 ATX-Agent 和 uiautomator 服务。
# 方法一:使用命令行工具初始化
python -m uiautomator2 init# 方法二:使用 Python 脚本初始化
python
>>> import uiautomator2 as u2
>>> u2.connect_usb() # 会自动安装必要组件
>>> exit()
初始化过程:
- 会在手机上安装两个 APK:
ATXAgent.apk- 核心服务app-uiautomator.apk或app-uiautomator-test.apk- UI 自动化服务
- 安装过程需要 1-3 分钟
- 手机上会弹出安装提示,点击"安装"
2.3 验证安装
创建测试文件 test_u2.py:
import uiautomator2 as u2# 连接设备
d = u2.connect() # 自动连接 USB 设备
print("设备信息:", d.info)
print("✅ uiautomator2 安装成功!")
运行测试:
python test_u2.py
成功会显示设备信息(型号、分辨率、Android 版本等)。
三、uiautomator2 核心功能
3.1 设备连接方式
import uiautomator2 as u2# 方式1:自动连接(推荐)
d = u2.connect()# 方式2:通过 USB 连接
d = u2.connect_usb()# 方式3:通过设备序列号连接
d = u2.connect('device_serial')# 方式4:通过 WiFi 连接(需先配置)
d = u2.connect('192.168.1.100')
3.2 基础操作
# 获取设备信息
print(d.info)# 获取屏幕分辨率
width, height = d.window_size()
print(f"分辨率: {width}x{height}")# 截图
d.screenshot("screen.png")# 点击坐标
d.click(500, 1000)# 滑动
d.swipe(500, 1500, 500, 500, 0.3) # 从下往上滑# 长按
d.long_click(500, 1000)# 输入文本
d.send_keys("Hello World")# 按键操作
d.press("home") # 返回主屏幕
d.press("back") # 返回键
3.3 应用管理
# 启动应用
d.app_start("com.ss.android.ugc.aweme")# 停止应用
d.app_stop("com.ss.android.ugc.aweme")# 清除应用数据
d.app_clear("com.ss.android.ugc.aweme")# 检查应用是否运行
if d.app_current().get('package') == "com.ss.android.ugc.aweme":print("抖音正在运行")# 获取当前应用信息
current = d.app_current()
print(f"当前应用: {current['package']}")
四、编写自动化脚本
4.1 基础滑动脚本
创建 u2_basic_swipe.py:
"""
uiautomator2 基础滑动脚本
功能:自动滑动视频
"""import uiautomator2 as u2
import timedef main():# 连接设备d = u2.connect()print(f"✅ 已连接设备: {d.info['productName']}")# 获取屏幕尺寸width, height = d.window_size()print(f"📱 屏幕分辨率: {width}x{height}")# 配置参数interval = 5 # 滑动间隔(秒)count = 0print(f"⏰ 每 {interval} 秒滑动一次")print("按 Ctrl+C 停止\n")try:while True:count += 1# 从屏幕下方滑到上方# 使用相对坐标,自适应不同分辨率d.swipe(width // 2, # x: 屏幕中央height * 0.8, # y1: 屏幕 80% 位置width // 2, # x: 保持在中央height * 0.2, # y2: 屏幕 20% 位置0.3 # 滑动持续时间(秒))print(f"[{count}] {time.strftime('%H:%M:%S')} ✅ 滑动成功")time.sleep(interval)except KeyboardInterrupt:print(f"\n⏹️ 已停止,共执行 {count} 次滑动")except Exception as e:print(f"\n❌ 发生错误: {e}")if __name__ == "__main__":main()
运行:
python u2_basic_swipe.py
4.2 高级功能脚本
创建 u2_advanced_swipe.py:
"""
uiautomator2 高级自动化脚本
功能:
1. 启动指定应用
2. 智能检测页面状态
3. 支持多种滑动模式
4. 异常处理和自动恢复
"""import uiautomator2 as u2
import time
import random
from datetime import datetimeclass VideoAutomation:def __init__(self, package_name, interval=5, total_count=None):"""初始化视频自动化控制器参数:package_name: 应用包名interval: 滑动间隔(秒)total_count: 总滑动次数(None=无限次)"""self.package_name = package_nameself.interval = intervalself.total_count = total_countself.device = Noneself.count = 0self.width = 0self.height = 0def connect(self):"""连接设备"""try:self.device = u2.connect()self.width, self.height = self.device.window_size()print(f"✅ 已连接设备: {self.device.info.get('productName', 'Unknown')}")print(f"📱 分辨率: {self.width}x{self.height}")print(f"🤖 Android 版本: {self.device.info.get('version', 'Unknown')}")return Trueexcept Exception as e:print(f"❌ 连接设备失败: {e}")return Falsedef start_app(self):"""启动应用"""try:print(f"🚀 正在启动应用: {self.package_name}")self.device.app_start(self.package_name)time.sleep(3) # 等待应用启动# 验证应用是否启动成功current = self.device.app_current()if current.get('package') == self.package_name:print(f"✅ 应用启动成功")return Trueelse:print(f"⚠️ 启动的应用不匹配,当前: {current.get('package')}")return Falseexcept Exception as e:print(f"❌ 启动应用失败: {e}")return Falsedef check_app_running(self):"""检查应用是否在前台运行"""try:current = self.device.app_current()return current.get('package') == self.package_nameexcept:return Falsedef swipe_up(self):"""向上滑动(下一个视频)"""try:# 使用屏幕中央位置滑动x = self.width // 2y1 = int(self.height * 0.75) # 起始位置:75%y2 = int(self.height * 0.25) # 结束位置:25%self.device.swipe(x, y1, x, y2, 0.3)self.count += 1return Trueexcept Exception as e:print(f"❌ 滑动失败: {e}")return Falsedef swipe_down(self):"""向下滑动(上一个视频)"""try:x = self.width // 2y1 = int(self.height * 0.25)y2 = int(self.height * 0.75)self.device.swipe(x, y1, x, y2, 0.3)return Trueexcept Exception as e:print(f"❌ 滑动失败: {e}")return Falsedef random_swipe(self):"""随机滑动(模拟人类行为)"""# 90% 概率向上,10% 概率向下if random.random() < 0.9:return self.swipe_up()else:print(" ↓ 随机向下滑动")return self.swipe_down()def should_continue(self):"""判断是否继续执行"""if self.total_count is None:return Truereturn self.count < self.total_countdef get_random_interval(self):"""获取随机间隔时间(模拟人类)"""# 在设定间隔基础上 ±20% 浮动variation = self.interval * 0.2return self.interval + random.uniform(-variation, variation)def run(self):"""运行自动化任务"""if not self.connect():returnif not self.start_app():returnprint(f"\n⏰ 平均滑动间隔: {self.interval} 秒(随机波动)")if self.total_count:print(f"🎯 目标次数: {self.total_count} 次")else:print(f"🔄 持续运行(按 Ctrl+C 停止)")print("-" * 50)start_time = time.time()try:while self.should_continue():# 检查应用是否还在前台if not self.check_app_running():print("⚠️ 应用不在前台,尝试重新启动...")if not self.start_app():break# 执行滑动current_time = datetime.now().strftime('%H:%M:%S')print(f"[{self.count + 1}] {current_time} ", end="")if self.random_swipe():print("✅")else:print("❌ 失败,继续...")# 等待随机间隔if self.should_continue():wait_time = self.get_random_interval()time.sleep(wait_time)except KeyboardInterrupt:print("\n\n⏹️ 用户停止")except Exception as e:print(f"\n❌ 发生错误: {e}")finally:elapsed = time.time() - start_timeself.print_statistics(elapsed)def print_statistics(self, elapsed_time):"""打印统计信息"""print(f"\n{'='*50}")print(f"📊 运行统计:")print(f" - 总滑动次数: {self.count}")print(f" - 运行时长: {int(elapsed_time)} 秒 ({int(elapsed_time/60)} 分钟)")if self.count > 0:print(f" - 平均间隔: {elapsed_time/self.count:.1f} 秒")print(f"{'='*50}")def main():import argparse# 解析命令行参数parser = argparse.ArgumentParser(description='uiautomator2 视频自动化脚本')parser.add_argument('package', help='应用包名,如: com.ss.android.ugc.aweme')parser.add_argument('-i', '--interval', type=float, default=5.0,help='滑动间隔(秒),默认5秒')parser.add_argument('-n', '--count', type=int, default=None,help='滑动次数,不指定则无限循环')args = parser.parse_args()# 创建并运行自动化任务automation = VideoAutomation(package_name=args.package,interval=args.interval,total_count=args.count)automation.run()if __name__ == "__main__":main()
4.3 智能元素定位脚本
创建 u2_smart_control.py:
"""
基于 UI 元素的智能控制脚本
功能:通过识别页面元素来智能操作
"""import uiautomator2 as u2
import timeclass SmartController:def __init__(self, package_name):self.package_name = package_nameself.device = u2.connect()def start(self):"""启动应用"""self.device.app_start(self.package_name)time.sleep(2)def wait_and_click(self, text=None, description=None, timeout=10):"""等待元素出现并点击"""try:if text:# 通过文本查找并点击self.device(text=text).click(timeout=timeout)print(f"✅ 点击了: {text}")return Trueelif description:# 通过描述查找并点击self.device(description=description).click(timeout=timeout)print(f"✅ 点击了: {description}")return Trueexcept u2.exceptions.UiObjectNotFoundError:print(f"⚠️ 未找到元素")return Falsedef swipe_if_video_exists(self):"""如果检测到视频元素就滑动"""# 示例:检测特定 ID 的视频容器if self.device(resourceId="video_container").exists:width, height = self.device.window_size()self.device.swipe(width//2, height*0.8, width//2, height*0.2, 0.3)print("✅ 检测到视频,已滑动")return Truereturn Falsedef auto_handle_popups(self):"""自动处理弹窗"""# 常见弹窗按钮文本close_texts = ["关闭", "取消", "不感兴趣", "跳过", "我知道了"]for text in close_texts:if self.device(text=text).exists:self.device(text=text).click()print(f"✅ 关闭弹窗: {text}")time.sleep(0.5)return Truereturn False# 使用示例
if __name__ == "__main__":controller = SmartController("com.ss.android.ugc.aweme")controller.start()# 自动处理启动页弹窗time.sleep(2)controller.auto_handle_popups()# 持续滑动for i in range(10):controller.swipe_if_video_exists()time.sleep(5)
五、运行指南
5.1 基础脚本运行
# 运行基础滑动脚本
python u2_basic_swipe.py
5.2 高级脚本运行
抖音自动刷视频(每5秒一次):
python u2_advanced_swipe.py com.ss.android.ugc.aweme
快手自动刷视频(每8秒一次,共50次):
python u2_advanced_swipe.py com.smile.gifmaker -i 8 -n 50
B站自动刷视频(每3秒一次):
python u2_advanced_swipe.py tv.danmaku.bili -i 3
5.3 查看帮助
python u2_advanced_swipe.py -h
六、WiFi 无线连接配置
6.1 配置步骤
1. 确保手机和电脑在同一 WiFi 网络
2. 通过 USB 启用 WiFi 调试:
# 查看手机 IP 地址
adb shell ip addr show wlan0# 启用 WiFi ADB(端口 5555)
adb tcpip 5555# 记下手机 IP 地址(如 192.168.1.100)
3. 连接 WiFi:
# 断开 USB 线
adb connect 192.168.1.100:5555
4. 在脚本中使用 WiFi 连接:
import uiautomator2 as u2# 使用 IP 地址连接
d = u2.connect('192.168.1.100')
6.2 WiFi 连接优势
- 无需 USB 线,更自由
- 可以同时控制多台手机
- 适合长时间运
行
七、UI 元素定位技巧
7.1 查看页面元素
安装 weditor(UI 查看器):
pip install weditor
启动 weditor:
python -m weditor
浏览器会自动打开 http://localhost:17310
使用方法:
- 输入设备 IP 或序列号,点击 Connect
- 点击 Dump Hierarchy 获取当前页面结构
- 点击元素查看属性(text、resourceId、className 等)
- 可以实时测试定位表达式
7.2 常用定位方法
import uiautomator2 as u2
d = u2.connect()# 通过文本定位
d(text="立即登录").click()# 通过文本包含
d(textContains="登录").click()# 通过 resourceId 定位
d(resourceId="com.app:id/button").click()# 通过 className 定位
d(className="android.widget.Button").click()# 通过 description 定位
d(description="确认按钮").click()# 组合定位
d(className="android.widget.Button", text="确认").click()# 索引定位(多个相同元素时)
d(className="android.widget.Button")[0].click() # 第一个按钮
d(className="android.widget.Button")[1].click() # 第二个按钮
7.3 等待元素出现
# 等待元素出现(最多等待10秒)
d(text="加载完成").wait(timeout=10)# 等待元素消失
d(text="加载中...").wait_gone(timeout=10)# 判断元素是否存在
if d(text="登录").exists:print("找到登录按钮")# 获取元素信息
info = d(text="登录").info
print(info)
八、高级功能示例
8.1 自动点赞脚本
"""
视频自动点赞脚本
"""
import uiautomator2 as u2
import timed = u2.connect()
d.app_start("com.ss.android.ugc.aweme")
time.sleep(3)for i in range(10):# 双击屏幕点赞(适用于抖音)width, height = d.window_size()d.double_click(width//2, height//2)print(f"❤️ 点赞第 {i+1} 次")time.sleep(1)# 滑动到下一个视频d.swipe(width//2, height*0.8, width//2, height*0.2, 0.3)time.sleep(5)
8.2 自动关注脚本
"""
自动关注作者脚本
"""
import uiautomator2 as u2
import timed = u2.connect()
d.app_start("com.ss.android.ugc.aweme")
time.sleep(3)for i in range(5):# 查找并点击关注按钮if d(text="关注").exists:d(text="关注").click()print(f"✅ 关注第 {i+1} 个作者")elif d(textContains="关注").exists:d(textContains="关注").click()print(f"✅ 关注第 {i+1} 个作者")else:print("⚠️ 未找到关注按钮")time.sleep(2)# 滑到下一个视频width, height = d.window_size()d.swipe(width//2, height*0.8, width//2, height*0.2, 0.3)time.sleep(5)
8.3 截图并保存
"""
定时截图脚本
"""
import uiautomator2 as u2
import time
from datetime import datetimed = u2.connect()
d.app_start("com.ss.android.ugc.aweme")for i in range(10):# 生成带时间戳的文件名timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")filename = f"screenshot_{timestamp}.png"# 截图d.screenshot(filename)print(f"📸 已保存截图: {filename}")# 滑动width, height = d.window_size()d.swipe(width//2, height*0.8, width//2, height*0.2, 0.3)time.sleep(5)
8.4 监控屏幕变化
"""
监控屏幕变化脚本
"""
import uiautomator2 as u2
import timed = u2.connect()# 获取初始截图
last_screen = d.screenshot()while True:time.sleep(1)current_screen = d.screenshot()# 简单对比(可用图像相似度算法优化)if current_screen != last_screen:print("🔄 屏幕内容已变化")last_screen = current_screen
九、故障排查
9.1 “ConnectionError: Unable to connect to device”
解决方案:
# 1. 检查 ADB 连接
adb devices# 2. 重新初始化 uiautomator2
python -m uiautomator2 init# 3. 检查 ATX-Agent 是否运行
adb shell ps | grep atx# 4. 重启 ATX-Agent
adb shell am force-stop com.github.uiautomator
python -m uiautomator2 init
9.2 元素定位失败
解决方案:
- 使用 weditor 查看实际元素属性
- 检查元素是否在屏幕可见区域
- 增加等待时间
- 使用更精确的定位方式
# 添加等待
d(text="按钮").wait(timeout=10)# 尝试不同定位方式
if not d(text="登录").exists:d(textContains="登录").click()
9.3 “UiObjectNotFoundError”
解决方案:
# 使用异常处理
try:d(text="按钮").click()
except u2.exceptions.UiObjectNotFoundError:print("元素不存在,跳过")# 或先判断
if d(text="按钮").exists:d(text="按钮").click()
9.4 操作响应慢
解决方案:
# 设置操作超时时间
d.settings['operation_delay'] = (0, 0) # 点击前后延迟
d.settings['operation_delay_methods'] = ['click', 'swipe']# 设置 implicitly_wait
d.implicitly_wait(10) # 隐式等待10秒
9.5 WiFi 连接断开
解决方案:
# 重新连接
adb connect <手机IP>:5555# 保持连接
adb connect <手机IP>:5555
adb -s <手机IP>:5555 shell settings put global stay_on_while_plugged_in 7
十、性能优化
10.1 加快操作速度
# 关闭等待时间
d.settings['wait_timeout'] = 10 # 全局等待超时
d.settings['operation_delay'] = (0, 0) # 操作延迟# 使用 shell 命令代替高级 API
d.shell("input swipe 500 1500 500 500 100") # 更快
10.2 减少资源占用
# 不需要截图时关闭
# 使用 shell 命令代替截图检查# 批量操作
for i in range(100):d.swipe(500, 1500, 500, 500, 0.1) # 减少滑动时间
10.3 异步操作
import asyncio
import uiautomator2 as u2async def swipe_task(device, count):for i in range(count):width, height = device.window_size()device.swipe(width//2, height*0.8, width//2, height*0.2, 0.3)await asyncio.sleep(3)async def main():d = u2.connect()await swipe_task(d, 10)asyncio.run(main())
十一、完整项目结构
phone-automation-u2/
├── test_u2.py # 测试连接
├── u2_basic_swipe.py # 基础滑动
├── u2_advanced_swipe.py # 高级滑动
├── u2_smart_control.py # 智能控制
├── screenshots/ # 截图目录
│ └── screenshot_*.png
└── logs/ # 日志目录└── automation.log
十二、uiautomator2 vs ADB 对比
| 功能 | uiautomator2 | ADB |
|---|---|---|
| 元素定位 | ✅ 支持 | ❌ 不支持 |
| 应用管理 | ✅ 便捷 | ⚠️ 需命令 |
| WiFi 连接 | ✅ 简单 | ⚠️ 需配置 |
| 学习曲线 | ⚠️ 中等 | ✅ 简单 |
| 性能 | ⚠️ 中等 | ✅ 快速 |
| 功能丰富度 | ✅ 丰富 | ⚠️ 基础 |
| 配置复杂度 | ⚠️ 需初始化 | ✅ 简单 |
十三、实战案例
13.1 完整的抖音自动刷视频脚本
"""
抖音自动刷视频完整脚本
功能:
- 自动启动抖音
- 智能跳过广告
- 随机间隔滑动
- 异常自动恢复
"""import uiautomator2 as u2
import time
import random
from datetime import datetimeclass DouyinAutomation:def __init__(self):self.device = u2.connect()self.package = "com.ss.android.ugc.aweme"self.count = 0def start(self):"""启动抖音"""print("🚀 启动抖音...")self.device.app_start(self.package)time.sleep(3)self.handle_popups()def handle_popups(self):"""处理启动弹窗"""close_buttons = ["关闭", "跳过", "我知道了", "取消"]for btn in close_buttons:if self.device(text=btn).exists:self.device(text=btn).click()print(f"✅ 关闭弹窗: {btn}")time.sleep(0.5)def swipe_next(self):"""滑到下一个视频"""width, height = self.device.window_size()# 添加轻微随机偏移x = width // 2 + random.randint(-50, 50)y1 = int(height * 0.75) + random.randint(-20, 20)y2 = int(height * 0.25) + random.randint(-20, 20)duration = random.uniform(0.25, 0.35)self.device.swipe(x, y1, x, y2, duration)self.count += 1def check_status(self):"""检查应用状态"""current = self.device.app_current()if current.get('package') != self.package:print("⚠️ 应用不在前台,重新启动")self.start()return Falsereturn Truedef run(self, duration_minutes=30):"""运行指定时长"""self.start()end_time = time.time() + duration_minutes * 60print(f"🎬 开始刷视频,持续 {duration_minutes} 分钟")print("-" * 50)try:while time.time() < end_time:if not self.check_status():continue# 滑动timestamp = datetime.now().strftime('%H:%M:%S')print(f"[{self.count + 1}] {timestamp} ✅")self.swipe_next()# 随机间隔 4-8 秒interval = random.uniform(4, 8)time.sleep(interval)except KeyboardInterrupt:print("\n⏹️ 用户停止")finally:print(f"\n📊 总共观看 {self.count} 个视频")if __name__ == "__main__":automation = DouyinAutomation()automation.run(duration_minutes=10) # 运行10分钟
十四、总结
恭喜完成 uiautomator2 教程!你现在已经掌握:
✅ uiautomator2 环境搭建和初始化
✅ 设备连接(USB 和 WiFi)
✅ 基础操作(点击、滑动、输入等)
✅ UI 元素定位技巧
✅ 应用管理和控制
✅ 高级功能实现
✅ 异常处理和优化
uiautomator2 优势:
- 功能强大,支持复杂交互
- 可以精确定位 UI 元素
- 支持无线连接
- Python API 友好
适用场景:
- 需要元素定位的自动化
- 复杂交互逻辑
- 应用测试
- UI 自动化操作
下一步建议:
- 学习图像识别(OpenCV)
- 研究 OCR 文字识别
- 探索 AI 辅助自动化
- 学习 Appium 实现跨平台自动化
参考资源:
- uiautomator2 文档:https://github.com/openatx/uiautomator2
- weditor 工具:https://github.com/alibaba/web-editor
- Android UI 文档:https://developer.android.com/reference/android/support/test/uiautomator/package-summary
