一、前言
关于截取元素,这边有一个坑在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')
这样就能截取到你想要的截图了:
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()
?编写测试代码执行后的结果:
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()
我这边为了方便定位问题和查看元素的时间,我在截图的时候会带上文件夹和时间标识做区分,上面关于截图高亮元素全屏和截取高亮元素我的开关是全部打开了,查看下效果:
再看下截图的结果:
?
5.3 基于上述封装有个不好的地方就是每次执行一个函数都要启动一次驱动,极耗性能,所以我对方法做了改良:
# -*- 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()
最后效果同上
|