ele.save()增加name参数,返回文件路径;get_screenshot()增加name和scroll_to_center参数

This commit is contained in:
g1879 2023-09-08 16:22:17 +08:00
parent 7394ab8059
commit e9d499bcb8
6 changed files with 65 additions and 55 deletions

View File

@ -5,7 +5,7 @@
""" """
from base64 import b64decode from base64 import b64decode
from json import loads, JSONDecodeError from json import loads, JSONDecodeError
from os import sep from os.path import sep
from pathlib import Path from pathlib import Path
from threading import Thread from threading import Thread
from time import perf_counter, sleep, time from time import perf_counter, sleep, time
@ -768,10 +768,11 @@ class ChromiumBase(BasePage):
''' '''
return {i['key']: i['val'] for i in self.run_js_loaded(js)} return {i['key']: i['val'] for i in self.run_js_loaded(js)}
def get_screenshot(self, path=None, as_bytes=None, as_base64=None, def get_screenshot(self, path=None, name=None, as_bytes=None, as_base64=None,
full_page=False, left_top=None, right_bottom=None): full_page=False, left_top=None, right_bottom=None):
"""对页面进行截图可对整个网页、可见网页、指定范围截图。对可视范围外截图需要90以上版本浏览器支持 """对页面进行截图可对整个网页、可见网页、指定范围截图。对可视范围外截图需要90以上版本浏览器支持
:param path: 完整路径后缀可选 'jpg','jpeg','png','webp' :param path: 保存路径
:param name: 完整文件名后缀可选 'jpg','jpeg','png','webp'
:param as_bytes: 是否以字节形式返回图片可选 'jpg','jpeg','png','webp'生效时path参数和as_base64参数无效 :param as_bytes: 是否以字节形式返回图片可选 'jpg','jpeg','png','webp'生效时path参数和as_base64参数无效
:param as_base64: 是否以base64字符串形式返回图片可选 'jpg','jpeg','png','webp'生效时path参数无效 :param as_base64: 是否以base64字符串形式返回图片可选 'jpg','jpeg','png','webp'生效时path参数无效
:param full_page: 是否整页截图为True截取整个网页为False截取可视窗口 :param full_page: 是否整页截图为True截取整个网页为False截取可视窗口
@ -779,7 +780,7 @@ class ChromiumBase(BasePage):
:param right_bottom: 截取范围右下角角坐标 :param right_bottom: 截取范围右下角角坐标
:return: 图片完整路径或字节文本 :return: 图片完整路径或字节文本
""" """
return self._get_screenshot(path=path, as_bytes=as_bytes, as_base64=as_base64, return self._get_screenshot(path=path, name=name, as_bytes=as_bytes, as_base64=as_base64,
full_page=full_page, left_top=left_top, right_bottom=right_bottom) full_page=full_page, left_top=left_top, right_bottom=right_bottom)
def clear_cache(self, session_storage=True, local_storage=True, cache=True, cookies=True): def clear_cache(self, session_storage=True, local_storage=True, cache=True, cookies=True):
@ -843,10 +844,11 @@ class ChromiumBase(BasePage):
return True return True
def _get_screenshot(self, path=None, as_bytes=None, as_base64=None, def _get_screenshot(self, path=None, name=None, as_bytes=None, as_base64=None,
full_page=False, left_top=None, right_bottom=None, ele=None): full_page=False, left_top=None, right_bottom=None, ele=None):
"""对页面进行截图可对整个网页、可见网页、指定范围截图。对可视范围外截图需要90以上版本浏览器支持 """对页面进行截图可对整个网页、可见网页、指定范围截图。对可视范围外截图需要90以上版本浏览器支持
:param path: 完整路径后缀可选 'jpg','jpeg','png','webp' :param path: 保存路径
:param name: 完整文件名后缀可选 'jpg','jpeg','png','webp'
:param as_bytes: 是否以字节形式返回图片可选 'jpg','jpeg','png','webp'生效时path参数和as_base64参数无效 :param as_bytes: 是否以字节形式返回图片可选 'jpg','jpeg','png','webp'生效时path参数和as_base64参数无效
:param as_base64: 是否以base64字符串形式返回图片可选 'jpg','jpeg','png','webp'生效时path参数无效 :param as_base64: 是否以base64字符串形式返回图片可选 'jpg','jpeg','png','webp'生效时path参数无效
:param full_page: 是否整页截图为True截取整个网页为False截取可视窗口 :param full_page: 是否整页截图为True截取整个网页为False截取可视窗口
@ -860,7 +862,7 @@ class ChromiumBase(BasePage):
pic_type = 'png' pic_type = 'png'
else: else:
if as_bytes not in ('jpg', 'jpeg', 'png', 'webp'): if as_bytes not in ('jpg', 'jpeg', 'png', 'webp'):
raise ValueError("只能接收 'jpg', 'jpeg', 'png', 'webp' 四种格式。") raise TypeError("只能接收 'jpg', 'jpeg', 'png', 'webp' 四种格式。")
pic_type = 'jpeg' if as_bytes == 'jpg' else as_bytes pic_type = 'jpeg' if as_bytes == 'jpg' else as_bytes
elif as_base64: elif as_base64:
@ -868,16 +870,18 @@ class ChromiumBase(BasePage):
pic_type = 'png' pic_type = 'png'
else: else:
if as_base64 not in ('jpg', 'jpeg', 'png', 'webp'): if as_base64 not in ('jpg', 'jpeg', 'png', 'webp'):
raise ValueError("只能接收 'jpg', 'jpeg', 'png', 'webp' 四种格式。") raise TypeError("只能接收 'jpg', 'jpeg', 'png', 'webp' 四种格式。")
pic_type = 'jpeg' if as_base64 == 'jpg' else as_base64 pic_type = 'jpeg' if as_base64 == 'jpg' else as_base64
else: else:
if not name:
name = f'{self.title}.jpg'
if not path: if not path:
path = f'{self.title}.jpg' path = '.'
path = get_usable_path(path) if not name.endswith(('.jpg', '.jpeg', '.png', '.webp')):
name = f'{name}.jpg'
path = get_usable_path(f'{path}{sep}{name}')
pic_type = path.suffix.lower() pic_type = path.suffix.lower()
if pic_type not in ('.jpg', '.jpeg', '.png', '.webp'):
raise TypeError(f'不支持的文件格式:{pic_type}')
pic_type = 'jpeg' if pic_type == '.jpg' else pic_type[1:] pic_type = 'jpeg' if pic_type == '.jpg' else pic_type[1:]
width, height = self.size width, height = self.size
@ -1099,8 +1103,7 @@ class Screencast(object):
"""非节俭模式运行方法""" """非节俭模式运行方法"""
self._running = True self._running = True
while self._enable: while self._enable:
p = self._path / f'{time()}.jpg' self._page.get_screenshot(path=self._path, name=f'{time()}.jpg')
self._page.get_screenshot(path=p)
sleep(.04) sleep(.04)
self._running = False self._running = False

View File

@ -198,13 +198,13 @@ class ChromiumBase(BasePage):
def get_local_storage(self, item: str = None) -> Union[str, dict, None]: ... def get_local_storage(self, item: str = None) -> Union[str, dict, None]: ...
def get_screenshot(self, path: [str, Path] = None, def get_screenshot(self, path: [str, Path] = None, name: str = None,
as_bytes: [bool, str] = None, as_base64: [bool, str] = None, as_bytes: [bool, str] = None, as_base64: [bool, str] = None,
full_page: bool = False, full_page: bool = False,
left_top: Tuple[int, int] = None, left_top: Tuple[int, int] = None,
right_bottom: Tuple[int, int] = None) -> Union[str, bytes]: ... right_bottom: Tuple[int, int] = None) -> Union[str, bytes]: ...
def _get_screenshot(self, path: [str, Path] = None, def _get_screenshot(self, path: [str, Path] = None, name: str = None,
as_bytes: [bool, str] = None, as_base64: [bool, str] = None, as_bytes: [bool, str] = None, as_base64: [bool, str] = None,
full_page: bool = False, full_page: bool = False,
left_top: Tuple[int, int] = None, left_top: Tuple[int, int] = None,

View File

@ -3,8 +3,7 @@
@Author : g1879 @Author : g1879
@Contact : g1879@qq.com @Contact : g1879@qq.com
""" """
from os import sep from os.path import basename, sep
from os.path import basename
from pathlib import Path from pathlib import Path
from time import perf_counter, sleep from time import perf_counter, sleep
@ -12,7 +11,7 @@ from .base import DrissionElement, BaseElement
from .commons.constants import FRAME_ELEMENT, NoneElement, Settings from .commons.constants import FRAME_ELEMENT, NoneElement, Settings
from .commons.keys import keys_to_typing, keyDescriptionForString, keyDefinitions from .commons.keys import keys_to_typing, keyDescriptionForString, keyDefinitions
from .commons.locator import get_loc from .commons.locator import get_loc
from .commons.tools import make_valid_name from .commons.tools import get_usable_path
from .commons.web import make_absolute_link, get_ele_txt, format_html, is_js_func, location_in_viewport, offset_scroll from .commons.web import make_absolute_link, get_ele_txt, format_html, is_js_func, location_in_viewport, offset_scroll
from .errors import ContextLossError, ElementLossError, JavaScriptError, NoRectError, ElementNotFoundError, \ from .errors import ContextLossError, ElementLossError, JavaScriptError, NoRectError, ElementNotFoundError, \
CDPError, NoResourceError, CanNotClickError CDPError, NoResourceError, CanNotClickError
@ -481,50 +480,53 @@ class ChromiumElement(DrissionElement):
else: else:
return result['content'] return result['content']
def save(self, path=None, rename=None, timeout=None): def save(self, path=None, name=None, timeout=None):
"""保存图片或其它有src属性的元素的资源 """保存图片或其它有src属性的元素的资源
:param path: 文件保存路径为None时保存到当前文件夹 :param path: 文件保存路径为None时保存到当前文件夹
:param rename: 文件名称为None时从资源url获取 :param name: 文件名称为None时从资源url获取
:param timeout: 等待资源加载的超时时间 :param timeout: 等待资源加载的超时时间
:return: None :return: 返回保存路径
""" """
data = self.get_src(timeout=timeout) data = self.get_src(timeout=timeout)
if not data: if not data:
raise NoResourceError raise NoResourceError
path = path or '.' path = path or '.'
rename = rename or basename(self.prop('currentSrc')) name = name or basename(self.prop('currentSrc'))
rename = make_valid_name(rename) path = get_usable_path(f'{path}{sep}{name}').absolute()
write_type = 'wb' if isinstance(data, bytes) else 'w' write_type = 'wb' if isinstance(data, bytes) else 'w'
Path(path).mkdir(parents=True, exist_ok=True) with open(path, write_type) as f:
with open(f'{path}{sep}{rename}', write_type) as f:
f.write(data) f.write(data)
def get_screenshot(self, path=None, as_bytes=None, as_base64=None): return str(path)
def get_screenshot(self, path=None, name=None, as_bytes=None, as_base64=None, scroll_to_center=False):
"""对当前元素截图,可保存到文件,或以字节方式返回 """对当前元素截图,可保存到文件,或以字节方式返回
:param path: 完整路径后缀可选 'jpg','jpeg','png','webp' :param path: 文件保存路径
:param name: 完整文件名后缀可选 'jpg','jpeg','png','webp'
:param as_bytes: 是否以字节形式返回图片可选 'jpg','jpeg','png','webp'生效时path参数和as_base64参数无效 :param as_bytes: 是否以字节形式返回图片可选 'jpg','jpeg','png','webp'生效时path参数和as_base64参数无效
:param as_base64: 是否以base64字符串形式返回图片可选 'jpg','jpeg','png','webp'生效时path参数无效 :param as_base64: 是否以base64字符串形式返回图片可选 'jpg','jpeg','png','webp'生效时path参数无效
:param scroll_to_center: 截图前是否滚动到视口中央
:return: 图片完整路径或字节文本 :return: 图片完整路径或字节文本
""" """
if self.tag == 'img': # 等待图片加载完成 if self.tag == 'img': # 等待图片加载完成
js = ('return this.complete && typeof this.naturalWidth != "undefined" ' js = ('return this.complete && typeof this.naturalWidth != "undefined" && this.naturalWidth > 0 '
'&& this.naturalWidth > 0 && typeof this.naturalHeight != "undefined" ' '&& typeof this.naturalHeight != "undefined" && this.naturalHeight > 0')
'&& this.naturalHeight > 0')
end_time = perf_counter() + self.page.timeout end_time = perf_counter() + self.page.timeout
while not self.run_js(js) and perf_counter() < end_time: while not self.run_js(js) and perf_counter() < end_time:
sleep(.1) sleep(.1)
# self.scroll.to_see(center=True) if scroll_to_center:
# sleep(1) self.scroll.to_see(center=True)
left, top = self.location left, top = self.location
width, height = self.size width, height = self.size
left_top = (left, top) left_top = (left, top)
right_bottom = (left + width, top + height) right_bottom = (left + width, top + height)
if not path: if not name:
path = f'{self.tag}.jpg' name = f'{self.tag}.jpg'
return self.page._get_screenshot(path, as_bytes=as_bytes, as_base64=as_base64, full_page=False, return self.page._get_screenshot(path, name, as_bytes=as_bytes, as_base64=as_base64, full_page=False,
left_top=left_top, right_bottom=right_bottom, ele=self) left_top=left_top, right_bottom=right_bottom, ele=self)
def input(self, vals, clear=True, by_js=False): def input(self, vals, clear=True, by_js=False):

View File

@ -180,10 +180,10 @@ class ChromiumElement(DrissionElement):
def get_src(self, timeout: float = None, base64_to_bytes: bool = True) -> Union[bytes, str, None]: ... def get_src(self, timeout: float = None, base64_to_bytes: bool = True) -> Union[bytes, str, None]: ...
def save(self, path: [str, bool] = None, rename: str = None, timeout: float = None) -> None: ... def save(self, path: [str, bool] = None, name: str = None, timeout: float = None) -> str: ...
def get_screenshot(self, path: [str, Path] = None, as_bytes: [bool, str] = None, def get_screenshot(self, path: [str, Path] = None, name: str = None, as_bytes: [bool, str] = None,
as_base64: [bool, str] = None) -> Union[str, bytes]: ... as_base64: [bool, str] = None, scroll_to_center: bool = False) -> Union[str, bytes]: ...
def input(self, vals: Any, clear: bool = True, by_js: bool = False) -> None: ... def input(self, vals: Any, clear: bool = True, by_js: bool = False) -> None: ...

View File

@ -4,6 +4,7 @@
@Contact : g1879@qq.com @Contact : g1879@qq.com
""" """
from copy import copy from copy import copy
from os.path import sep
from re import search from re import search
from threading import Thread from threading import Thread
from time import sleep, perf_counter from time import sleep, perf_counter
@ -492,19 +493,21 @@ class ChromiumFrame(ChromiumBase):
self._check_ok() self._check_ok()
return self.frame_ele.afters(filter_loc, timeout, ele_only=ele_only) return self.frame_ele.afters(filter_loc, timeout, ele_only=ele_only)
def get_screenshot(self, path=None, as_bytes=None, as_base64=None): def get_screenshot(self, path=None, name=None, as_bytes=None, as_base64=None):
"""对页面进行截图可对整个网页、可见网页、指定范围截图。对可视范围外截图需要90以上版本浏览器支持 """对页面进行截图可对整个网页、可见网页、指定范围截图。对可视范围外截图需要90以上版本浏览器支持
:param path: 完整路径后缀可选 'jpg','jpeg','png','webp' :param path: 文件保存路径
:param name: 完整文件名后缀可选 'jpg','jpeg','png','webp'
:param as_bytes: 是否以字节形式返回图片可选 'jpg','jpeg','png','webp'生效时path参数和as_base64参数无效 :param as_bytes: 是否以字节形式返回图片可选 'jpg','jpeg','png','webp'生效时path参数和as_base64参数无效
:param as_base64: 是否以base64字符串形式返回图片可选 'jpg','jpeg','png','webp'生效时path参数无效 :param as_base64: 是否以base64字符串形式返回图片可选 'jpg','jpeg','png','webp'生效时path参数无效
:return: 图片完整路径或字节文本 :return: 图片完整路径或字节文本
""" """
return self.frame_ele.get_screenshot(path=path, as_bytes=as_bytes, as_base64=as_base64) return self.frame_ele.get_screenshot(path=path, name=name, as_bytes=as_bytes, as_base64=as_base64)
def _get_screenshot(self, path=None, as_bytes: [bool, str] = None, as_base64: [bool, str] = None, def _get_screenshot(self, path=None, name=None, as_bytes: [bool, str] = None, as_base64: [bool, str] = None,
full_page=False, left_top=None, right_bottom=None, ele=None): full_page=False, left_top=None, right_bottom=None, ele=None):
"""实现对元素截图 """实现截图
:param path: 完整路径后缀可选 'jpg','jpeg','png','webp' :param path: 文件保存路径
:param name: 完整文件名后缀可选 'jpg','jpeg','png','webp'
:param as_bytes: 是否以字节形式返回图片可选 'jpg','jpeg','png','webp'生效时path参数和as_base64参数无效 :param as_bytes: 是否以字节形式返回图片可选 'jpg','jpeg','png','webp'生效时path参数和as_base64参数无效
:param as_base64: 是否以base64字符串形式返回图片可选 'jpg','jpeg','png','webp'生效时path参数无效 :param as_base64: 是否以base64字符串形式返回图片可选 'jpg','jpeg','png','webp'生效时path参数无效
:param full_page: 是否整页截图为True截取整个网页为False截取可视窗口 :param full_page: 是否整页截图为True截取整个网页为False截取可视窗口
@ -514,7 +517,7 @@ class ChromiumFrame(ChromiumBase):
:return: 图片完整路径或字节文本 :return: 图片完整路径或字节文本
""" """
if not self._is_diff_domain: if not self._is_diff_domain:
return super().get_screenshot(path=path, as_bytes=as_bytes, as_base64=as_base64, return super().get_screenshot(path=path, name=name, as_bytes=as_bytes, as_base64=as_base64,
full_page=full_page, left_top=left_top, right_bottom=right_bottom) full_page=full_page, left_top=left_top, right_bottom=right_bottom)
if as_bytes: if as_bytes:
@ -522,7 +525,7 @@ class ChromiumFrame(ChromiumBase):
pic_type = 'png' pic_type = 'png'
else: else:
if as_bytes not in ('jpg', 'jpeg', 'png', 'webp'): if as_bytes not in ('jpg', 'jpeg', 'png', 'webp'):
raise ValueError("只能接收 'jpg', 'jpeg', 'png', 'webp' 四种格式。") raise TypeError("只能接收 'jpg', 'jpeg', 'png', 'webp' 四种格式。")
pic_type = 'jpeg' if as_bytes == 'jpg' else as_bytes pic_type = 'jpeg' if as_bytes == 'jpg' else as_bytes
elif as_base64: elif as_base64:
@ -530,16 +533,18 @@ class ChromiumFrame(ChromiumBase):
pic_type = 'png' pic_type = 'png'
else: else:
if as_base64 not in ('jpg', 'jpeg', 'png', 'webp'): if as_base64 not in ('jpg', 'jpeg', 'png', 'webp'):
raise ValueError("只能接收 'jpg', 'jpeg', 'png', 'webp' 四种格式。") raise TypeError("只能接收 'jpg', 'jpeg', 'png', 'webp' 四种格式。")
pic_type = 'jpeg' if as_base64 == 'jpg' else as_base64 pic_type = 'jpeg' if as_base64 == 'jpg' else as_base64
else: else:
if not path: if not path:
path = f'{self.title}.jpg' path = '.'
path = get_usable_path(path) if not name:
name = f'{self.title}.jpg'
if not name.endswith(('.jpg', '.jpeg', '.png', '.webp')):
name = f'{name}.jpg'
path = get_usable_path(f'{path}{sep}{name}')
pic_type = path.suffix.lower() pic_type = path.suffix.lower()
if pic_type not in ('.jpg', '.jpeg', '.png', '.webp'):
raise TypeError(f'不支持的文件格式:{pic_type}')
pic_type = 'jpeg' if pic_type == '.jpg' else pic_type[1:] pic_type = 'jpeg' if pic_type == '.jpg' else pic_type[1:]
self.frame_ele.scroll.to_see(center=True) self.frame_ele.scroll.to_see(center=True)
@ -562,7 +567,7 @@ class ChromiumFrame(ChromiumBase):
new_ele.scroll.to_see(True) new_ele.scroll.to_see(True)
top = int(self.frame_ele.style('border-top').split('px')[0]) top = int(self.frame_ele.style('border-top').split('px')[0])
left = int(self.frame_ele.style('border-left').split('px')[0]) left = int(self.frame_ele.style('border-left').split('px')[0])
r = self._target_page.get_screenshot(path=path, as_bytes=as_bytes, as_base64=as_base64, r = self._target_page.get_screenshot(path=path, name=name, as_bytes=as_bytes, as_base64=as_base64,
left_top=(cx + left, cy + top), right_bottom=(cx + w + left, cy + h + top)) left_top=(cx + left, cy + top), right_bottom=(cx + w + left, cy + h + top))
self._target_page.remove_ele(new_ele) self._target_page.remove_ele(new_ele)
return r return r

View File

@ -174,11 +174,11 @@ class ChromiumFrame(ChromiumBase):
timeout: float = None, timeout: float = None,
ele_only: bool = True) -> List[Union[ChromiumElement, str]]: ... ele_only: bool = True) -> List[Union[ChromiumElement, str]]: ...
def get_screenshot(self, path: [str, Path] = None, def get_screenshot(self, path: [str, Path] = None, name: str = None,
as_bytes: [bool, str] = None, as_bytes: [bool, str] = None,
as_base64: [bool, str] = None) -> Union[str, bytes]: ... as_base64: [bool, str] = None) -> Union[str, bytes]: ...
def _get_screenshot(self, path: [str, Path] = None, def _get_screenshot(self, path: [str, Path] = None, name: str = None,
as_bytes: [bool, str] = None, as_base64: [bool, str] = None, as_bytes: [bool, str] = None, as_base64: [bool, str] = None,
full_page: bool = False, full_page: bool = False,
left_top: Tuple[int, int] = None, left_top: Tuple[int, int] = None,