从0开始的appium+Android+python自动抢红包世界生活 | 您所在的位置:网站首页 › 有没有专门抢红包软件 › 从0开始的appium+Android+python自动抢红包世界生活 |
前言
关于 appium 是什么之类的不再赘述,有关的博文已经很多了,本文旨在提供截至 2022 年除夕可以在 Windows 11 上复现的操作方法 虽然这篇博文被看到的时候应该只能用在 2023 年的春节了 环境配置安装 appium desktop 安装 appium-inspector 安装 JDK 安装 MuMu 模拟器 安装好后在 系统应用 -> 设置 -> 开发者选项 中打开 USB调试 同时查看 设置 -> 关于平板电脑 -> Android版本,我的是 6.0.1 借助 Android Studio 安装 SDK 首先下载+安装+运行 Android Studio ,进入到欢迎界面,再进入 SDK 管理 进行一堆环境变量的设置 Win+R 运行 control system -> 相关链接 -> 高级系统设置 -> 环境变量 下方环境变量区中新建 变量名:ANDROID_HOME 变量值:下图所示 变量名:prog_dir 变量值:%ANDROID_HOME%\platform-tools 变量名:ANDROID_SWT 变量值:%ANDROID_HOME%\tools\lib\x86_64 已有的系统变量中找到Path -> 编辑 -> 编辑文本 -> 在文末插入 %JAVA_HOME%\bin;%ANDROID_HOME%;%ANDROID_HOME%/tools;%ANDROID_HOME%/platform-tools; 测试 在 cmd 中运行 C:\Users\username> java -version C:\Users\username> javac -version C:\Users\username> adb devices结果类似下图即说明设置成功 安装 appium 的 Python 驱动 pip3 install appium-python-client 链接Android模拟器如果在 cmd 下执行 adb devices 显示出有设备链接了那就万事大吉 但是 MuMu 应该是没办法被自动检测到的,如下位置 MuMu 告诉我们运行 adb connect 127.0.0.1:7555 分别打开 Appium Server GUI 和 Appium Inspector,原本他们是在一起的,现在分成了两个程序 GUI:直接 Start ,此时 Edit Configuration 里面应该已经自动填上了刚才设置的JAVA_HOME 和 ANDROID_HOME 如果你下载最新 Appium Desktop 时仍有如下提示,请把 Insepector 中的 Reomote Path 改成 /wd/hub ,否则会报错(见 Append:Failed to create session) 编辑 Desired Capabilities,我们直接用 JSON 格式 有关 Desired Capabilities 的官方文档 . newCommandTimeout: 一个 session 在未接收到任何命令多久后会自动关闭,默认 60s 太短了 Start Session 补充:包名获取 这里任意 appPackage 和 appActivity 的获取可以用以下方式,仍以微信为例: 在 MuMu 上打开微信 在 cmd 中运行 adb shell 进入模拟器操作系统 运行 dumpsys window windows | grep mFocusedApp,对照一下就懂了,其中第一次运行时是在登录界面,第二次是在联系人界面,所以 appActivity 不同 Inspector 演示 点击我们想要了解的地方就可以看到 id (即 resource_id) 和相关信息了。注意它的界面不是自动同步的,必须手动点一下上面的刷新才会同步 以上代码和刚刚按的 Start Session 是一样的,观察模拟器会发现微信被打开了 使用 Insepctor 来分析 UIPython 代码运行起来后,Inspector -> Attach to Session -> 选择 Session ID,对应的 Session ID 可以通过 driver 创建好后 driver.session_id 查看 print('session_id:', self.driver.session_id)Appium Inspector 其实是一个不太好的选择,一是慢,二是检测能力似乎不强,三是看不到元素层次,很多时候要点的元素被遮挡了 这里有两个现成的选择 sdk\tools\bin 下的 uiautomatorviewer.bat ,但是 Java 版本必须是 Java 8sdk\tools 下的 monitor.bat最初的环境配置中其实已经加入了与这两个软件相关的环境变量配置,以下展示 Ui Automator Viewer 的界面 这里有一个BUG,如果用 MuMu 默认的平板分辨率,打开微信的时候在 uiautomatorviewer 显示的界面也会横过来,我的解决办法是直接把 MuMu 设成类似手机的分别率 Ui Automator Viewer 增强 用 lazyuiautomatorviewer.jar 替代 Sdk\tools\lib 下的对应文件,这个 Viewer 可以显示元素的 xpath,注意换的时候名字记得改成和原来的一样 Appium 光速入门 from selenium.webdriver.common.by import By from appium.webdriver.common.appiumby import AppiumBy from selenium.webdriver.support.ui import WebDriverWait from selenium.common.exceptions import TimeoutException, NoSuchElementException '''查找元素实例''' element = driver.find_element(By.ID, 'com.tencent.mm:id/d5v') # 会返回第一个找到的,如果没找到会抛出 NoSuchElementException 错误 # By.ID 是应该优先考虑的方法 element = driver.find_elements(By.CLASS_NAME, 'android.widget.LinearLayout') # 返回所有符合元素的列表,如果没有符合的则返回空列表 driver.find_element(By.XPATH,"//android.support.v7.widget.RecyclerView[@resource-id='com.tencent.mm:id/a5u']/android.widget.LinearLayout[1]") # By.XPATH 利用路径表达式查找元素 driver.find_elements(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("'+text+'")') # 利用元素的 text 属性查找元素 '''元素方法''' element.click() # 点击 element.send_keys() # 输入 '''元素等待''' def func(driver): return driver.find_elements(By.CLASS_NAME, 'android.widget.LinearLayout') WebDriverWait(driver = mydriver, timeout = 1, poll_frequency = 0.5).until(func) # 会在 timeout 时间内尝试调用 func(driver), 间隔 poll_frequency # 当 func 返回真时提前退出 # 超时后抛出 TimeoutException 错误 完整代码2022.01.31:还没写呢,今年的除夕都已经过了,明年再写吧 2022.02.01:还是给它写了,加了个自动登录的功能,因为模拟器和手机上切换登录时总是要输密码,于是就自动化了 import time from typing import List import keyboard from appium import webdriver from appium.webdriver.common.appiumby import AppiumBy from appium.webdriver.common.touch_action import TouchAction from appium.webdriver.webdriver import WebDriver from appium.webdriver.webelement import WebElement from selenium.common.exceptions import (NoSuchElementException, StaleElementReferenceException, TimeoutException) from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait def is_element_exist(driver, element, method='by_id', timeout=0, frequency=0.2): """ 监听 element_text 是否在 timeout 时间内存在 , 结果以列表形式返回 element_text: list 或 str 格式,当是 list 格式时表示是否存在其中任意一个 可选method有: 'by_text', 'by_id' """ def exist(driver: WebDriver) -> List[WebElement]: if method == 'by_text': def find(text): return driver.find_elements( AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("'+text+'")') elif method == 'by_id': def find(id): return driver.find_elements(By.ID, id) else: raise ValueError if isinstance(element, str): return find(element) else: for ele in element: if find(ele): return find(ele) return [] if timeout: try: WebDriverWait(driver, timeout, frequency).until(exist) except TimeoutException: return [] else: return exist(driver) else: return exist(driver) class RedEnvelope(): def __init__(self): self.desired_caps = { "platformName": "Android", "deviceName": "pzyhfagmr8nfd6hm", "platformVersion": "6.0.1", "appPackage": "com.tencent.mm", "appActivity": ".ui.LauncherUI", "newCommandTimeout": 6000, "noReset": True } self.driver = webdriver.Remote( 'http://127.0.0.1:4723/wd/hub', desired_capabilities=self.desired_caps) actions = TouchAction(self.driver) self.password = '不告诉你' self.grab_flag = True def login(self): # d5z:紧急冻结 cns:通讯录 if not is_element_exist(self.driver, ['com.tencent.mm:id/d5z', 'com.tencent.mm:id/cns'], 'by_id', 10): print('微信启动失败') if is_element_exist(self.driver, 'com.tencent.mm:id/d5z'): print('检测到微信被登出,正在重新登录') # d5v:切换验证方式 if is_element_exist(self.driver, 'com.tencent.mm:id/d5v'): self.driver.find_element( By.ID, 'com.tencent.mm:id/d5v').click() # f40:用xx登录[重复] if is_element_exist(self.driver, 'com.tencent.mm:id/f40', timeout=1): # 用密码登录 self.driver.find_element( By.XPATH, "//android.support.v7.widget.RecyclerView[@resource-id='com.tencent.mm:id/a5u']/android.widget.LinearLayout[1]").click() self.login_with_password() else: print('等待用密码登录入口超时') else: print('登录微信成功 (由保持登录记录)') def login_with_password(self): print('正在用密码登录') if is_element_exist(self.driver, 'com.tencent.mm:id/bhn', timeout=1): self.driver.find_element( By.ID, 'com.tencent.mm:id/bhn').send_keys(self.password) self.driver.find_element(By.ID, 'com.tencent.mm:id/d5n').click() else: print('等待密码输入框超时') if is_element_exist(self.driver, ['com.tencent.mm:id/cns'], 'by_id', 10): print('登录微信成功 (由键入密码)') def main(self): print('正在启动微信') self.login() def add_hotkey_stop_grab(self): def stop_grab(): self.grab_flag = False keyboard.add_hotkey('ctrl+alt+r', stop_grab) print('激活了热键 ctrl+alt+r 以中止抢红包') def grab(self): # 如果要实现稳定多会话抢红包, 得再删除没有抢到的红包(不会显示消息:你已领取xx的红包) self.grab_from_message() return '''多会话''' # b4r 微信->每个会话窗口 pers = is_element_exist(self.driver, 'com.tencent.mm:id/b4r') if pers: for per in pers: message = per.find_element(By.ID, 'com.tencent.mm:id/cyv').text if '[微信红包]' in message: per.click() self.grab_from_message() break else: self.grab_from_message() def grab_from_message(self): try: messages = is_element_exist( self.driver, 'com.tencent.mm:id/al7', timeout=1) for message in messages[::-1]: if is_element_exist(message, 'com.tencent.mm:id/ra'): # print('哇,发现一个红包!') env_app = is_element_exist( message, 'com.tencent.mm:id/r0') if env_app: pass '''气氛组''' # if env_app[0].text == '已领取': # print('哦,是领过的') # elif env_app[0].text == '已被领完': # print('啊,是没抢到的') # else: # print('咦,这是什么') else: print('抢它!') message.click() # den:开 bottom = is_element_exist( self.driver, 'com.tencent.mm:id/den', timeout=1, frequency=0.01) if bottom: bottom[0].click() # d_h 抢到xx元 dh = is_element_exist( self.driver, 'com.tencent.mm:id/d_h', timeout=2, frequency=0.01) if dh: print('抢到 '+dh[0].text+' 元') else: print('没抢到') self.driver.find_element( By.ID, 'com.tencent.mm:id/dm').click() else: print('没抢到') # dem:红包下的x self.driver.find_element( By.ID, 'com.tencent.mm:id/dem').click() '''多会话''' # self.actions.long_press(message) # self.actions.perform() # self.driver.find_element( # By.XPATH, "//android.widget.LinearLayout[@resource-id='com.tencent.mm:id/hyf']/android.widget.LinearLayout[1]").click() break except StaleElementReferenceException: return '''多会话''' # rr:又上角的返回 # rr = is_element_exist( # self.driver, 'com.tencent.mm:id/rr', timeout=1)[0] # rr.click() def grab_start(self, frequcency=0): print('开始抢红包') self.add_hotkey_stop_grab() self.grab_flag = True while self.grab_flag: self.grab() time.sleep(frequcency) print('抢红包中止') if __name__ == '__main__': R = RedEnvelope() R.main() # 建议用交互式 # R.grab_start() Append:Failed to create session如果运行右下角 Start Session 后遇到一个报错 |
CopyRight 2018-2019 实验室设备网 版权所有 |