用 Python 脚本模拟点击 Android APP ------ 全面技术指南
在自动化测试、自动操作、RPA 或数据采集场景中,经常需要在 PC 上用 Python 控制 Android 设备进行点击、输入、滑动等操作。本文从零开始全面介绍几种常见方案:ADB、uiautomator2、pure-python-adb、Appium,以及控件定位、UI dump 解析、坐标适配与稳定性优化等工程实践。
📑 目录
背景与原理速览
PC 控制 Android 的核心方法包括:
-
ADB input :执行
input tap、input swipe等命令模拟触摸事件 -
uiautomator:从 Android UI 层读取控件树,按 resource-id/text 定位控件
-
Appium:跨平台自动化框架,基于 WebDriver
-
pure-python-adb:用 Python 原生实现 adb 协议
不同方法侧重点不同:
ADB 轻量、快速,但定位基于坐标;uiautomator2 和 Appium 提供 UI 树级别的稳定定位方法。
准备工作
-
Android 设备已开启 USB 调试
-
PC 可以执行
adb devices -
Python 环境(≥3.8)
-
常用包(按需安装):
pip install uiautomator2
pip install pure-python-adb
pip install Appium-Python-Client
方法一:ADB 命令(最简单 & 最通用)
这是最基础也最兼容的方式。
📌 Python 调用 ADB 执行点击
import subprocess
import time
ADB = "adb"
def adb(cmd: str):
full = ADB.split() + cmd.split()
return subprocess.check_output(full).decode('utf-8')
def tap(x, y):
adb(f"shell input tap {x} {y}")
def swipe(x1, y1, x2, y2, duration=300):
adb(f"shell input swipe {x1} {y1} {x2} {y2} {duration}")
tap(200, 800)
swipe(300, 1200, 300, 400, 500)
📌 获取屏幕分辨率
adb shell wm size
# Physical size: 1080x2400
✔️ 优点
-
无需额外安装
-
非常快
-
全机型高兼容性
❌ 缺点
-
依赖坐标,UI 改动或分辨率不同都会失效
-
无法直接基于控件定位
方法二:uiautomator2(更稳定的控件定位方式)
最推荐的 Python 自动化方案,支持资源 id、text、xpath 定位控件。
📌 安装初始化
pip install --upgrade uiautomator2
python -m uiautomator2 init
📌 Python 示例
import uiautomator2 as u2
import time
d = u2.connect() # USB 或 IP 连接
# 启动 app
d.app_start("com.example.app")
# 通过 resource-id 选择控件
d(resourceId="com.example.app:id/login").click()
# 通过 text
d(text="下一步").click()
# dump 当前界面 XML
xml = d.dump_hierarchy()
print(xml[:500])
✔️ 优点
-
最易用且最稳
-
能获取控件信息
-
支持 xpath、手势、多点触控
❌ 缺点
-
设备需安装 uiautomator2 服务
-
某些深度定制系统可能不稳定
方法三:pure-python-adb 与 adb shell 结合
适合需要 programmatic adb 的场景,不用频繁创建进程。
📌 安装
pip install pure-python-adb
📌 示例代码
from ppadb.client import Client
client = Client(host="127.0.0.1", port=5037)
device = client.devices()[0]
# 点击
device.shell("input tap 200 800")
# dump UI
device.shell("uiautomator dump /sdcard/uidump.xml")
device.pull("/sdcard/uidump.xml", "uidump.xml")
方法四:Appium(企业级自动化)
适合复杂 UI 自动化 / 多设备并发 / 测试平台集成。
📌 Python 代码示例
from appium import webdriver
from selenium.webdriver.common.by import By
caps = {
"platformName": "Android",
"deviceName": "device",
"appPackage": "com.example.app",
"appActivity": ".MainActivity",
}
driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)
driver.find_element(By.ID, "com.example.app:id/login").click()
driver.quit()
✔️ 强大且标准
❌ 部署复杂,需要 Appium Server
控件定位方式:坐标 / id / text / xpath / UI Dump
🟢 推荐优先级
-
resource-id(最稳定)
-
text
-
className + index / 层次
-
xpath(慢但通用)
-
屏幕坐标(最不稳定,但简单)
不同分辨率适配(坐标比例换算)
如果必须用坐标,建议做分辨率适配。
📌 坐标换算函数
def scale(x, y, base_w, base_h, w, h):
return int(x * w / base_w), int(y * h / base_h)
📌 获取分辨率
adb shell wm size
稳定性与同步策略(等待/重试/断言)
建议实践:
-
不要到处用 sleep
-
使用条件等待:
exists(timeout=...) -
加入 retry(网络波动、动画会导致不稳定)
-
操作后做断言(比如检查文本是否出现)
-
失败时保存截图 + UI dump
示例:带重试的点击
def click_retry(d, selector, tries=3):
for i in range(tries):
if d(selector).exists(timeout=2):
d(selector).click()
return True
return False
常见问题与排查技巧
| 现象 | 排查方向 |
|---|---|
| 设备无响应 / 一直 unauthorize | 重插 USB,确认授权弹窗 |
| 点击不生效 | 坐标不对 / 目标被遮挡 / 动画未结束 |
| uiautomator2 init 失败 | 重启设备 / 切换 USB 端口 |
| dump 不到控件 | 可能是 SurfaceView / Flutter / WebView(可辅助 OCR) |
安全与合规性提醒
-
请确保你对目标设备和目标应用拥有合法操作权限
-
不要用于绕过安全、刷量等非法用途
-
企业环境中操作前注意数据与隐私保护
完整综合示例:从控件 dump 到点击
import uiautomator2 as u2
import re
import time
d = u2.connect()
# 启动 app
d.app_start("com.example.app")
# 首选通过 text 点击
if d(text="确认").exists(timeout=3):
d(text="确认").click()
else:
# 备选:从 XML dump 找 bounds
xml = d.dump_hierarchy()
m = re.search(r'text="确认".*?bounds="(\[.*?\])"', xml)
if m:
bounds = m.group(1)
nums = list(map(int, re.findall(r"\d+", bounds)))
l, t, r, b = nums
cx, cy = (l+r)//2, (t+b)//2
d.click(cx, cy)
总结 & 下一步建议
-
仅需简单点击时:ADB input 最快
-
想稳定可维护:uiautomator2 强烈推荐
-
需要分布式 / 测试体系:选 Appium
-
避免硬编码坐标,优先使用 resource-id、text
-
引入等待、重试、断言让脚本更稳
-
借助 UI dump、截图、日志排查错误