2w元教学课程,免费分享,教你搞定selenium自动化框架封装!

2w元教学课程,免费分享,教你搞定selenium自动化框架封装!

首先,大家在做selenium自动化的时候,通常都是调用官网提供的元素定位方法,直接调用,可能初期做自动化的时候,不会有什么问题,但是随着我们项目功能越来越多,业务也越来越复杂,有些定位功能,需要单独定制,如果是直接调用的话,我们需要修改所有调用元素定位方法的代码,这样我们维护代码需要耗费很多时间,下面我们通过将元素定位单独封装,来解决这个问题,话不多说,直接贴代码。

(注意:这里执行的地方,需要依赖driver驱动,这里我用的是Chrome浏览器,所以安装的是Chromedriver,大家可以直接去下载搭建环境)

log.py

# -*- coding: utf-8 -*-
# @Time    : 2019/2/15 9:23 AM
# @Author  : 余少琪
# @FileName: log.py
# @email   : 1603453211@qq.com

"""
这里封装了打印日志,之后做自动化的过程中,可以调用直接LogHandler方法,
将元素定位的信息打印出来,方便我们查看及定位问题。
"""


import logging
from logging import handlers
import colorlog
from common.setting import DevConfig


class LogHandler(object):
    # 日志级别关系映射
    level_relations = {
   
        'debug': logging.DEBUG,
        'info': logging.INFO,
        'warning': logging.WARNING,
        'error': logging.ERROR,
        'crit': logging.CRITICAL
    }

    def __init__(self, filename, level='info', when='D', backCount=3, fmt='%(asctime)s - %(filename)s[line:%('
                                                                          'lineno)d] - %(levelname)s: %(message)s'):
        self.logger = logging.getLogger(filename)

        self.log_colors_config = {
   
            'DEBUG': 'cyan',
            'INFO': 'white',
            'WARNING': 'yellow',
            'ERROR': 'red',
            'CRITICAL': 'red',
        }
        formatter = colorlog.ColoredFormatter(
            '%(log_color)s[%(asctime)s] [%(name)s] [%(levelname)s]: %(message)s',
            log_colors=self.log_colors_config)

        # 设置日志格式
        format_str = logging.Formatter(fmt)
        # 设置日志级别
        self.logger.setLevel(self.level_relations.get(level))
        # 往屏幕上输出
        sh = logging.StreamHandler()
        # 设置屏幕上显示的格式
        sh.setFormatter(formatter)
        # 往文件里写入#指定间隔时间自动生成文件的处理器
        th = handlers.TimedRotatingFileHandler(filename=filename, when=when, backupCount=backCount, encoding='utf-8')
        """
        #实例化TimedRotatingFileHandler
        #interval是时间间隔,backupCount是备份文件的个数,如果超过这个个数,就会自动删除,when是间隔的时间单位,单位有以下几种:
        # S 秒
        # M 分
        # H 小时、
        # D 天、
        # W 每星期(interval==0时代表星期一)
        # midnight 每天凌晨    
        """
        # 设置文件里写入的格式
        th.setFormatter(format_str)
        # 把对象加到logger里
        self.logger.addHandler(sh)
        self.logger.addHandler(th)


log_path = DevConfig().log_path
Log = LogHandler(log_path + '/all.log', level='debug')
# get_info_log = LogHandler(log_path + '/info.log', level='info')
# get_error_log = LogHandler(log_path + '/error.log', level='error')


if __name__ == '__main__':
    msg = '111'
    # log = LogHandler('all.log', level='debug')
    Log.logger.debug('debug')
    # log.logger.info(msg)
    # log.logger.warning('警告')
    # log.logger.error('报错')
    # log.logger.critical('严重')
    # LogHandler('error.log', level='error').logger.error('error')
    # is_open = ReadIni(node='log').get_value("run")
    # get_info_log.logger.info("111")

依赖库

> pip install PyUserInput 	 	
> pip intsall pyperclip
# -*- coding: utf-8 -*-
# @Time    : 2019/2/15 8:54 AM
# @Author  : 余少琪
# @FileName: basepage.py
# @email   : 1603453211@qq.com

from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# DevConfig 是我自己存放项目文件地址的,你们可以结合自己的业务单独创建
from common.setting import DevConfig
import datetime
from Outputs.logs.log import Log
from selenium.webdriver.support.select import Select
from time import time, sleep
from pykeyboard import PyKeyboard
import pyperclip
from pymouse import PyMouse
import os
import platform


class BasePage:

    def __init__(self, driver: WebDriver):
        self.driver = driver

    # 等待元素可见
    def wait_ele_visible(self, loc, img_name, timeout=20, poll_fre=0.5):
        """

        :param loc: 元素
        :param timeout:
        :param poll_fre:
        :return: 等待元素

        """
        start_time = time()

        try:
            WebDriverWait(self.driver, timeout, poll_frequency=poll_fre).until(EC.visibility_of_element_located(loc))

            Log.logger.info("等待{0}元素可见, 用时{1:.2f}秒.".format(loc, time()-start_time))

        except:
            self.save_page_shots(img_name)
            Log.logger.error("等待{0}元素可见失败, 用时{1:.2f}秒.".format(loc, time()-start_time))
            raise

    def send_pictures(self, loc, img_name, file):
        """
        上传图片
        :param loc:
        :param img_name:
        :param file: 图片路径
        :return:
        """
        start_time = time()
        platform_system = platform.system()
        if platform_system  == 'Windows':
            try:

                self.get_element(loc, img_name).send_keys(file)

                Log.logger.info("开始上传文件,文件路径{0}, 用时{1:.2f}秒.".format(file, time() - start_time))

            except:
                self.save_page_shots(img_name)
                Log.logger.error("上传图片失败!图片路径{0}".format(file))
                raise

        elif platform_system == 'Linux' or platform_system == "Darwin":

            def _is_China(file):

                # 判断文件名称中是否包含中文
                for ch in file:
                    if u'\u4e00' <= ch <= u'\u9fff':
                        return True

                return False

            try:
                if _is_China(file) is True:
                    Log.logger.error("文件路径中不允许包含中文字符!请修改文件命名。文件路径:{0}".format(file))

                if _is_China(file) is False:
                    Log.logger.info("开始上传图片, 图片路径:{0}".format(file))

                    self.click_element(loc, img_name)
                    k = PyKeyboard()
                    # k.press_keys(['Command', 'Shift', 'E'])

                    m = PyMouse()
                    filepath = '/'
                    # 模拟键盘点击 Command + Shift + G
                    k.press_keys(['Command', 'Shift', 'G'])

                    # 获取当前屏幕尺寸
                    x_dim, y_dim = m.screen_size()
                    m.click(x_dim // 2, y_dim // 2, 1)

                    # 复制文件路径开头的斜杠/
                    pyperclip.copy(filepath)

                    # 粘贴斜杠/
                    k.press_keys(['Command', 'V'])
                    # 输入文件全路径进去
                    k.type_string(file)
                    sleep(2)
                    k.press_key('Return')
                    sleep(2)
                    k.press_key('Return')
                    sleep(2)

            except:
                self.save_page_shots(img_name)
                Log.logger.error("上传图片失败!图片路径{0}".format(file))
                raise

        else:
            Log.logger.error("{0}操作系统不支持上传文件功能!".format(get_current_system()))

    # 精准 link text 定位
    def accurate_link_text(self, text, img_name):
        start_time = time()
        Log.logger.info("开始通过 link text 进行定位, 定位内容:{0},用时{1:.2f}秒.".format(text, time()-start_time))
        try:
            self.driver.find_element_by_link_text(str(text)).click()
        except:
            self.save_page_shots(img_name)
            Log.logger.error("精准 link text 定位失败!,用时{0:.2f}秒.".format(time()-start_time))

    # 等待元素可点击
    def wait_ele_clickable(self, loc, img_name, timeout=20, poll_fre=0.5):
        """

        :param loc: 元素
        :param timeout:
        :param poll_fre:
        :return: 等待元素

        """
        start_time = time()
        Log.logger.info("{0} 等待 {1} 元素可点击, 用时{2:.2f}秒.".format(img_name, loc, time()-start_time))

        try:
            WebDriverWait(self.driver, timeout, poll_frequency=poll_fre).until(EC.element_to_be_clickable(loc))

        except:
            self.save_page_shots(img_name)
            Log.logger.error("等待元素可点击失败:{0}, 用时{1:.2f}秒.".format(loc, time()-start_time))
            raise

    # 元素存在
    def wait_page_contain_element(self):
        pass

    # 查看元素
    def get_element(self, loc, img_name):
        start_time = time()
        Log.logger.info("在 {0} 查找元素:{1}, 用时{2:.2f}秒.".format(img_name, loc, time()-start_time))
        try:
            ele = self.driver.find_element(*loc)

        except:
            self.save_page_shots(img_name)
            Log.logger.error("查找元素{0}失败!用时{1:.2f}秒.".format(loc, time()-start_time))
            raise

        else:
            return ele

    # 查找所有元素
    def get_elements(self, loc, img_name):
        start_time = time()
        Log.logger.info("在 {0} 查找元素:{1}, 用时{2:.2f}秒.".format(img_name, loc, time()-start_time))
        try:
            ele = self.driver.find_elements(*loc)

        except:
            self.save_page_shots(img_name)
            Log.logger.error("查找元素{0}失败!用时{1:.2f}秒.".format(loc, time()-start_time))
            raise

        else:
            return ele

    # 点击元素
    def click_element(self, loc, img_name, timeout=20, poll_fre=0.5):
        start_time = time()
        self.wait_ele_visible(loc, img_name, timeout, poll_fre)
        ele = self.get_element(loc, img_name)
        Log.logger.info("对 {0} 点击元素:{1},用时{2:.2f}秒. ".format(img_name, loc, time()-start_time))

        try:
            ele.click()
        except:
            self.save_page_shots(img_name)
            Log.logger.error("点击元素失败, 元素内容:{0}".format(loc))
            raise

    # 元素的输入属性
    def input_text(self, loc, value, img_name, timeout=20, poll_fre=0.5):
        start_time = time()
        self.wait_ele_visible(loc, img_name, timeout, poll_fre)
        ele = self.get_element(loc, img_name)
        Log.logger.info("对 {0} 将元素 {1} 输入文本值:{2}, 用时{3:.2f}秒.".format(img_name, loc, value, time()-start_time))
        try:
            ele.send_keys(value)
        except:
            self.save_page_shots(img_name)
            Log.logger.error("元素输入文本失败!内容: {0}, 用时{1:.2f}秒. ".format(value, time()-start_time))
            raise

    # 获取元素属性
    def get_ele_attribute(self, loc, attr_name, img_name,  timeout=20, poll_fre=0.5):
        start_time = time()
        self.wait_ele_visible(loc, img_name, timeout, poll_fre)
        ele = self.get_element(loc, img_name)
        Log.logger.info("在 {0} 获取元素 {1} 的 {2} 属性, 用时{3:.2f}秒".format(img_name, loc, attr_name, time()-start_time))
        try:
            value = ele.get_attribute(attr_name)

        except:
            self.save_page_shots(img_name)
            Log.logger.error("获取元素属性{0}失败!用时{1:.2f}秒".format(loc, time()-start_time))

        else:
            Log.logger.info("属性值为:{}".format(value))
            return value

    # 获取属性的文本内容
    def get_ele_text(self, loc, img_name,  timeout=20, poll_fre=0.5):
        start_time = time()
        self.wait_ele_visible(loc, img_name, timeout, poll_fre)
        ele = self.get_element(loc, img_name)
        Log.logger.info("在 {0} 获取元素 {1} 的文本值, 用时{2:.2f}秒 ".format(img_name, loc, time()-start_time))
        try:
            text = ele.text
        except:
            self.save_page_shots(img_name)
            Log.logger.error("获取元素文本值失败!文本值内容:{0}, 用时{1:.2f}秒".format(loc, time()-start_time))

        else:
            Log.logger.info("属性值为:{0}, 用时{1:.2f}秒".format(text, time()-start_time))
            return text

    # 截图
    def save_page_shots(self, img_name):

        # 将图片存储在 OutPuts 的 screenshot 的文件中,唯一不同的是图片的命名

        file_name = "{}_{}.png".format(img_name, datetime.datetime.now())

        # 判断文件如果不存在,则创建存放一个文件夹
        # DevConfig.screenshots_path是我自己创建的文件夹路径,你们可以自己创建一个
        if not os.path.exists(DevConfig.screenshots_path):
            os.mkdir(DevConfig.screenshots_path)

        self.driver.save_screenshot(DevConfig.screenshots_path + "/" + file_name)
        Log.logger.info("页面图片保存在:{}".format(DevConfig.screenshots_path + "/" + file_name))

    # 切换至iframe页面
    def switch_to_ifaram(self, loc, img_name):
        Log.logger.info("切换ifram页面:{}".format(loc))
        try:
            WebDriverWait(self.driver, 20).until(EC.frame_to_be_available_and_switch_to_it(loc))

        except:
            self.save_page_shots(img_name)

            Log.logger.error("切换至iframe页面失败!")

    # 纵向滚动条操作
    def vertical_scroll_bar(self, img_name, *postion, type=None):
        """

        :param img_name:
        :param postion: 滚动条的坐标,为 postion 为 0,则表示滑动到顶部,为 1000 时则表示滑动到底部
                        横向滑动时,需要同时传 x ,y 轴的坐标
        :param type: 纵向 :portrait; 横向 :transverse
        :return:
        """
        Log.logger.info("开始滑动滚动条,滚动位置为{0}".format(postion))
        try:
            global js
            # 判断滚动类型为纵向
            if type == 'portrait' or type is None:
                js = 'var action=document.documentElement.scrollTop=' + ",".join(map(str, postion))

            # 判断滚动类型为横向
            elif type == 'transverse':
                js = "window.scrollTo" + str(postion) + ";"

            self.driver.execute_script(js)

        except:
            self.save_page_shots(img_name)
            Log.logger.error("滚条条滑动失败!")

    # 富文本框输入
    def rishtext(self, content, loc, img_name, loc_type="Id"):
        """
        :param content: 富文本输入内容
        :param loc: 元素名称
        :param img_name:
        :param loc_type: Id , ClassName, Name,TagName
        :return:
        """
        try:

            Log.logger.info("开始输入富文本内容:{0}".format(content))

            js = 'document.getElementBy' + str(loc_type) + "(" + str(
                loc) + ")" + 'contentWindow.document.body.innerHTML="%s"' % content
            self.driver.execute_script(js)

        except:
            self.save_page_shots(img_name)
            Log.logger.error("富文本框输入失败!")

    # 下拉框定位
    def drop_down_menu_positioning(self, loc, img_name, type, value):
        """

        :param loc:  loc = driver.find_element_by_id("kw")
        :param img_name:
        :param type: 下拉框定位类型,index 为坐标元素定位,value 为值定位,text则为文字定位
        :param value:
        :return:
        """

        Log.logger.info("开始进行下拉框定位,定位类型:{0}, 定位内容:{1}".format(type, value))
        try:

            select = Select(loc)
            if type == "index":
                select.select_by_index(value)

            elif type == 'value':
                select.select_by_value(value)
            elif type == 'text':
                select.select_by_visible_text(value)

        except:
            self.save_page_shots(img_name)
            Log.logger.error("下拉框定位失败!")


if __name__ == '__main__':
    pass

元素定位我们已经封装好了,那么我们来看看具体的运行情况,下面我们以打开百度地址为例,然后在百度输入框中,输入“selenium”进行查询

    from  selenium import webdriver
    from selenium.webdriver.common.by import By
    driver = webdriver.Chrome()
    
    # 打开百度地址
    driver.get("http://www.baidu.com")
    
    # 实例化BasePage,然后传入driver对象
    basepage = BasePage(driver)`在这里插入代码片`
    
    # 因为是输入内容,所以我们这里调用封装好的input_text()方法进行调用
    basepage.input_text((By.NAME, "wd"), 'selenium', '再输入框中输入内容')
    basepage.click_element((By.ID, 'su'), '点击搜索')
    

可以看到我们已经搜索成功了,下面我们再来看看日志的情况
在这里插入图片描述
查看日志信息
在这里插入图片描述
以上这些就是元素定位封装的方法,basepage中我只封装了一些常用的定位方法,selenium中元素定位的方式有很多,如果还有其他需要的,可以自己单独在进行封装。关于元素封装我也是基于自己对于自动化的学习和理解整理出来的,如果大家有什么更好的方式,欢迎随时讨论~

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/100660.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Java内存管理-你真的理解Java中的数据类型吗(十)

    勿在流沙筑高台,出来混迟早要还的。做一个积极的人编码、改bug、提升自己我有一个乐园,面向编程,春暖花开!作为Java程序员,Java 的数据类型这个是一定要知道的! 但是不管是那种数据类型最终存储都要到内存中,本文由浅入深对数据类型进行介绍和讲解,相信读完本文你一定会有收获的,会对Java数据类型有更深的了解和认识!本文地图一、什么是位、字节、字符、字符集位(bit):计算机…

    2022年2月28日
    190
  • C++ vector用法(详解!!函数,实现)

    C++ vector用法(详解!!函数,实现)1,简述一下vector的基本操作,它的size,capacity(),clear,reverse,reserve,  push_back等!!!2,说说,vector的存储特性,是顺序存储还是如同链表般,如果是顺序存储的话,那么是如何执行  erase,insert等函数,???(假如后面的空间不够的话,我们需要合理的算法来重新找出一块  相应的空间吗???拷贝,回收吗???是不

    2022年6月15日
    47
  • 压缩文件密码破解神器rarcrack

    压缩文件密码破解神器rarcrackhttp://hi.baidu.com/sdusoul/item/b11d13ee1181b4225b2d6401采用的是暴力破解,可以指定特定密码,否则暴力破解,很长时间吧要,三位密码100需要2分钟左右,六位密码要一年吧,用mapreduce大概可以快些。

    2022年6月6日
    30
  • require和import区别

    require和import区别区别 1 模块加载的时间 require 运行时加载 import 编译时加载 效率更高 区别 2 模块的本质 require 模块就是对象 输入时必须查找对象属性 import ES6 模块不是对象 而是通过 export 命令显式指定输出的代码 再通过 import 命令输入 这也导致了没法引用 ES6 模块本身 因为它不是对象 CommonJS 模块 let exists read

    2025年6月10日
    0
  • java和前端哪个好学_java和前端哪个难学?「建议收藏」

    java和前端哪个好学_java和前端哪个难学?「建议收藏」不是同一样东西,无法比较。Java和JavaScript哪个难学?前端和后端哪个难学?问Java和前端哪个难学,就像在问牛排和中餐哪个好吃一样。Java是门编程语言,前端是一个领域不能相比较。再回到正题。Java和JavaScript哪个难学?答案是各有各的难。仅仅对于一门编程语言而已,难也难不倒哪里去。你能说语法很难吗?你能说面向对象很难吗?理解了也没有那么难吧。我大学…

    2022年7月8日
    26
  • java 四舍五入保留小数点后两位

    java 四舍五入保留小数点后两位方式一:doublef=3.1516;BigDecimalb=newBigDecimal(f);doublef1=b.setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue();输出结果f1为3.15;源码解读:  publicBigDecimalsetScale(intnewScale,introundingMode)//intnewScale为小数点后保留的位数,introundingMode为变量进

    2022年5月21日
    1.2K

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号