当前位置: 首页 > news >正文

使用Python操作你的手机(Appium入门)

目录

  • 使用Python操作你的手机(Appium入门)
    • 1. 引言
    • 2. 环境准备
      • 2.1 安装Node.js和NPM
      • 2.2 安装Appium
      • 2.3 安装Appium Python客户端
      • 2.4 安装和配置Android SDK
      • 2.5 安装Java Development Kit (JDK)
    • 3. Appium基础概念
      • 3.1 Appium架构
      • 3.2 Desired Capabilities
      • 3.3 元素定位策略
    • 4. 编写第一个Appium脚本
      • 4.1 导入必要的库
      • 4.2 配置Desired Capabilities
      • 4.3 实现计算器操作
      • 4.4 完整的测试类
    • 5. 进阶操作
      • 5.1 处理不同的元素定位情况
      • 5.2 手势操作
      • 5.3 处理弹窗和权限请求
      • 5.4 等待策略
    • 6. 完整代码示例
    • 7. 常见问题与解决方案
      • 7.1 连接问题
      • 7.2 元素找不到问题
      • 7.3 权限问题
      • 7.4 性能问题
    • 8. 最佳实践
      • 8.1 代码组织
      • 8.2 错误处理
      • 8.3 维护性
    • 9. 总结

『宝藏代码胶囊开张啦!』—— 我的 CodeCapsule 来咯!✨
写代码不再头疼!我的新站点 CodeCapsule 主打一个 “白菜价”+“量身定制”!无论是卡脖子的毕设/课设/文献复现,需要灵光一现的算法改进,还是想给项目加个“外挂”,这里都有便宜又好用的代码方案等你发现!低成本,高适配,助你轻松通关!速来围观 👉 CodeCapsule官网

使用Python操作你的手机(Appium入门)

1. 引言

在当今移动互联网时代,手机应用已经成为人们日常生活中不可或缺的一部分。随着移动应用的快速发展,自动化测试和手机操作的需求也日益增长。Appium作为一个开源的移动应用自动化测试框架,能够帮助我们实现这一目标。

Appium支持多种编程语言,包括Python、Java、Ruby等,并可以同时测试Android和iOS平台的应用。它采用WebDriver协议,使得我们可以使用熟悉的Selenium WebDriver API来编写移动应用的自动化脚本。

本文将详细介绍如何使用Python和Appium来操作手机,从环境搭建到实际脚本编写,帮助读者快速掌握这一实用技能。

2. 环境准备

在开始使用Appium之前,我们需要完成一系列的环境配置工作。以下是详细的步骤:

2.1 安装Node.js和NPM

Appium服务器是基于Node.js开发的,因此首先需要安装Node.js。可以从Node.js官网下载并安装最新版本。

安装完成后,可以通过以下命令验证安装是否成功:

node --version
npm --version

2.2 安装Appium

通过NPM全局安装Appium:

npm install -g appium

安装完成后,可以通过以下命令启动Appium服务器:

appium

2.3 安装Appium Python客户端

使用pip安装Appium的Python客户端库:

pip install Appium-Python-Client

2.4 安装和配置Android SDK

对于Android设备,需要安装Android SDK并配置环境变量:

  1. 下载Android Studio或独立SDK工具
  2. 设置ANDROID_HOME环境变量
  3. 将platform-tools和tools目录添加到PATH环境变量中

2.5 安装Java Development Kit (JDK)

Appium需要Java环境支持,请安装JDK 8或更高版本。

3. Appium基础概念

3.1 Appium架构

Appium采用客户端-服务器架构:

  • Appium服务器:接收来自客户端的命令,并将其转换为移动设备可以理解的原生命令
  • Appium客户端:各种编程语言的客户端库,用于发送命令到Appium服务器
客户端脚本
Appium服务器
Android设备
iOS设备
UI Automator
XCUITest

3.2 Desired Capabilities

Desired Capabilities是一组键值对,用于告诉Appium服务器我们想要启动怎样的会话。常见的Capabilities包括:

  • platformName:平台名称(Android或iOS)
  • platformVersion:平台版本
  • deviceName:设备名称
  • appPackage:应用包名
  • appActivity:应用活动名

3.3 元素定位策略

Appium支持多种元素定位策略:

  • ID定位
  • Class Name定位
  • XPath定位
  • Accessibility ID定位
  • Android UI Automator定位

4. 编写第一个Appium脚本

下面我们将编写一个简单的Appium脚本,用于打开手机上的计算器应用并进行简单的计算操作。

4.1 导入必要的库

from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from appium.options.android import UiAutomator2Options
import time
import unittest

4.2 配置Desired Capabilities

def setup_driver():# 配置Appium选项options = UiAutomator2Options()# 设置设备基本信息options.platform_name = 'Android'options.platform_version = '12'  # 根据你的设备版本修改options.device_name = 'Android Emulator'options.automation_name = 'UiAutomator2'# 设置应用信息options.app_package = 'com.android.calculator2'options.app_activity = 'com.android.calculator2.Calculator'# 其他设置options.no_reset = True  # 不重置应用状态# 连接Appium服务器driver = webdriver.Remote('http://localhost:4723', options=options)return driver

4.3 实现计算器操作

def calculator_operations(driver):"""执行计算器操作"""try:# 等待计算器加载完成time.sleep(2)# 定位数字按钮和操作符# 注意:不同设备上的计算器应用可能具有不同的元素ID# 这里使用的是Android原生计算器的元素ID# 点击数字 7btn_7 = driver.find_element(AppiumBy.ID, 'com.android.calculator2:id/digit_7')btn_7.click()# 点击加号btn_plus = driver.find_element(AppiumBy.ID, 'com.android.calculator2:id/op_add')btn_plus.click()# 点击数字 8btn_8 = driver.find_element(AppiumBy.ID, 'com.android.calculator2:id/digit_8')btn_8.click()# 点击等号btn_equals = driver.find_element(AppiumBy.ID, 'com.android.calculator2:id/eq')btn_equals.click()# 获取结果result = driver.find_element(AppiumBy.ID, 'com.android.calculator2:id/result')calculated_result = result.textprint(f"计算结果: 7 + 8 = {calculated_result}")# 验证结果是否正确expected_result = '15'if calculated_result == expected_result:print("测试通过!计算结果正确。")else:print(f"测试失败!期望结果: {expected_result}, 实际结果: {calculated_result}")return calculated_resultexcept Exception as e:print(f"操作过程中出现错误: {str(e)}")return None

4.4 完整的测试类

class CalculatorTest(unittest.TestCase):"""计算器测试类"""def setUp(self):"""测试前置设置"""self.driver = setup_driver()self.driver.implicitly_wait(10)  # 设置隐式等待时间def tearDown(self):"""测试后置清理"""if self.driver:self.driver.quit()def test_addition_operation(self):"""测试加法运算"""result = calculator_operations(self.driver)self.assertEqual(result, '15', "加法运算结果不正确")def test_clear_operation(self):"""测试清除操作"""try:# 先输入一些数字btn_5 = self.driver.find_element(AppiumBy.ID, 'com.android.calculator2:id/digit_5')btn_5.click()# 点击清除按钮btn_clear = self.driver.find_element(AppiumBy.ID, 'com.android.calculator2:id/clr')btn_clear.click()# 验证显示区域是否已清除result = self.driver.find_element(AppiumBy.ID, 'com.android.calculator2:id/formula')current_display = result.text# 清除后显示区域应该为空或显示0self.assertTrue(not current_display or current_display == '0', "清除操作未正常工作")except Exception as e:self.fail(f"清除操作测试失败: {str(e)}")

5. 进阶操作

5.1 处理不同的元素定位情况

在实际应用中,我们可能会遇到各种复杂的定位情况。以下是一些常用的定位方法:

def advanced_element_locating(driver):"""演示高级元素定位方法"""# 1. 使用XPath定位# 通过文本内容定位元素element_by_text = driver.find_element(AppiumBy.XPATH, "//*[@text='确定']")# 通过部分文本内容定位element_by_partial_text = driver.find_element(AppiumBy.XPATH, "//*[contains(@text, '确定')]")# 2. 使用Accessibility ID定位(通常对应content-desc属性)element_by_accessibility = driver.find_element(AppiumBy.ACCESSIBILITY_ID, "按钮描述")# 3. 使用Class Name定位elements_by_class = driver.find_elements(AppiumBy.CLASS_NAME, "android.widget.Button")# 4. 使用Android UI Automator定位element_by_uiautomator = driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("确定")')return {'by_text': element_by_text,'by_partial_text': element_by_partial_text,'by_accessibility': element_by_accessibility,'by_class': elements_by_class,'by_uiautomator': element_by_uiautomator}

5.2 手势操作

Appium支持多种手势操作,如滑动、长按、拖拽等:

def gesture_operations(driver):"""演示手势操作"""# 获取屏幕尺寸window_size = driver.get_window_size()screen_width = window_size['width']screen_height = window_size['height']# 1. 滑动操作 - 从底部滑动到顶部start_x = screen_width / 2start_y = screen_height * 0.8end_x = screen_width / 2end_y = screen_height * 0.2driver.swipe(start_x, start_y, end_x, end_y, 1000)# 2. 滚动操作# 滚动到指定元素scroll_to_element = driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,'new UiScrollable(new UiSelector().scrollable(true))''.scrollIntoView(new UiSelector().text("目标元素"))')# 3. 长按操作element_to_long_press = driver.find_element(AppiumBy.ID, 'some.element.id')driver.long_press(element_to_long_press)# 4. 拖拽操作source_element = driver.find_element(AppiumBy.ID, 'source.element')target_element = driver.find_element(AppiumBy.ID, 'target.element')driver.drag_and_drop(source_element, target_element)

5.3 处理弹窗和权限请求

def handle_popups_and_permissions(driver):"""处理弹窗和权限请求"""try:# 尝试查找并点击允许按钮allow_button = driver.find_element(AppiumBy.ID, 'com.android.packageinstaller:id/permission_allow_button')allow_button.click()print("已处理权限请求")except Exception:# 如果找不到特定的允许按钮,尝试其他方式try:# 使用文本定位允许按钮allow_by_text = driver.find_element(AppiumBy.XPATH, "//*[@text='允许' or @text='ALLOW']")allow_by_text.click()print("通过文本定位处理了权限请求")except Exception:print("未找到权限请求弹窗或处理失败")# 处理其他类型的弹窗try:# 查找确定、好的、知道了等按钮confirm_buttons = ["确定", "确认", "好的", "知道了", "OK", "Okay"]for button_text in confirm_buttons:try:confirm_btn = driver.find_element(AppiumBy.XPATH, f"//*[@text='{button_text}']")confirm_btn.click()print(f"点击了 {button_text} 按钮")breakexcept Exception:continueexcept Exception as e:print(f"处理弹窗时出现错误: {str(e)}")

5.4 等待策略

合理的等待策略对于自动化测试的稳定性至关重要:

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ECdef wait_strategies(driver):"""演示不同的等待策略"""# 1. 显式等待 - 等待元素可点击wait = WebDriverWait(driver, 10)element = wait.until(EC.element_to_be_clickable((AppiumBy.ID, 'some.element.id')))element.click()# 2. 显式等待 - 等待元素可见visible_element = wait.until(EC.visibility_of_element_located((AppiumBy.ID, 'visible.element')))# 3. 显式等待 - 等待元素存在(不一定可见)present_element = wait.until(EC.presence_of_element_located((AppiumBy.ID, 'present.element')))# 4. 自定义等待条件def custom_condition(driver):"""自定义等待条件"""try:element = driver.find_element(AppiumBy.ID, 'custom.element')return element.is_displayed()except Exception:return Falsecustom_element = wait.until(custom_condition)return {'clickable': element,'visible': visible_element,'present': present_element,'custom': custom_element}

6. 完整代码示例

下面是一个完整的Appium脚本示例,展示了如何使用Python操作手机:

#!/usr/bin/env python3
"""
Appium手机操作示例
作者:你的名字
日期:2024年1月
描述:使用Python和Appium操作手机计算器应用
"""import time
import unittest
import logging
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from appium.options.android import UiAutomator2Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, NoSuchElementException# 配置日志
logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)class MobileAutomationFramework:"""移动自动化框架类"""def __init__(self, server_url='http://localhost:4723'):self.server_url = server_urlself.driver = Noneself.wait = Nonedef setup_driver(self, capabilities_dict=None):"""设置Appium驱动"""if capabilities_dict is None:# 默认配置 - Android计算器capabilities_dict = {'platformName': 'Android','platformVersion': '12','deviceName': 'Android Emulator','automationName': 'UiAutomator2','appPackage': 'com.android.calculator2','appActivity': 'com.android.calculator2.Calculator','noReset': True,'newCommandTimeout': 300}try:options = UiAutomator2Options()for key, value in capabilities_dict.items():setattr(options, key, value)logger.info("正在连接Appium服务器...")self.driver = webdriver.Remote(self.server_url, options=options)# 设置显式等待self.wait = WebDriverWait(self.driver, 15)logger.info("Appium驱动设置成功")return Trueexcept Exception as e:logger.error(f"设置Appium驱动失败: {str(e)}")return Falsedef teardown(self):"""清理资源"""if self.driver:self.driver.quit()logger.info("Appium驱动已关闭")def find_element_with_wait(self, by, value, timeout=15):"""带等待的元素查找"""wait = WebDriverWait(self.driver, timeout)return wait.until(EC.presence_of_element_located((by, value)))def safe_click(self, by, value, timeout=15):"""安全的点击操作"""try:element = self.find_element_with_wait(by, value, timeout)element.click()logger.info(f"成功点击元素: {value}")return Trueexcept Exception as e:logger.error(f"点击元素失败: {value}, 错误: {str(e)}")return Falsedef take_screenshot(self, filename=None):"""截取屏幕截图"""if filename is None:filename = f"screenshot_{int(time.time())}.png"try:self.driver.save_screenshot(filename)logger.info(f"截图已保存: {filename}")return Trueexcept Exception as e:logger.error(f"截图失败: {str(e)}")return Falsedef get_page_source(self):"""获取页面源代码"""try:return self.driver.page_sourceexcept Exception as e:logger.error(f"获取页面源代码失败: {str(e)}")return Noneclass CalculatorAutomation(MobileAutomationFramework):"""计算器自动化类"""def __init__(self):super().__init__()self.number_mapping = {'0': 'digit_0', '1': 'digit_1', '2': 'digit_2','3': 'digit_3', '4': 'digit_4', '5': 'digit_5','6': 'digit_6', '7': 'digit_7', '8': 'digit_8','9': 'digit_9'}self.operator_mapping = {'+': 'op_add', '-': 'op_sub', '*': 'op_mul', '/': 'op_div'}def input_number(self, number):"""输入数字"""if not isinstance(number, (int, str)):raise ValueError("数字必须是整数或字符串")number_str = str(number)for digit in number_str:if digit in self.number_mapping:element_id = f"com.android.calculator2:id/{self.number_mapping[digit]}"self.safe_click(AppiumBy.ID, element_id)time.sleep(0.1)  # 短暂延迟,确保输入稳定else:logger.warning(f"无法识别的数字: {digit}")def input_operator(self, operator):"""输入操作符"""if operator not in self.operator_mapping:raise ValueError(f"不支持的操作符: {operator}")element_id = f"com.android.calculator2:id/{self.operator_mapping[operator]}"self.safe_click(AppiumBy.ID, element_id)def calculate(self, num1, operator, num2):"""执行计算"""logger.info(f"执行计算: {num1} {operator} {num2}")# 输入第一个数字self.input_number(num1)# 输入操作符self.input_operator(operator)# 输入第二个数字self.input_number(num2)# 点击等号self.safe_click(AppiumBy.ID, 'com.android.calculator2:id/eq')# 获取结果return self.get_result()def get_result(self):"""获取计算结果"""try:result_element = self.find_element_with_wait(AppiumBy.ID, 'com.android.calculator2:id/result')result = result_element.textlogger.info(f"计算结果: {result}")return resultexcept Exception as e:logger.error(f"获取结果失败: {str(e)}")return Nonedef clear_calculator(self):"""清除计算器"""self.safe_click(AppiumBy.ID, 'com.android.calculator2:id/clr')logger.info("计算器已清除")class TestCalculatorOperations(unittest.TestCase):"""计算器操作测试类"""@classmethoddef setUpClass(cls):"""测试类设置"""cls.calculator = CalculatorAutomation()success = cls.calculator.setup_driver()if not success:raise Exception("无法初始化Appium驱动")@classmethoddef tearDownClass(cls):"""测试类清理"""cls.calculator.teardown()def setUp(self):"""单个测试设置"""# 确保每次测试前计算器是清除状态self.calculator.clear_calculator()time.sleep(1)def test_addition(self):"""测试加法"""result = self.calculator.calculate(15, '+', 7)self.assertEqual(result, '22', "加法测试失败")def test_subtraction(self):"""测试减法"""result = self.calculator.calculate(20, '-', 8)self.assertEqual(result, '12', "减法测试失败")def test_multiplication(self):"""测试乘法"""result = self.calculator.calculate(6, '*', 9)self.assertEqual(result, '54', "乘法测试失败")def test_division(self):"""测试除法"""result = self.calculator.calculate(56, '/', 7)self.assertEqual(result, '8', "除法测试失败")def test_complex_operation(self):"""测试复杂运算"""# 15 + 27 - 8self.calculator.input_number(15)self.calculator.input_operator('+')self.calculator.input_number(27)self.calculator.input_operator('-')self.calculator.input_number(8)self.calculator.safe_click(AppiumBy.ID, 'com.android.calculator2:id/eq')result = self.calculator.get_result()self.assertEqual(result, '34', "复杂运算测试失败")def main():"""主函数"""logger.info("开始Appium手机操作演示")# 创建计算器自动化实例calculator = CalculatorAutomation()try:# 设置驱动if not calculator.setup_driver():logger.error("无法启动Appium驱动,程序退出")return# 执行一系列测试计算test_calculations = [(8, '+', 4),(15, '-', 6),(7, '*', 9),(81, '/', 9)]for num1, op, num2 in test_calculations:result = calculator.calculate(num1, op, num2)expected = str(eval(f"{num1}{op}{num2}"))if result == expected:logger.info(f"✓ {num1} {op} {num2} = {result} (正确)")else:logger.error(f"✗ {num1} {op} {num2} = {result} (期望: {expected})")# 截取屏幕截图calculator.take_screenshot("calculator_final_state.png")logger.info("Appium手机操作演示完成")except Exception as e:logger.error(f"程序执行过程中出现错误: {str(e)}")finally:# 确保资源被正确清理calculator.teardown()if __name__ == "__main__":# 可以直接运行演示main()# 或者运行单元测试# unittest.main(verbosity=2)

7. 常见问题与解决方案

7.1 连接问题

问题:无法连接到Appium服务器

解决方案

  1. 确保Appium服务器正在运行:appium
  2. 检查端口是否被占用,默认端口是4723
  3. 验证URL格式:http://localhost:4723

7.2 元素找不到问题

问题:脚本无法找到指定元素

解决方案

  1. 增加等待时间,使用显式等待
  2. 使用不同的定位策略
  3. 检查应用是否已正确启动
  4. 使用Appium Desktop的Inspector工具验证元素定位

7.3 权限问题

问题:应用权限请求导致脚本中断

解决方案

  1. 在Desired Capabilities中设置autoGrantPermissions: true
  2. 在脚本中添加权限处理逻辑
  3. 手动预先授予应用所需权限

7.4 性能问题

问题:脚本运行缓慢

解决方案

  1. 减少不必要的等待时间
  2. 使用更高效的元素定位策略
  3. 避免频繁的页面源代码获取
  4. 考虑使用更快的测试设备或模拟器

8. 最佳实践

8.1 代码组织

  • 使用Page Object模式将页面元素和操作封装成类
  • 将配置信息与测试逻辑分离
  • 使用配置文件或环境变量管理设备信息和应用信息

8.2 错误处理

  • 实现完善的异常处理机制
  • 添加重试机制处理偶发性失败
  • 使用日志记录详细的操作信息

8.3 维护性

  • 使用有意义的变量名和函数名
  • 添加清晰的注释和文档
  • 定期更新Appium和相关依赖

9. 总结

通过本文的介绍,我们学习了如何使用Python和Appium来操作手机应用。从环境搭建到基础操作,再到高级技巧,我们覆盖了使用Appium进行移动自动化的关键知识点。

Appium作为一个强大的跨平台移动自动化工具,结合Python的简洁语法,为我们提供了强大的手机操作能力。无论是进行自动化测试还是实现复杂的手机操作流程,Appium都是一个值得掌握的技能。

随着移动应用的不断发展,掌握移动自动化技术将会变得越来越重要。希望本文能够为你提供一个良好的起点,帮助你在移动自动化的道路上走得更远。


注意:在实际使用中,请根据你的具体设备和应用调整代码中的元素定位信息和配置参数。不同的设备和应用版本可能会有差异,需要灵活调整脚本。

http://www.dtcms.com/a/519458.html

相关文章:

  • Spire.Doc 实践指南:将Word 文档转换为 XML
  • 【2B篇】阿里通义 Qwen3-VL 新增 2B、32B 两个模型尺寸,手机也能轻松运行
  • 目标检测YOLO实战应用案例100讲-基于多模态和多模型融合 的三维目标检测
  • 【成长纪实】从“Hello World”到分布式实战的进阶之路
  • 图论理论基础(1)
  • 开源 Linux 服务器与中间件(十)Mqtt协议和Emqx服务器安装测试
  • 网站建设实践鉴定手机网站建设讯息
  • 网站管理文档怎么写晚上睡不着看点害羞的东西app
  • uni-app 广告弹窗最佳实践:不扰民、可控制频次、含完整源码
  • 使用eNSP模拟器搭建网络拓扑结构(笔记2):从 0 到 1 掌握华为网络仿真
  • UniApp 多页面编译优化:编译时间从10分钟到1分钟
  • C++变量与函数命名规范技术指南 (基于华为编码规范与现代C++最佳实践)
  • ELK1——elasticsearch
  • 【图像卷积基础】卷积过程卷积实现通道扩充与压缩池化Pooling原理和可视化
  • 杭州公司网站设计外贸手工做兼职的网站
  • 深入浅出Langchain4j——构建Java大语言模型应用的新范式
  • Babylon.js学习之路《添加自定义摇杆控制相机》
  • 【JAVA 进阶】SpringBoot集成Sa-Token权限校验框架深度解析
  • 【CMakeLists.txt】Qt6 依赖配置详解
  • 用js做网站登录网页成品
  • 数据库安全网关:从“看得见访问”到“控得住风险”的关键一层
  • 对泊松过程的理解
  • 【数论】质数筛(埃氏筛、欧拉筛)
  • 扩展名网站兰州做网站一咨询兰州做网站公司
  • 华为OD-Java面经-21届考研
  • Excel拆分和合并优化版本
  • 智能网联汽车:当汽车遇上“智慧网络”
  • 常规点光源在工业视觉检测上的应用
  • C++新特性——正则表达式
  • 基于卷积神经网络的汽车类型识别系统,resnet50,vgg16,resnet34【pytorch框架,python代码】