天天看点

selenium之关于截图元素定位及只截取元素讲解

一、前言

关于截取元素,这边有一个坑在mac,mac默认是二倍截图保存,会导致你元素定位没错,初始截图没错,但是在截取元素的时候老是截取不到你要的元素图,虽然网上有类似将坐标乘以2,同样放大两倍的方法去截取,但是如果在你不知道分别率被放大多少倍的时候,你又该如何去解决这个问题呢?下面有这方便的讲解

二、实战

1、mac的二倍截图问题解决代码

# coding:utf-8
from selenium import webdriver
from PIL import Image

driver = webdriver.Chrome()
driver.get('http://www.baidu.com/')

driver.save_screenshot('button.png')
element = driver.find_element_by_id("su")
print(element.location)                # 打印元素坐标
print(element.size)                    # 打印元素大小

left = element.location['x'] * 2
top = element.location['y'] * 2
right = (element.location['x'] + element.size['width']) * 2
bottom = (element.location['y'] + element.size['height']) * 2


im = im.crop((left, top, right, bottom))
im.save('button.png')      

这个是我们查了很多资料,知道二倍截图问题时候解决的问题,但是在我们不知道分别率是否被放大的时候,我们又该怎么办?

2、计算倍放大的分辨率倍数,再截取元素

# coding:utf-8
from selenium import webdriver
from PIL import Image
driver = webdriver.Chrome()
width = driver.get_window_size().get("width")
print(width)
driver.get('http://www.baidu.com/')

driver.save_screenshot('button.png')
element = driver.find_element_by_id("su")
print(element.location)                # 打印元素坐标
print(element.size)                    # 打印元素大小

left = element.location['x']
top = element.location['y']
right = (element.location['x'] + element.size['width'])
bottom = (element.location['y'] + element.size['height'])

im = Image.open('button.png')
i = im.size
p = i[0]
n = p / width
im = im.crop((left*n, top*n, right*n, bottom*n))
im.save('button.png')      

这样就能截取到你想要的截图了:

selenium之关于截图元素定位及只截取元素讲解

3、如何截取高亮元素

def highlight(element, element_name=None, debug=True):
        '''
        debug 参数用来开关截图功能
        '''
    # 高亮元素
    def apply_style():
        driver.execute_script("arguments[0].style.border='6px solid red'", element)

    # 截图
    def screen_shot(screen_name):
        driver.save_screenshot(screen_name)

    if debug:
        try:
            screen_shot(str(element_name) + '_before.jpg')
            apply_style()
            screen_shot(str(element_name) + '_after.jpg')
        except Exception as e:
            return e

    apply_style()      

 编写测试代码执行后的结果:

selenium之关于截图元素定位及只截取元素讲解

4、这边一个小插曲,就是我们在查找元素和判断元素是否存在的时候会比较麻烦,我们现在在这边做一个小封装:

from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait



#  方法一,用 try...except...
def is_element_exsist(driver, locator):
    '''
    判断元素是否存在,存在返回 True,不存返回 False
    :param locator: locator 为元组类型,如("id", "yy") :return: bool 值,True or False
    '''
    try:
        driver.find_element(*locator)
        return True
    except Exception as msg:
        print("元素%s 找不到:%s" % (locator, msg))
        return False


# 方法二:用 elements 定义一组元素方法
def is_element_exsist1(driver, locator):
    '''
    判断元素是否存在,存在返回 True,不存返回 False
    :param locator: locator 为元组类型,如("id", "yy") :return: bool 值,True or False
    '''
    eles = driver.find_elements(*locator)
    if len(eles) < 1:
        return False
    else:
        return True


# 方法三,用WebDriverWait 和 expected_conditions
def is_element_exsist2(driver, locator):
    '''
    结合 WebDriverWait 和 expected_conditions 判断元素是否存在, 每间隔 1 秒判断一次,30s 超时,
    存在返回 True,不存返回 False :param locator: locator 为元组类型,
    如("id", "yy") :return: bool 值,True or False
    '''
    try:
        WebDriverWait(driver, 30, 1).until(EC.presence_of_element_located(locator))
        return True
    except:
        return False


# 查看元素的方法
def find(driver, locator, timeout=30):
    '''定位元素,参数 locator 是元祖类型, 如("id", "yy")'''
    element = WebDriverWait(driver, timeout, 1).until(EC.presence_of_element_located(locator))      

 5、现在我们要做的是,竟然截屏标记元素和截取元素都完成了,我们是否可以将这两种截取元素的定位方法合并,并做开发在后台配置,下面就是我要处理的问题,以及看看我是怎么处理的:

5.1 先在config.yaml配置文件中做一个开关控制:

screenshot:
  is_highlight_ele_screenshot: True
  is_screenshot_ele: True      

5.2 然后就可以在until中新建一个elementOperate.py,然后开始我们的封装和实现:

# -*- coding: utf-8 -*-

"""
@author: lucas
@Function:
@file: elementOperate.py
@time: 2021/10/17 3:42 下午
"""
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

import time
import os
from selenium import webdriver
from utils.config import REPORT_PATH, Config
from PIL import Image


#  方法一,用 try...except...
def is_element_exsist(driver, locator):
    '''
    判断元素是否存在,存在返回 True,不存返回 False
    :param locator: locator 为元组类型,如("id", "yy") :return: bool 值,True or False
    '''
    try:
        driver.find_element(*locator)
        return True
    except Exception as msg:
        print("元素%s 找不到:%s" % (locator, msg))
        return False


# 方法二:用 elements 定义一组元素方法
def is_element_exsist1(driver, locator):
    '''
    判断元素是否存在,存在返回 True,不存返回 False
    :param locator: locator 为元组类型,如("id", "yy") :return: bool 值,True or False
    '''
    eles = driver.find_elements(*locator)
    if len(eles) < 1:
        return False
    else:
        return True


# 方法三,用WebDriverWait 和 expected_conditions
def is_element_exsist2(driver, locator):
    '''
    结合 WebDriverWait 和 expected_conditions 判断元素是否存在, 每间隔 1 秒判断一次,30s 超时,
    存在返回 True,不存返回 False :param locator: locator 为元组类型,
    如("id", "yy") :return: bool 值,True or False
    '''
    try:
        WebDriverWait(driver, 30, 1).until(EC.presence_of_element_located(locator))
        return True
    except:
        return False


# 查看元素的方法
def find(driver, locator, timeout=30):
    '''定位元素,参数 locator 是元祖类型, 如("id", "yy")'''
    element = WebDriverWait(driver, timeout, 1).until(EC.presence_of_element_located(locator))
    return element


# 高亮元素
def highlight(driver, element, element_name=None, is_highlight_ele_screenshot=True, is_screenshot_ele=True):
    '''
    :param driver: 浏览器驱动
    :param element: 定位到的元素
    :param element_name: 元素名
    :param is_highlight_ele_screenshot: 是否要带有高亮元素的截屏,默认True是要,Flase是不要
    :param is_screenshot_ele: 是否要截取元素的图,默认True是要,Flase是不要
    :return: 预期的截屏图
    '''
    day = time.strftime('%Y%m%d', time.localtime(time.time()))
    screenshot_path = REPORT_PATH + '/screenshot_%s' % day
    tm = time.strftime('%H%M%S', time.localtime(time.time()))

    # 高亮元素
    def apply_style():
        driver.execute_script("arguments[0].style.border='6px solid red'", element)

    # 截图
    def screen_shot(screen_name):
        driver.maximize_window()
        if not os.path.exists(screenshot_path):
            os.makedirs(screenshot_path)
        filename = screenshot_path + '/%s_%s.png' % (screen_name, tm)
        driver.save_screenshot(filename)
        return filename

    # 截取元素
    def screenshot_element(file_name):
        # 获取初始截屏的分辨率宽度
        width = driver.get_window_size().get("width")
        # 获取元素坐标
        left = element.location['x']
        top = element.location['y']
        right = (element.location['x'] + element.size['width'])
        bottom = (element.location['y'] + element.size['height'])
        im = Image.open(file_name)
        # 获取图片宽度
        p = im.size[0]
        # 计算被放大的分辨率倍数
        n = p / width
        # 截取元素坐标
        im = im.crop((left * n, top * n, right * n, bottom * n))
        # 保存截取元素的截图
        im.save(screenshot_path + "/" + str(element_name) + '元素.png')

    if is_highlight_ele_screenshot:
        try:
            # 打开浏览器前的全屏截图
            screen_shot(str(element_name) + '_before')
            apply_style()
            # 高亮元素后的全屏截图
            file_name = screen_shot(str(element_name) + '_after')
            if is_screenshot_ele:
                # 截取的高亮元素图
                screenshot_element(file_name)
        except Exception as e:
            return e

    apply_style()


if __name__ == '__main__':
    # 读取配置文件控制截屏需求
    screenshot = Config().get('screenshot')
    is_open_1 = screenshot.get('is_highlight_ele_screenshot')
    is_open_2 = screenshot.get('is_screenshot_ele')
    # 启动驱动,打开浏览器地址
    driver = webdriver.Chrome()
    driver.get('http://www.baidu.com/')
    locator = ('id', 'su')
    # 判断元素是否存在
    exsist = is_element_exsist2(driver, locator)
    print("是否存在元素:", exsist)
    # 查找元素
    element = find(driver, locator, timeout=10)
    # 获取满足你需求的截图
    highlight(driver, element, 'su', is_open_1, is_open_2)
    driver.close()      

我这边为了方便定位问题和查看元素的时间,我在截图的时候会带上文件夹和时间标识做区分,上面关于截图高亮元素全屏和截取高亮元素我的开关是全部打开了,查看下效果:

selenium之关于截图元素定位及只截取元素讲解

再看下截图的结果:

selenium之关于截图元素定位及只截取元素讲解
selenium之关于截图元素定位及只截取元素讲解
# -*- coding: utf-8 -*-

"""
@author: lucas
@Function:
@file: browser.py
@time: 2021/9/10 4:53 下午
"""
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
import time
import os
from selenium import webdriver
from utils.config import REPORT_PATH, Config, DRIVER_PATH
from PIL import Image

# 可根据需要自行扩展
# 火狐浏览器驱动路径
FIREFOX_PATH = DRIVER_PATH + '/geckodriver'
# 谷歌浏览器驱动路径
CHROMEDRIVER_PATH = DRIVER_PATH + '/chromedriver'

TYPES = {'firefox': webdriver.Firefox, 'chrome': webdriver.Chrome}
EXECUTABLE_PATH = {'firefox': FIREFOX_PATH, 'chrome': CHROMEDRIVER_PATH}
day = time.strftime('%Y%m%d', time.localtime(time.time()))
screenshot_path = REPORT_PATH + '/screenshot_%s' % day
tm = time.strftime('%H%M%S', time.localtime(time.time()))


class UnSupportBrowserTypeError(Exception):
    pass


class Browser(object):
    def __init__(self, browser_type='firefox'):
        self._type = browser_type.lower()
        if self._type in TYPES:
            self.browser = TYPES[self._type]
        else:
            raise UnSupportBrowserTypeError('仅支持%s!' % ', '.join(TYPES.keys()))
        self.driver = None

    def get(self, url, maximize_window=True, implicitly_wait=30):
        self.driver = self.browser(executable_path=EXECUTABLE_PATH[self._type])
        self.driver.get(url)
        if maximize_window:
            self.driver.maximize_window()
        self.driver.implicitly_wait(implicitly_wait)
        return self

    # 查看元素
    def find(self, locator, timeout=30):
        '''定位元素,参数 locator 是元祖类型, 如("id", "yy")'''
        element = WebDriverWait(self.driver, timeout, 1).until(EC.presence_of_element_located(locator))
        return element

    # 是否存在元素
    def is_element_exsist2(self, locator):
        try:
            self.find(locator, timeout=30)
            return True
        except:
            return False

    # 截屏
    def screen_shot(self, screen_name):
        self.driver.maximize_window()

        if not os.path.exists(screenshot_path):
            os.makedirs(screenshot_path)
        filename = screenshot_path + '/%s_%s.png' % (screen_name, tm)
        self.driver.save_screenshot(filename)
        return filename

    # 截取元素
    def screenshot_element(self, element, element_name, file_name):
        # 获取初始截屏的分辨率宽度
        width = self.driver.get_window_size().get("width")
        # 获取元素坐标
        left = element.location['x']
        top = element.location['y']
        right = (element.location['x'] + element.size['width'])
        bottom = (element.location['y'] + element.size['height'])
        im = Image.open(file_name)
        # 获取图片宽度
        p = im.size[0]
        # 计算被放大的分辨率倍数
        n = p / width
        # 截取元素坐标
        im = im.crop((left * n, top * n, right * n, bottom * n))
        # 保存截取元素的截图
        print(str(element))
        im.save(screenshot_path + "/" + str(element_name) + '元素.png')

    # 高亮元素
    def highlight(self, element, element_name=None, is_highlight_ele_screenshot=True, is_screenshot_ele=True):
        '''
        :param element: 定位到的元素
        :param element_name: 元素名
        :param is_highlight_ele_screenshot: 是否要带有高亮元素的截屏,默认True是要,Flase是不要
        :param is_screenshot_ele: 是否要截取元素的图,默认True是要,Flase是不要
        :return: 预期的截屏图
        '''

        # 高亮元素
        def apply_style():
            self.driver.execute_script("arguments[0].style.border='6px solid red'", element)

        # 截图
        if is_highlight_ele_screenshot:
            try:
                # 打开浏览器前的全屏截图
                self.screen_shot(element_name + '_before')
                apply_style()
                # 高亮元素后的全屏截图
                file_name = self.screen_shot(str(element_name) + '_after')
                if is_screenshot_ele:
                    # 截取的高亮元素图
                    self.screenshot_element(str(element_name), file_name)
            except Exception as e:
                return e

        apply_style()

    def close(self):
        self.driver.close()

    def quit(self):
        self.driver.quit()


if __name__ == '__main__':
    # 读取配置文件控制截屏需求
    screenshot = Config().get('screenshot')
    is_open_1 = screenshot.get('is_highlight_ele_screenshot')
    is_open_2 = screenshot.get('is_screenshot_ele')
    b = Browser('chrome').get('http://www.baidu.com')
    locator = ('id', 'su')
    exsist = b.is_element_exsist2(locator)
    print("是否存在元素:", exsist)
    # 查找元素
    element = b.find(locator, timeout=10)
    print(element)
    b.highlight(element, 'su', is_open_1, is_open_2)
    b.quit()      

继续阅读