Appium python+appium 自动化测试 - 获取短信 + 图片验证码

Tin · 2021年01月30日 · 最后由 loneyao 回复于 2021年02月04日 · 5816 次阅读

本篇文章主要讲述的是如何自动获取短信验证码和如何自动获取图片验证码,并写入到对应的输入框中(以下均使用微博的找回密码作为示例)

获取短信验证码的方法有三种,如下所示:

  • 在手机的通知栏中获取短信内容
  • 通过监控手机日志获取短信验证码
  • 通过 redis 获取短信验证码

以下只写出第一种在通知栏中获取短信内容的方法,后续两种方法会在后面进行分享。

获取图片验证码的方法:

通过百度的 OCR 文字识别,进行识别获取图片中的字母、文字或数字验证码。

一、获取短信验证码

方法:发送短信验证码后,打开手机的通知栏,定位短信内容,将定位到的内容填入到验证码输入框中,核心代码为:

# 打开手机通知栏
self.driver.open_notifications()
# 获取定位短信内容(封装了元素定位,详细操作可见上一篇PO模式封装的博客)
message = self.find_element(self._message_content)
# 将短信内容转换为text文本
message_content = message.text
# 通过正则匹配短信内容中的验证码(使用r前缀可以自动转义,不需要手动转换字符串,6表示6位数字的验证码)
ver_code = re.findall(r'[\d]{6}', message_content)
# 关闭通知栏(下面方式为点击手机返回键来关闭通知栏)
self.driver.press_keycode(4)
# 自动填入验证码(将获取到的验证自动填入到验证码输入框中)
self.input_verification_code(ver_code)

注意:由于我的类中继承了 BasePage 类,而 BasePage 类中声明 driver 是属于 WebDriver 库 (WebDriver 库属于 Selenium 框架),而以上代码中调用的 open_notifications() 和 press_keycode() 方法是属于 webdriver 库 (webdriver 库属于 appium 框架),所以需要在该类中导入 webdriver 类,并在类中将 driver 声明为 webdriver,否则会出现如下图所示错误:

导入和声明的方式如下:

# 导入webdriver
from appium import webdriver
from page.base_page import BasePage

class PhoneLoginPage(BasePage):
        # 声明
        driver: webdriver = None

二、申请百度 OCR 识别接口,下载对应语言的 SDK 文件

在调用百度 OCR 图片识别前,需要先申请百度通用文字识别接口,申请方法如下:

1.登录百度 AI 平台,申请百度通用文字识别接口,免费激活 AI 平台的使用权限

百度 AI 平台网址:https://ai.baidu.com/

进入开放能力→文字识别→通用文字识别,如下图:

点击立即使用:

创建应用:

创建成功后进入管理应用,能查看创建的应用 AppID、API Key、Secret Key,如下图:

2. 查看适用不同平台/语言/功能的 SDK

文字识别链接:https://ai.baidu.com/sdk#ocr

下载需要语言的 SDK:

我使用的是 python 语言,所以下载的是对应的 Python SDK(支持 python 版本:2.7.+,3.+),下载完成后,安装方式有两种:

①已经安装 pip,打开命令提示符,输入以下命令即可

pip install baidu-aip

②已经安装 setuptools,打开命令提示符,输入以下命令即可

python setup.py install

出现下图表示安装成功:

3.文字识别接口说明(参考文档)

文字识别接口说明链接:https://ai.baidu.com/ai-doc/OCR/7kibizyfm

或者可以通过上面图片(下载所需语言的 SDK)→使用说明→接口说明进入(后续会使用到这篇文档,建议在写脚本前先阅读)

三、图片验证码识别

方法:在工程中自动创建一个存放图片的文件夹,再定位图片验证码的控件,截取图片验证码,将截取到的图片使用自动生成的规则的文件名,保存在创建的图片文件夹内,存储后通过百度的 OCR 文字识别,获取到保存的截图,进行文字识别,再将识别的结果输出到验证码输入框内

注意:

问:为什么要将图片有规则的命名?

答:方便后续查找核对

问:为什么截图保存不能保存在本地磁盘内,而是保存在本地工程目录下方?

答:如果保存图片时使用的是本地固定的磁盘,那别人使用你的代码时就需要修改保存的路径,如果保存在本地工程目录下方,别人就不需要手动创建文件夹,直接运行代码就能自动在工程目录下生成文件夹

1.screenshot.py — 验证码截图,并通过特定的规律保存在特定的文件夹中

import time
from common.image_recognition import ImageRecognition
from page.base_page import BasePage

class Screen(BasePage):
    # 图片验证码输入框
    _img_check_code = "/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.RelativeLayout/android.widget.FrameLayout/android.webkit.WebView/android.webkit.WebView/android.widget.Image"

    # 截取页面中特定区域的图片
    def get_part_screen(self):
        # 截图将图片保存至固定的位置
        img_folder = 'E:\\study\\Fork\\Weibo_Demo\\Weibo\\image\\'
        # 截图的文件名组成
        times = time.strftime('%Y%m%d%H%M', time.localtime(time.time()))
        # 图片存储位置+文件名
        screen_save_path = img_folder + times + '.png'
        # 截取特定位置的图片,并保存在已定义的存储位置和规定的文件名
        self.find_xpath(self._img_check_code).screenshot(screen_save_path)
        # 实例化ImageRecognition,传入需要识别的图片位置
        ir = ImageRecognition(screen_save_path)
        a = ir.ocr()
        return a

2.image_recognition.py — 调用百度 OCR 文字识别

from aip import AipOcr

class ImageRecognition:
    # 初始化path,定义为字符类型
    def __init__(self, path: ''):
        self.path = path

    # 你的 APPID AK SK
    APP_ID = '123456'
    API_KEY = 'fydhafbuebfuebiufwbiufe'
    SECRET_KEY = 'difwebfubweufbweffefefefefefef'
    client = AipOcr(APP_ID, API_KEY, SECRET_KEY)

    # 配置参数,识别到图片内容时,需要输出的内容
    def get_garameter(self):
        options = {
            # 识别语言类型,默认为CHN_ENG
            "language_type": "ENG",
            # 是否检测图像朝向,默认不检测,即:false
            "detect_language": "true"
            # 是否返回识别结果中每一行的置信度
            # "probability": "true"
        }
        return options

    # 打开文件
    def get_file_content(self, file_path):
        with open(file_path, 'rb') as fp:
            return fp.read()

    # 需要识别的图片的路径
    def get_image(self):
        return self.get_file_content(self.path)

    # 调用百度ocr接口,进行精准识别
    def ocr(self):
        image_content = self.client.basicAccurate(self.get_image()), self.get_garameter()
        return image_content

注意:在调用百度 OCR 识别需要在运行环境(工程)下安装 baidu-aip,安装步骤如下:

打开 Setting→Python Interpreter,点击 + 号

搜索输入:baidu-aip,点击搜索结果列表中第一个,点击 install package

在 Setting→Python Interpreter 页面出现下图,表示安装成功

3.retrieve_password_page.py — 对获取到的图片验证码解析

from time import sleep
from selenium.webdriver.common.by import By
from common.screenshot import Screen
from page.base_page import BasePage

class RetrievePasswordPage(BasePage):
    # 初始化,用于实例化Screen类
    def __init__(self, driver):
        super().__init__(driver)
        self.screen = Screen(driver)

    # 自动获取图片验证码中的内容
    def auto_get_check_code(self, telephone):
        self.input_telephone(telephone)
        # 获取图片验证码,获取到的是元组
        check_code_tuple = self.screen.get_part_screen()
        print(check_code_tuple)
        # 在元组中提取第一个数据(元组下标位置从0开始),获取到的数据为字典(字典是一组键值对)
        check_code_list = check_code_tuple[0]
        print(check_code_list)
        # 在字典中提取第三个数据'words_result',获取到的数据为列表
        word_result = check_code_list['words_result']
        # 判断获取到的列表的值是否为空,为空则在列表中自定义一个字典“word_result”的值
        if word_result:
            print(word_result)
        else:
            word_result = [{'words': 'F3HA'}]
        print(word_result)
        # 在列表中提取一个数据,获取到的数据是字典
        dic_words = word_result[0]
        print(dic_words)
        # 在字典中输出"words"
        word = dic_words['words']
        print(word)
        self.input_image_check_code(word)
        self.find_xpath(self._btn_confirm).click()
        sleep(1)

说明:由于通过百度 OCR 识别到的内容是一个字典,如下:

({'log_id': 6483429546783692489, 'words_result_num': 1, 'words_result': [{'words': ' F3H'}]}, {'language_type': 'ENG', 'detect_language': 'true'})

通过处理,将以上内容转换为 JSON 格式,转换后如下图显示:

我们需要获取的是 words,所以需要一层一层的获取元素,具体的获取方式是请查看 python 的字典、元组、列表,可参考菜鸟教程:https://www.runoob.com/python3/python3-dictionary.html

注意:经实验,该种方式对于比较奇葩的图片验证码识别概率低,识别中规中矩的图片验证码成功率较高

4.testcase.py — 测试用例

# 找回密码测试用例脚本
import pytest
from common.init import AppStart

class TestRetrievePassword:
    def setup(self):
        self.retrievepassword = AppStart.start().enter_retrieve_password()

    # 自动获取图片验证码
    def test_auto_get_check_code(self):
        telephone = "18056120351"
        self.retrievepassword.auto_get_check_code(telephone)
        assert self.retrievepassword.get_phone_and_check_code_tips() == "请输入正确的验证码"

    def teardown(self):
        AppStart.quit()

以上内容有错误的地方,大家多多指正,谢谢!

共收到 2 条回复 时间 点赞

就喜欢这种一个系列下来的文章,给楼主赞一个

学习了

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册