mirror of
https://gitee.com/g1879/DrissionPage.git
synced 2024-12-10 04:00:23 +08:00
3.2.12页面对象增加滚动行为和等待滚动结束设置;优化滚动逻辑;修复保存图片未正确等待问题
This commit is contained in:
parent
7c43573fad
commit
04e82ee5e6
@ -931,6 +931,11 @@ class ChromiumBaseSetter(object):
|
||||
"""返回用于设置页面加载策略的对象"""
|
||||
return PageLoadStrategy(self._page)
|
||||
|
||||
@property
|
||||
def scroll(self):
|
||||
"""返回用于设置页面滚动设置的对象"""
|
||||
return PageScrollSetter(self._page.scroll)
|
||||
|
||||
def timeouts(self, implicit=None, page_load=None, script=None):
|
||||
"""设置超时时间,单位为秒
|
||||
:param implicit: 查找元素超时时间
|
||||
@ -1089,16 +1094,30 @@ class ChromiumPageScroll(ChromiumScroll):
|
||||
self.t1 = 'window'
|
||||
self.t2 = 'document.documentElement'
|
||||
|
||||
def to_see(self, loc_or_ele):
|
||||
def to_see(self, loc_or_ele, center=False):
|
||||
"""滚动页面直到元素可见
|
||||
:param loc_or_ele: 元素的定位信息,可以是loc元组,或查询字符串
|
||||
:param center: 是否尽量滚动到页面正中
|
||||
:return: None
|
||||
"""
|
||||
ele = self._driver._ele(loc_or_ele)
|
||||
ele.run_js('this.scrollIntoView({behavior: "auto", block: "nearest", inline: "nearest"});')
|
||||
self._to_see(ele, center)
|
||||
|
||||
def _to_see(self, ele, center):
|
||||
"""执行滚动页面直到元素可见
|
||||
:param ele: 元素对象
|
||||
:param center: 是否尽量滚动到页面正中
|
||||
:return: None
|
||||
"""
|
||||
if center:
|
||||
ele.run_js('this.scrollIntoViewIfNeeded();')
|
||||
self._wait_scrolled()
|
||||
return
|
||||
|
||||
ele.run_js('this.scrollIntoViewIfNeeded(false);')
|
||||
if ele.states.is_covered:
|
||||
ele.run_js('this.scrollIntoView({behavior: "auto", block: "center", inline: "center"});')
|
||||
ele.run_js('this.scrollIntoViewIfNeeded();')
|
||||
self._wait_scrolled()
|
||||
|
||||
|
||||
class Timeout(object):
|
||||
@ -1149,3 +1168,27 @@ class PageLoadStrategy(object):
|
||||
def none(self):
|
||||
"""设置页面加载策略为none"""
|
||||
self._page._page_load_strategy = 'none'
|
||||
|
||||
|
||||
class PageScrollSetter(object):
|
||||
def __init__(self, scroll):
|
||||
self._scroll = scroll
|
||||
|
||||
def wait_complete(self, on_off=True):
|
||||
"""设置滚动命令后是否等待完成
|
||||
:param on_off: 开或关
|
||||
:return: None
|
||||
"""
|
||||
if not isinstance(on_off, bool):
|
||||
raise TypeError('on_off必须为bool。')
|
||||
self._scroll._wait_complete = on_off
|
||||
|
||||
def smooth(self, on_off=True):
|
||||
"""设置页面滚动是否平滑滚动
|
||||
:param on_off: 开或关
|
||||
:return: None
|
||||
"""
|
||||
if not isinstance(on_off, bool):
|
||||
raise TypeError('on_off必须为bool。')
|
||||
b = 'smooth' if on_off else 'auto'
|
||||
self._scroll._driver.run_js(f'document.documentElement.style.setProperty("scroll-behavior","{b}");')
|
||||
|
@ -241,7 +241,9 @@ class ChromiumBaseWaiter(object):
|
||||
class ChromiumPageScroll(ChromiumScroll):
|
||||
def __init__(self, page: ChromiumBase): ...
|
||||
|
||||
def to_see(self, loc_or_ele: Union[str, tuple, ChromiumElement]) -> None: ...
|
||||
def to_see(self, loc_or_ele: Union[str, tuple, ChromiumElement], center: bool = False) -> None: ...
|
||||
|
||||
def _to_see(self, ele: ChromiumElement, center: bool) -> None: ...
|
||||
|
||||
|
||||
class ChromiumBaseSetter(object):
|
||||
@ -251,6 +253,9 @@ class ChromiumBaseSetter(object):
|
||||
@property
|
||||
def load_strategy(self) -> PageLoadStrategy: ...
|
||||
|
||||
@property
|
||||
def scroll(self) -> PageScrollSetter: ...
|
||||
|
||||
def timeouts(self, implicit: float = None, page_load: float = None, script: float = None): ...
|
||||
|
||||
def user_agent(self, ua: str, platform: str = None) -> None: ...
|
||||
@ -286,3 +291,12 @@ class PageLoadStrategy(object):
|
||||
def eager(self) -> None: ...
|
||||
|
||||
def none(self) -> None: ...
|
||||
|
||||
|
||||
class PageScrollSetter(object):
|
||||
def __init__(self, scroll: ChromiumPageScroll):
|
||||
self._scroll: ChromiumPageScroll = ...
|
||||
|
||||
def wait_complete(self, on_off: bool = True): ...
|
||||
|
||||
def smooth(self, on_off: bool = True): ...
|
||||
|
@ -172,7 +172,7 @@ class ChromiumElement(DrissionElement):
|
||||
def scroll(self):
|
||||
"""用于滚动滚动条的对象"""
|
||||
if self._scroll is None:
|
||||
self._scroll = ChromiumScroll(self)
|
||||
self._scroll = ChromiumElementScroll(self)
|
||||
return self._scroll
|
||||
|
||||
@property
|
||||
@ -429,6 +429,7 @@ class ChromiumElement(DrissionElement):
|
||||
while perf_counter() < end_time:
|
||||
try:
|
||||
result = self.page.run_cdp('Page.getResourceContent', frameId=frame, url=src)
|
||||
break
|
||||
except CallMethodError:
|
||||
pass
|
||||
sleep(.1)
|
||||
@ -477,7 +478,7 @@ class ChromiumElement(DrissionElement):
|
||||
while not self.run_js(js) and perf_counter() < end_time:
|
||||
sleep(.1)
|
||||
|
||||
self.page.scroll.to_see(self)
|
||||
self.scroll.to_see(center=True)
|
||||
sleep(1)
|
||||
left, top = self.location
|
||||
width, height = self.size
|
||||
@ -1648,7 +1649,7 @@ class Click(object):
|
||||
"""
|
||||
if not by_js:
|
||||
try:
|
||||
self._ele.page.scroll.to_see(self._ele)
|
||||
self._ele.scroll.to_see()
|
||||
if self._ele.states.is_in_viewport and not self._ele.states.is_covered:
|
||||
client_x, client_y = self._ele.locations.viewport_click_point
|
||||
self._click(client_x, client_y)
|
||||
@ -1711,10 +1712,12 @@ class ChromiumScroll(object):
|
||||
"""
|
||||
self._driver = ele
|
||||
self.t1 = self.t2 = 'this'
|
||||
self._wait_complete = False
|
||||
|
||||
def _run_js(self, js):
|
||||
js = js.format(self.t1, self.t2, self.t2)
|
||||
self._driver.run_js(js)
|
||||
self._wait_scrolled()
|
||||
|
||||
def to_top(self):
|
||||
"""滚动到顶端,水平位置不变"""
|
||||
@ -1774,6 +1777,36 @@ class ChromiumScroll(object):
|
||||
"""
|
||||
self._run_js(f'{{}}.scrollBy({pixel}, 0);')
|
||||
|
||||
def _wait_scrolled(self):
|
||||
if not self._wait_complete:
|
||||
return
|
||||
|
||||
page = self._driver.page if isinstance(self._driver, ChromiumElement) else self._driver
|
||||
r = page.run_cdp('Page.getLayoutMetrics')
|
||||
x = r['layoutViewport']['pageX']
|
||||
y = r['layoutViewport']['pageY']
|
||||
|
||||
while True:
|
||||
sleep(.1)
|
||||
r = page.run_cdp('Page.getLayoutMetrics')
|
||||
x1 = r['layoutViewport']['pageX']
|
||||
y1 = r['layoutViewport']['pageY']
|
||||
|
||||
if x == x1 and y == y1:
|
||||
break
|
||||
|
||||
x = x1
|
||||
y = y1
|
||||
|
||||
|
||||
class ChromiumElementScroll(ChromiumScroll):
|
||||
def to_see(self, center=False):
|
||||
"""滚动页面直到元素可见
|
||||
:param center: 是否尽量滚动到页面正中
|
||||
:return: None
|
||||
"""
|
||||
self._driver.page.scroll.to_see(self._driver, center=center)
|
||||
|
||||
|
||||
class ChromiumSelect(object):
|
||||
"""ChromiumSelect 类专门用于处理 d 模式下 select 标签"""
|
||||
|
@ -27,7 +27,7 @@ class ChromiumElement(DrissionElement):
|
||||
self._backend_id: str = ...
|
||||
self._doc_id: str = ...
|
||||
self._ids: ChromiumElementIds = ...
|
||||
self._scroll: ChromiumScroll = ...
|
||||
self._scroll: ChromiumElementScroll = ...
|
||||
self._click: Click = ...
|
||||
self._select: ChromiumSelect = ...
|
||||
self._wait: ChromiumElementWaiter = ...
|
||||
@ -89,7 +89,7 @@ class ChromiumElement(DrissionElement):
|
||||
def sr(self) -> Union[None, ChromiumShadowRoot]: ...
|
||||
|
||||
@property
|
||||
def scroll(self) -> ChromiumScroll: ...
|
||||
def scroll(self) -> ChromiumElementScroll: ...
|
||||
|
||||
@property
|
||||
def click(self) -> Click: ...
|
||||
@ -163,7 +163,7 @@ class ChromiumElement(DrissionElement):
|
||||
def _find_elements(self, loc_or_str: Union[Tuple[str, str], str], timeout: float = None,
|
||||
single: bool = True, relative: bool = False, raise_err: bool = False) \
|
||||
-> Union[ChromiumElement, ChromiumFrame, str, NoneElement,
|
||||
List[Union[ChromiumElement, ChromiumFrame, str]]]: ...
|
||||
List[Union[ChromiumElement, ChromiumFrame, str]]]: ...
|
||||
|
||||
def style(self, style: str, pseudo_ele: str = '') -> str: ...
|
||||
|
||||
@ -453,6 +453,7 @@ class ChromiumScroll(object):
|
||||
self.t1: str = ...
|
||||
self.t2: str = ...
|
||||
self._driver: Union[ChromiumPage, ChromiumElement, ChromiumFrame] = ...
|
||||
self._wait_complete: bool = ...
|
||||
|
||||
def _run_js(self, js: str): ...
|
||||
|
||||
@ -476,6 +477,13 @@ class ChromiumScroll(object):
|
||||
|
||||
def right(self, pixel: int = 300) -> None: ...
|
||||
|
||||
def _wait_scrolled(self) -> None: ...
|
||||
|
||||
|
||||
class ChromiumElementScroll(ChromiumScroll):
|
||||
|
||||
def to_see(self, center: bool = False) -> None: ...
|
||||
|
||||
|
||||
class ChromiumSelect(object):
|
||||
def __init__(self, ele: ChromiumElement):
|
||||
|
@ -428,48 +428,55 @@ class ChromiumFrame(ChromiumBase):
|
||||
return super().get_screenshot(path=path, as_bytes=as_bytes, as_base64=as_base64,
|
||||
full_page=full_page, left_top=left_top, right_bottom=right_bottom)
|
||||
|
||||
else:
|
||||
if as_bytes:
|
||||
if as_bytes is True:
|
||||
pic_type = 'png'
|
||||
else:
|
||||
if as_bytes not in ('jpg', 'jpeg', 'png', 'webp'):
|
||||
raise ValueError("只能接收 'jpg', 'jpeg', 'png', 'webp' 四种格式。")
|
||||
pic_type = 'jpeg' if as_bytes == 'jpg' else as_bytes
|
||||
|
||||
elif as_base64:
|
||||
if as_base64 is True:
|
||||
pic_type = 'png'
|
||||
else:
|
||||
if as_base64 not in ('jpg', 'jpeg', 'png', 'webp'):
|
||||
raise ValueError("只能接收 'jpg', 'jpeg', 'png', 'webp' 四种格式。")
|
||||
pic_type = 'jpeg' if as_base64 == 'jpg' else as_base64
|
||||
|
||||
if as_bytes:
|
||||
if as_bytes is True:
|
||||
pic_type = 'png'
|
||||
else:
|
||||
if not path:
|
||||
path = f'{self.title}.jpg'
|
||||
path = get_usable_path(path)
|
||||
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:]
|
||||
if as_bytes not in ('jpg', 'jpeg', 'png', 'webp'):
|
||||
raise ValueError("只能接收 'jpg', 'jpeg', 'png', 'webp' 四种格式。")
|
||||
pic_type = 'jpeg' if as_bytes == 'jpg' else as_bytes
|
||||
|
||||
self.scroll.to_see(ele)
|
||||
cx, cy = ele.locations.viewport_location
|
||||
w, h = ele.size
|
||||
img_data = f'data:image/{pic_type};base64,{self.frame_ele.get_screenshot(as_base64=True)}'
|
||||
body = self.page('t:body')
|
||||
first_child = body('c::first-child')
|
||||
js = f'''
|
||||
haskell = document.createElement('img');
|
||||
haskell.src = "{img_data}";
|
||||
arguments[0].insertBefore(haskell, this);
|
||||
return haskell;'''
|
||||
new_ele = first_child.run_js(js, body)
|
||||
r = self.page.get_screenshot(path=path, as_bytes=as_bytes, as_base64=as_base64,
|
||||
left_top=(cx, cy), right_bottom=(cx + w, cy + h))
|
||||
self.page.remove_ele(new_ele)
|
||||
return r
|
||||
elif as_base64:
|
||||
if as_base64 is True:
|
||||
pic_type = 'png'
|
||||
else:
|
||||
if as_base64 not in ('jpg', 'jpeg', 'png', 'webp'):
|
||||
raise ValueError("只能接收 'jpg', 'jpeg', 'png', 'webp' 四种格式。")
|
||||
pic_type = 'jpeg' if as_base64 == 'jpg' else as_base64
|
||||
|
||||
else:
|
||||
if not path:
|
||||
path = f'{self.title}.jpg'
|
||||
path = get_usable_path(path)
|
||||
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:]
|
||||
|
||||
self.frame_ele.scroll.to_see(center=True)
|
||||
self.scroll.to_see(ele, center=True)
|
||||
cx, cy = ele.locations.viewport_location
|
||||
w, h = ele.size
|
||||
img_data = f'data:image/{pic_type};base64,{self.frame_ele.get_screenshot(as_base64=True)}'
|
||||
body = self.page('t:body')
|
||||
first_child = body('c::first-child')
|
||||
if not isinstance(first_child, ChromiumElement):
|
||||
first_child = first_child.frame_ele
|
||||
js = f'''
|
||||
img = document.createElement('img');
|
||||
img.src = "{img_data}";
|
||||
img.style.setProperty("z-index",9999999);
|
||||
img.style.setProperty("position","fixed");
|
||||
arguments[0].insertBefore(img, this);
|
||||
return img;'''
|
||||
new_ele = first_child.run_js(js, body)
|
||||
new_ele.scroll.to_see(True)
|
||||
top = int(self.frame_ele.style('border-top').split('px')[0])
|
||||
left = int(self.frame_ele.style('border-left').split('px')[0])
|
||||
r = self.page.get_screenshot(path=path, as_bytes=as_bytes, as_base64=as_base64,
|
||||
left_top=(cx + left, cy + top), right_bottom=(cx + w + left, cy + h + top))
|
||||
self.page.remove_ele(new_ele)
|
||||
return r
|
||||
|
||||
def _find_elements(self, loc_or_ele, timeout=None, single=True, relative=False, raise_err=None):
|
||||
"""在frame内查找单个元素
|
||||
@ -621,14 +628,16 @@ class ChromiumFrameScroll(ChromiumPageScroll):
|
||||
"""
|
||||
self._driver = frame.doc_ele
|
||||
self.t1 = self.t2 = 'this.documentElement'
|
||||
self._wait_complete = False
|
||||
|
||||
def to_see(self, loc_or_ele):
|
||||
def to_see(self, loc_or_ele, center=False):
|
||||
"""滚动页面直到元素可见
|
||||
:param loc_or_ele: 元素的定位信息,可以是loc元组,或查询字符串
|
||||
:param center: 是否尽量滚动到页面正中
|
||||
:return: None
|
||||
"""
|
||||
ele = loc_or_ele if isinstance(loc_or_ele, ChromiumElement) else self._driver._ele(loc_or_ele)
|
||||
ele.run_js('this.scrollIntoView({behavior: "auto", block: "center", inline: "center"});')
|
||||
self._to_see(ele, center)
|
||||
|
||||
|
||||
class ChromiumFrameSetter(ChromiumBaseSetter):
|
||||
|
@ -196,6 +196,8 @@ class ChromiumFrameIds(object):
|
||||
class ChromiumFrameScroll(ChromiumPageScroll):
|
||||
def __init__(self, frame: ChromiumFrame) -> None: ...
|
||||
|
||||
def to_see(self, loc_or_ele: Union[str, tuple, ChromiumElement], center: bool = False) -> None: ...
|
||||
|
||||
|
||||
class ChromiumFrameSetter(ChromiumBaseSetter):
|
||||
_page: ChromiumFrame = ...
|
||||
|
Loading…
x
Reference in New Issue
Block a user