Appium框架详解

不用碰手机屏幕就能自动看练手的短视频、自动完成APP新版本的回归测试——Appium的统一跨平台UI自动化,是主流天花板级工具之一。一套Python API,同时覆盖Android和iOS的真机/模拟器,不用分别啃复杂的UiAutomator/XCUITest原生工具链。

本文从「环境搭建→核心工具类封装→抖音实战」快速带你入门,所有代码适配Python高亮,复制即可修改调试。


一、5分钟搭好跨平台基础环境

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

  • 服务器端:Node.js编写的核心中转层,通过WebDriver协议与设备通信
  • 客户端库:对应Python/Java/JS等语言,开发者只需写业务逻辑调用API就行

1.1 全平台通用依赖

不管你做Android还是iOS,先装这几个通用核心工具:

工具作用安装建议
Node.js + npm LTS全局安装Appium服务器、Inspector官网下载(自动配置环境变量)
Appium Python ClientPython版API封装pip安装最新稳定版

1.2 一键安装与平台补充

打开终端/CMD,先装通用服务和驱动:

# 全局安装核心Appium服务器(加--session-override方便调试)
npm install -g appium
# 全局安装Appium Inspector(可视化定位元素的神器,必须要)
npm install -g appium-inspector
# 本文以Android实战,先装UiAutomator2驱动
appium driver install uiautomator2
# 装Python客户端(推荐独立虚拟环境)
pip install Appium-Python-Client
针对不同平台的补刀
  • Android用户:装Android Studio(自动管理SDK、ADB),开启开发者模式的「USB调试」「USB安装」「允许模拟位置(可选)」
  • iOS用户(仅macOS):装Xcode(自动管理模拟器/真机签名),执行xcode-select --install安装命令行工具 :::

1.3 验证环境是否跑通

启动Appium服务器(默认监听http://127.0.0.1:4723):

appium --address 127.0.0.1 --port 4723 --session-override --log-timestamp

看到输出里有 「Appium REST http interface listener started」 就说明服务器没问题!


二、轻量通用工具类封装

避免每次写脚本都重复配置连接、显式等待等代码,先封装一个开箱即用的AppiumHelper类。

2.1 完整Python代码

from appium import webdriver
from appium.options.android import UiAutomator2Options
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time

class AppiumHelper:
    """Android UiAutomator2轻量自动化助手(可扩展iOS)"""
    
    def __init__(self, 
                 device_name: str, 
                 app_package: str = None, 
                 app_activity: str = None, 
                 remote_url: str = "http://127.0.0.1:4723"):
        """
        初始化助手
        :param device_name: 设备名/模拟器名(adb devices/Xcode模拟器列表获取)
        :param app_package: 目标APP包名
        :param app_activity: 目标APP启动页Activity
        :param remote_url: Appium服务器地址
        """
        self.remote_url = remote_url
        self.driver = None
        
        # 配置默认Android选项(可覆盖)
        self.options = UiAutomator2Options()
        self.options.platform_name = "Android"
        self.options.device_name = device_name
        self.options.auto_grant_permissions = True  # 自动授权所有权限
        self.options.unicode_keyboard = True         # 支持中文输入
        self.options.reset_keyboard = True            # 脚本结束恢复默认键盘
        self.options.no_reset = True                  # 保留APP登录状态
        
        # 可选配置:指定APP包和启动页
        if app_package:
            self.options.app_package = app_package
        if app_activity:
            self.options.app_activity = app_activity
    
    def connect(self) -> bool:
        """连接Appium并启动APP"""
        try:
            self.driver = webdriver.Remote(self.remote_url, options=self.options)
            print(f"✅ Appium连接成功:{self.options.device_name}")
            return True
        except Exception as e:
            print(f"❌ Appium连接失败:{str(e)}")
            return False
    
    def disconnect(self) -> None:
        """安全断开连接"""
        if self.driver:
            self.driver.quit()
            print("🔌 连接已断开")
    
    # -------------------- 显式等待与元素操作 --------------------
    def wait_element(self, locator: tuple, timeout: int = 10):
        """
        显式等待元素出现
        :param locator: 定位器元组,如(AppiumBy.ID, "xxx")
        :param timeout: 最长等待时间
        :return: WebElement对象或None
        """
        try:
            return WebDriverWait(self.driver, timeout).until(
                EC.presence_of_element_located(locator)
            )
        except Exception:
            return None
    
    def tap_element(self, locator: tuple, timeout: int = 10) -> bool:
        """点击元素(带等待)"""
        el = self.wait_element(locator, timeout)
        if el:
            el.click()
            return True
        print(f"⚠️  未找到可点击元素:{locator}")
        return False
    
    def input_text(self, locator: tuple, text: str, timeout: int = 10) -> bool:
        """输入文本(自动清空原内容)"""
        el = self.wait_element(locator, timeout)
        if el:
            el.clear()
            el.send_keys(text)
            return True
        print(f"⚠️  未找到可输入元素:{locator}")
        return False
    
    # -------------------- 基础手势操作 --------------------
    def swipe_up(self, duration: int = 800) -> None:
        """向上滑动屏幕(刷视频/滚动列表通用)"""
        size = self.driver.get_window_size()
        w, h = size["width"], size["height"]
        start = (w // 2, h * 3 // 4)
        end = (w // 2, h // 4)
        self.driver.swipe(*start, *end, duration)

三、抖音实战:模拟真实用户浏览

用刚才的AppiumHelper,写一个能随机点赞、随机关注、模拟真实停留时间的抖音脚本。

3.1 先获取APP关键信息

打开抖音APP,执行ADB命令获取包名和启动页:

# Windows CMD
adb shell dumpsys window | findstr mCurrentFocus
# macOS/Linux终端
adb shell dumpsys window | grep mCurrentFocus

通常输出类似:

mCurrentFocus=Window{abc123 u0 com.ss.android.ugc.aweme/com.ss.android.ugc.aweme.splash.SplashActivity}
  • 包名:com.ss.android.ugc.aweme
  • 启动页:com.ss.android.ugc.aweme.splash.SplashActivity

3.2 完整实战代码

from appium.webdriver.common.appiumby import AppiumBy
import time
import random
# 假设刚才的封装放在同目录的appium_helper.py里
from appium_helper import AppiumHelper

class DouyinAuto:
    """抖音模拟浏览自动化类"""
    
    def __init__(self, device_name: str):
        # 配置抖音的连接信息
        self.helper = AppiumHelper(
            device_name=device_name,
            app_package="com.ss.android.ugc.aweme",
            app_activity=".splash.SplashActivity"
        )
        self.driver = None
    
    def start(self) -> None:
        """启动连接并等待APP加载完成"""
        if self.helper.connect():
            self.driver = self.helper.driver
            print("⏳ 等待抖音冷启动...")
            time.sleep(10)  # 抖音冷启动时间较长
    
    def like_current(self) -> bool:
        """尝试多个定位器点赞(防止版本更新失效)"""
        like_locs = [
            (AppiumBy.ID, "com.ss.android.ugc.aweme:id/like_icon"),
            (AppiumBy.XPATH, '//android.widget.ImageView[@content-desc="点赞"]')
        ]
        for loc in like_locs:
            if self.helper.tap_element(loc, timeout=3):
                print("❤️  点赞成功")
                return True
        print("⚠️  未找到点赞按钮")
        return False
    
    def follow_author(self) -> None:
        """随机关注当前作者(模拟真实行为)"""
        # 先点击头像进入主页
        avatar_loc = (AppiumBy.ID, "com.ss.android.ugc.aweme:id/author_avatar")
        if self.helper.tap_element(avatar_loc, timeout=3):
            time.sleep(2)
            # 点击关注按钮(支持文本匹配)
            follow_loc = (AppiumBy.XPATH, '//android.widget.Button[contains(@text, "关注")]')
            if self.helper.tap_element(follow_loc, timeout=2):
                print("👤 关注成功")
            # 返回视频页面
            self.driver.back()
            time.sleep(1)
    
    def run(self, cycles: int = 5, like_rate: float = 0.7, follow_rate: float = 0.05):
        """
        运行自动化任务
        :param cycles: 浏览视频的次数
        :param like_rate: 点赞概率(0-1)
        :param follow_rate: 关注概率(0-1)
        """
        if not self.driver:
            return
        
        print(f"🤖 开始任务:共浏览{cycles}个视频")
        for i in range(cycles):
            print(f"\n🔄 第{i+1}个视频:")
            
            # 随机点赞
            if random.random() < like_rate:
                self.like_current()
            
            # 随机关注
            if random.random() < follow_rate:
                self.follow_author()
            
            # 模拟真实停留时间(2-6秒)
            stay = random.uniform(2, 6)
            print(f"⏳ 停留{stay:.1f}秒")
            time.sleep(stay)
            
            # 滑到下一个视频
            if i < cycles - 1:
                print("📱 滑动到下一个")
                self.helper.swipe_up(duration=700)
                time.sleep(0.8)  # 等待视频预加载
        
        print("\n🏁 任务全部完成!")
        self.helper.disconnect()

if __name__ == "__main__":
    # 替换成你自己的设备名(adb devices获取)
    AUTO = DouyinAuto(device_name="emulator-5554")
    AUTO.start()
    AUTO.run(cycles=3, like_rate=0.9, follow_rate=0.1)

四、总结与拓展

Appium的核心优势在于跨平台统一API生态完善,入门后可以做这些拓展:

  1. 更精准的定位:用Appium Inspector直接抓取UI元素,替换示例里的备用定位器
  2. 更复杂的手势:用ActionChain实现长按、拖拽、双指缩放
  3. 并行多设备:用Docker容器或者Appium Grid同时控制多台设备
  4. 与测试框架结合:和Pytest、Allure一起做专业的UI自动化测试报告

:::warning 合规提示 使用自动化脚本时,请严格遵守目标平台的用户协议和法律法规,禁止用于恶意刷量、批量爬取隐私数据等违规行为。