夜雨聆风学习资料网

ARTICLE · 1057137

用 Python 写第一段安卓自动化脚本:FIRERPA 选择器定位实战

用 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、逆向内容只用于自家设备与授权场景。

完整脚本代码见配套仓库;公众号回复「脚本」可获取仓库地址。

相关学习资料