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 time defmain():# 连接设备 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:whileTrue: 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 datetime classVideoAutomation:def__init__(self, package_name, interval=5, total_count=None):""" 初始化视频自动化控制器 参数: package_name: 应用包名 interval: 滑动间隔(秒) total_count: 总滑动次数(None=无限次) """ self.package_name = package_name self.interval = interval self.total_count = total_count self.device =None self.count =0 self.width =0 self.height =0defconnect(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')}")returnTrueexcept Exception as e:print(f"❌ 连接设备失败: {e}")returnFalsedefstart_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"✅ 应用启动成功")returnTrueelse:print(f"⚠️ 启动的应用不匹配,当前: {current.get('package')}")returnFalseexcept Exception as e:print(f"❌ 启动应用失败: {e}")returnFalsedefcheck_app_running(self):"""检查应用是否在前台运行"""try: current = self.device.app_current()return current.get('package')== self.package_name except:returnFalsedefswipe_up(self):"""向上滑动(下一个视频)"""try:# 使用屏幕中央位置滑动 x = self.width //2 y1 =int(self.height *0.75)# 起始位置:75% y2 =int(self.height *0.25)# 结束位置:25% self.device.swipe(x, y1, x, y2,0.3) self.count +=1returnTrueexcept Exception as e:print(f"❌ 滑动失败: {e}")returnFalsedefswipe_down(self):"""向下滑动(上一个视频)"""try: x = self.width //2 y1 =int(self.height *0.25) y2 =int(self.height *0.75) self.device.swipe(x, y1, x, y2,0.3)returnTrueexcept Exception as e:print(f"❌ 滑动失败: {e}")returnFalsedefrandom_swipe(self):"""随机滑动(模拟人类行为)"""# 90% 概率向上,10% 概率向下if random.random()<0.9:return self.swipe_up()else:print(" ↓ 随机向下滑动")return self.swipe_down()defshould_continue(self):"""判断是否继续执行"""if self.total_count isNone:returnTruereturn self.count < self.total_count defget_random_interval(self):"""获取随机间隔时间(模拟人类)"""# 在设定间隔基础上 ±20% 浮动 variation = self.interval *0.2return self.interval + random.uniform(-variation, variation)defrun(self):"""运行自动化任务"""ifnot self.connect():returnifnot 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():# 检查应用是否还在前台ifnot self.check_app_running():print("⚠️ 应用不在前台,尝试重新启动...")ifnot 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_time self.print_statistics(elapsed)defprint_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}")defmain():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 time classSmartController:def__init__(self, package_name): self.package_name = package_name self.device = u2.connect()defstart(self):"""启动应用""" self.device.app_start(self.package_name) time.sleep(2)defwait_and_click(self, text=None, description=None, timeout=10):"""等待元素出现并点击"""try:if text:# 通过文本查找并点击 self.device(text=text).click(timeout=timeout)print(f"✅ 点击了: {text}")returnTrueelif description:# 通过描述查找并点击 self.device(description=description).click(timeout=timeout)print(f"✅ 点击了: {description}")returnTrueexcept u2.exceptions.UiObjectNotFoundError:print(f"⚠️ 未找到元素")returnFalsedefswipe_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("✅ 检测到视频,已滑动")returnTruereturnFalsedefauto_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)returnTruereturnFalse# 使用示例if __name__ =="__main__": controller = SmartController("com.ss.android.ugc.aweme") controller.start()# 自动处理启动页弹窗 time.sleep(2) controller.auto_handle_popups()# 持续滑动for i inrange(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 50B站自动刷视频(每3秒一次):
python u2_advanced_swipe.py tv.danmaku.bili -i 35.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 time d = u2.connect() d.app_start("com.ss.android.ugc.aweme") time.sleep(3)for i inrange(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 time d = u2.connect() d.app_start("com.ss.android.ugc.aweme") time.sleep(3)for i inrange(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 datetime d = u2.connect() d.app_start("com.ss.android.ugc.aweme")for i inrange(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 time d = u2.connect()# 获取初始截图 last_screen = d.screenshot()whileTrue: 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)# 尝试不同定位方式ifnot 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 inrange(100): d.swipe(500,1500,500,500,0.1)# 减少滑动时间10.3 异步操作
import asyncio import uiautomator2 as u2 asyncdefswipe_task(device, count):for i inrange(count): width, height = device.window_size() device.swipe(width//2, height*0.8, width//2, height*0.2,0.3)await asyncio.sleep(3)asyncdefmain(): 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 datetime classDouyinAutomation:def__init__(self): self.device = u2.connect() self.package ="com.ss.android.ugc.aweme" self.count =0defstart(self):"""启动抖音"""print("🚀 启动抖音...") self.device.app_start(self.package) time.sleep(3) self.handle_popups()defhandle_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)defswipe_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 +=1defcheck_status(self):"""检查应用状态""" current = self.device.app_current()if current.get('package')!= self.package:print("⚠️ 应用不在前台,重新启动") self.start()returnFalsereturnTruedefrun(self, duration_minutes=30):"""运行指定时长""" self.start() end_time = time.time()+ duration_minutes *60print(f"🎬 开始刷视频,持续 {duration_minutes} 分钟")print("-"*50)try:while time.time()< end_time:ifnot 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