ARTICLE · 1057137
用 Python 写第一段安卓自动化脚本:FIRERPA 选择器定位实战
上一集你能在浏览器里看到手机屏幕。这一集我们让手机自己点起来。
整个过程就一件事:用 Python 库连上远程桌面,找到一个元素,模拟点击、输入、等待。一个能跑的最小脚本大概 15 行。
客户端/服务端模型一句话讲清楚
服务(firerpa)跑在手机上,等你发指令。客户端(Python 库)跑在 PC 上,发指令、收结果。两边通过 Console URL 那条 HTTP/HTTPS 通道对话。
为什么不是 AutoJS 那种"脚本直接跑手机上"?因为集中调度和版本管理在服务器侧更容易。哪天测试 50 台机器,你不用每台手动改一遍脚本,改服务器就行;哪天代码有 bug,回滚就是下次重启的事。
装客户端 + 连接设备
PyPI 上有官方包:
pip install lamda 最小连接代码:
import lamda d = lamda.connect("http://192.168.1.20:65000") # 你第 1 集那个 Console URL print(d.info()) # 看看设备信息:型号、分辨率、Android 版本、当前前台 App 跑通说明网络通。没通基本就是 URL 错了或者手机 PC 不在同一网段,回到上一集那三个坑。
选择器:找到屏幕上的元素
自动化 90% 的时间花在"找元素"。FIRERPA 的选择器体系跟 Appium 一脉相承,按属性匹配:
# 文本完全匹配 d(text="登录") # 文本包含 d(textContains="登录") # 文本正则 d(textMatches=r"^登录.*") # Resource ID(最稳定,优先用) d(resourceId="com.example:id/login_btn") # 类名 d(className="android.widget.Button") # 内容描述(图片按钮常用) d(description="搜索") # 状态 d(clickable=True) 最常用的是 resourceId,因为它稳定;但很多 App 不给按钮 ID,这时候退回 textContains。
第一个能跑的最小脚本
点开微信 → 输入"测试" → 等待右上角"+号"出现:
from lamda import Corner import time # 1. 打开微信 d.shell("am start -n com.tencent.mm/.ui.LauncherUI") time.sleep(2) # 2. 找输入框(在微信主界面顶部"搜索"位置) search = d(resourceId="com.tencent.mm:id/fab") search.click() # 3. 输入文本(Unicode 直接传) search_edit = d(resourceId="com.tencent.mm:id/search_edit_text") search_edit.set_text("测试") # 4. 等待结果出现 d(textContains="测试").wait_for_exists(timeout=10*1000) print("✓ 完成:微信搜索'测试'已触发") 这一段你拷过去就能跑(前提是第 1 集的服务已经启动)。完整脚本里我没把 Corner 全列,是因为大部分按钮默认点中心就够了;要精确点边缘就传 Corner.COR_TOPLEFT 之类的。
选择器重复怎么办:链式收窄
实战里你会反复遇到这种场景:列表 10 条,每条都有一个"删除"按钮,文本全是"删除"。
# 找到第一条消息,再找里面的删除按钮 first_item = d(resourceId="com.example:id/item").instance(0) delete_btn = first_item.child(text="删除") delete_btn.click() instance(N) 选第 N 个匹配;child(...) 进入子级;sibling() 进入兄弟级。这三个组合能解决 80% 的"长得一样"问题。
剩下的 20%?要么回到 layout 检视看完整属性,要么上 OCR(第 5 集讲)。
把脚本做成「自动签到」雏形
完整可运行脚本放文末折叠或仓库,这里给你结构感受:
# 1. 点开 App d.shell("am start -n app.package/.MainActivity") # 2. 等某个独有元素出现(确认 App 已加载) d(resourceId="app:id/home_banner").wait_for_exists(15000) # 3. 找签到入口(可能每天位置不一样,做两次兜底) sign = d(resourceId="app:id/sign_button") if not sign.exists(): d(textContains="签到").click() else: sign.click() # 4. 等「签到成功」提示出现 toast = d(textContains="成功") if toast.wait_for_exists(8000): print("✓ 签到完成") else: print("× 没找到成功提示,可能 UI 变了") 工程经验:每次用 wait_for_exists 而不是 sleep,因为不同设备/不同网络下 App 启动速度不一样,硬 sleep 会要么太慢要么太快。
三个新手坑
元素不存在脚本崩溃:默认的 click() 找不到会抛异常,先 d(...).exists() 判一下,或用 click_exists()(找不到就默默跳过)。
Unicode 输入在某些设备被弹回:用 set_text("...") 而不是长按粘贴,原生支持 Unicode。
等待时间不够:找元素默认 timeout 10 秒,App 启动可能需要 15 秒以上,加 wait_for_exists(30*1000)。
你现在手里有什么
一个能跑的 Python 脚本,能让手机按你写的逻辑点起来 一套选择器 + 链式定位的肌肉记忆 知道 exists/click_exists/wait_for_exists的工程用法
下一集你想看看脚本到底跟服务器说了什么吗?还是更想直接看 App 内部的请求?抓包。
系列导航见第 1 集文末。
合规边界:本系列抓包、Hook、逆向内容只用于自家设备与授权场景。
完整脚本代码见配套仓库;公众号回复「脚本」可获取仓库地址。