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)
|
return PageLoadStrategy(self._page)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def scroll(self):
|
||||||
|
"""返回用于设置页面滚动设置的对象"""
|
||||||
|
return PageScrollSetter(self._page.scroll)
|
||||||
|
|
||||||
def timeouts(self, implicit=None, page_load=None, script=None):
|
def timeouts(self, implicit=None, page_load=None, script=None):
|
||||||
"""设置超时时间,单位为秒
|
"""设置超时时间,单位为秒
|
||||||
:param implicit: 查找元素超时时间
|
:param implicit: 查找元素超时时间
|
||||||
@ -1089,16 +1094,30 @@ class ChromiumPageScroll(ChromiumScroll):
|
|||||||
self.t1 = 'window'
|
self.t1 = 'window'
|
||||||
self.t2 = 'document.documentElement'
|
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 loc_or_ele: 元素的定位信息,可以是loc元组,或查询字符串
|
||||||
|
:param center: 是否尽量滚动到页面正中
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
ele = self._driver._ele(loc_or_ele)
|
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:
|
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):
|
class Timeout(object):
|
||||||
@ -1149,3 +1168,27 @@ class PageLoadStrategy(object):
|
|||||||
def none(self):
|
def none(self):
|
||||||
"""设置页面加载策略为none"""
|
"""设置页面加载策略为none"""
|
||||||
self._page._page_load_strategy = '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):
|
class ChromiumPageScroll(ChromiumScroll):
|
||||||
def __init__(self, page: ChromiumBase): ...
|
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):
|
class ChromiumBaseSetter(object):
|
||||||
@ -251,6 +253,9 @@ class ChromiumBaseSetter(object):
|
|||||||
@property
|
@property
|
||||||
def load_strategy(self) -> PageLoadStrategy: ...
|
def load_strategy(self) -> PageLoadStrategy: ...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def scroll(self) -> PageScrollSetter: ...
|
||||||
|
|
||||||
def timeouts(self, implicit: float = None, page_load: float = None, script: float = None): ...
|
def timeouts(self, implicit: float = None, page_load: float = None, script: float = None): ...
|
||||||
|
|
||||||
def user_agent(self, ua: str, platform: str = None) -> None: ...
|
def user_agent(self, ua: str, platform: str = None) -> None: ...
|
||||||
@ -286,3 +291,12 @@ class PageLoadStrategy(object):
|
|||||||
def eager(self) -> None: ...
|
def eager(self) -> None: ...
|
||||||
|
|
||||||
def none(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):
|
def scroll(self):
|
||||||
"""用于滚动滚动条的对象"""
|
"""用于滚动滚动条的对象"""
|
||||||
if self._scroll is None:
|
if self._scroll is None:
|
||||||
self._scroll = ChromiumScroll(self)
|
self._scroll = ChromiumElementScroll(self)
|
||||||
return self._scroll
|
return self._scroll
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -429,6 +429,7 @@ class ChromiumElement(DrissionElement):
|
|||||||
while perf_counter() < end_time:
|
while perf_counter() < end_time:
|
||||||
try:
|
try:
|
||||||
result = self.page.run_cdp('Page.getResourceContent', frameId=frame, url=src)
|
result = self.page.run_cdp('Page.getResourceContent', frameId=frame, url=src)
|
||||||
|
break
|
||||||
except CallMethodError:
|
except CallMethodError:
|
||||||
pass
|
pass
|
||||||
sleep(.1)
|
sleep(.1)
|
||||||
@ -477,7 +478,7 @@ class ChromiumElement(DrissionElement):
|
|||||||
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.page.scroll.to_see(self)
|
self.scroll.to_see(center=True)
|
||||||
sleep(1)
|
sleep(1)
|
||||||
left, top = self.location
|
left, top = self.location
|
||||||
width, height = self.size
|
width, height = self.size
|
||||||
@ -1648,7 +1649,7 @@ class Click(object):
|
|||||||
"""
|
"""
|
||||||
if not by_js:
|
if not by_js:
|
||||||
try:
|
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:
|
if self._ele.states.is_in_viewport and not self._ele.states.is_covered:
|
||||||
client_x, client_y = self._ele.locations.viewport_click_point
|
client_x, client_y = self._ele.locations.viewport_click_point
|
||||||
self._click(client_x, client_y)
|
self._click(client_x, client_y)
|
||||||
@ -1711,10 +1712,12 @@ class ChromiumScroll(object):
|
|||||||
"""
|
"""
|
||||||
self._driver = ele
|
self._driver = ele
|
||||||
self.t1 = self.t2 = 'this'
|
self.t1 = self.t2 = 'this'
|
||||||
|
self._wait_complete = False
|
||||||
|
|
||||||
def _run_js(self, js):
|
def _run_js(self, js):
|
||||||
js = js.format(self.t1, self.t2, self.t2)
|
js = js.format(self.t1, self.t2, self.t2)
|
||||||
self._driver.run_js(js)
|
self._driver.run_js(js)
|
||||||
|
self._wait_scrolled()
|
||||||
|
|
||||||
def to_top(self):
|
def to_top(self):
|
||||||
"""滚动到顶端,水平位置不变"""
|
"""滚动到顶端,水平位置不变"""
|
||||||
@ -1774,6 +1777,36 @@ class ChromiumScroll(object):
|
|||||||
"""
|
"""
|
||||||
self._run_js(f'{{}}.scrollBy({pixel}, 0);')
|
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):
|
class ChromiumSelect(object):
|
||||||
"""ChromiumSelect 类专门用于处理 d 模式下 select 标签"""
|
"""ChromiumSelect 类专门用于处理 d 模式下 select 标签"""
|
||||||
|
@ -27,7 +27,7 @@ class ChromiumElement(DrissionElement):
|
|||||||
self._backend_id: str = ...
|
self._backend_id: str = ...
|
||||||
self._doc_id: str = ...
|
self._doc_id: str = ...
|
||||||
self._ids: ChromiumElementIds = ...
|
self._ids: ChromiumElementIds = ...
|
||||||
self._scroll: ChromiumScroll = ...
|
self._scroll: ChromiumElementScroll = ...
|
||||||
self._click: Click = ...
|
self._click: Click = ...
|
||||||
self._select: ChromiumSelect = ...
|
self._select: ChromiumSelect = ...
|
||||||
self._wait: ChromiumElementWaiter = ...
|
self._wait: ChromiumElementWaiter = ...
|
||||||
@ -89,7 +89,7 @@ class ChromiumElement(DrissionElement):
|
|||||||
def sr(self) -> Union[None, ChromiumShadowRoot]: ...
|
def sr(self) -> Union[None, ChromiumShadowRoot]: ...
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def scroll(self) -> ChromiumScroll: ...
|
def scroll(self) -> ChromiumElementScroll: ...
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def click(self) -> Click: ...
|
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,
|
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) \
|
single: bool = True, relative: bool = False, raise_err: bool = False) \
|
||||||
-> Union[ChromiumElement, ChromiumFrame, str, NoneElement,
|
-> Union[ChromiumElement, ChromiumFrame, str, NoneElement,
|
||||||
List[Union[ChromiumElement, ChromiumFrame, str]]]: ...
|
List[Union[ChromiumElement, ChromiumFrame, str]]]: ...
|
||||||
|
|
||||||
def style(self, style: str, pseudo_ele: str = '') -> str: ...
|
def style(self, style: str, pseudo_ele: str = '') -> str: ...
|
||||||
|
|
||||||
@ -453,6 +453,7 @@ class ChromiumScroll(object):
|
|||||||
self.t1: str = ...
|
self.t1: str = ...
|
||||||
self.t2: str = ...
|
self.t2: str = ...
|
||||||
self._driver: Union[ChromiumPage, ChromiumElement, ChromiumFrame] = ...
|
self._driver: Union[ChromiumPage, ChromiumElement, ChromiumFrame] = ...
|
||||||
|
self._wait_complete: bool = ...
|
||||||
|
|
||||||
def _run_js(self, js: str): ...
|
def _run_js(self, js: str): ...
|
||||||
|
|
||||||
@ -476,6 +477,13 @@ class ChromiumScroll(object):
|
|||||||
|
|
||||||
def right(self, pixel: int = 300) -> None: ...
|
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):
|
class ChromiumSelect(object):
|
||||||
def __init__(self, ele: ChromiumElement):
|
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,
|
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)
|
full_page=full_page, left_top=left_top, right_bottom=right_bottom)
|
||||||
|
|
||||||
else:
|
if as_bytes:
|
||||||
if as_bytes:
|
if as_bytes is True:
|
||||||
if as_bytes is True:
|
pic_type = 'png'
|
||||||
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
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if not path:
|
if as_bytes not in ('jpg', 'jpeg', 'png', 'webp'):
|
||||||
path = f'{self.title}.jpg'
|
raise ValueError("只能接收 'jpg', 'jpeg', 'png', 'webp' 四种格式。")
|
||||||
path = get_usable_path(path)
|
pic_type = 'jpeg' if as_bytes == 'jpg' else as_bytes
|
||||||
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.scroll.to_see(ele)
|
elif as_base64:
|
||||||
cx, cy = ele.locations.viewport_location
|
if as_base64 is True:
|
||||||
w, h = ele.size
|
pic_type = 'png'
|
||||||
img_data = f'data:image/{pic_type};base64,{self.frame_ele.get_screenshot(as_base64=True)}'
|
else:
|
||||||
body = self.page('t:body')
|
if as_base64 not in ('jpg', 'jpeg', 'png', 'webp'):
|
||||||
first_child = body('c::first-child')
|
raise ValueError("只能接收 'jpg', 'jpeg', 'png', 'webp' 四种格式。")
|
||||||
js = f'''
|
pic_type = 'jpeg' if as_base64 == 'jpg' else as_base64
|
||||||
haskell = document.createElement('img');
|
|
||||||
haskell.src = "{img_data}";
|
else:
|
||||||
arguments[0].insertBefore(haskell, this);
|
if not path:
|
||||||
return haskell;'''
|
path = f'{self.title}.jpg'
|
||||||
new_ele = first_child.run_js(js, body)
|
path = get_usable_path(path)
|
||||||
r = self.page.get_screenshot(path=path, as_bytes=as_bytes, as_base64=as_base64,
|
pic_type = path.suffix.lower()
|
||||||
left_top=(cx, cy), right_bottom=(cx + w, cy + h))
|
if pic_type not in ('.jpg', '.jpeg', '.png', '.webp'):
|
||||||
self.page.remove_ele(new_ele)
|
raise TypeError(f'不支持的文件格式:{pic_type}。')
|
||||||
return r
|
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):
|
def _find_elements(self, loc_or_ele, timeout=None, single=True, relative=False, raise_err=None):
|
||||||
"""在frame内查找单个元素
|
"""在frame内查找单个元素
|
||||||
@ -621,14 +628,16 @@ class ChromiumFrameScroll(ChromiumPageScroll):
|
|||||||
"""
|
"""
|
||||||
self._driver = frame.doc_ele
|
self._driver = frame.doc_ele
|
||||||
self.t1 = self.t2 = 'this.documentElement'
|
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 loc_or_ele: 元素的定位信息,可以是loc元组,或查询字符串
|
||||||
|
:param center: 是否尽量滚动到页面正中
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
ele = loc_or_ele if isinstance(loc_or_ele, ChromiumElement) else self._driver._ele(loc_or_ele)
|
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):
|
class ChromiumFrameSetter(ChromiumBaseSetter):
|
||||||
|
@ -196,6 +196,8 @@ class ChromiumFrameIds(object):
|
|||||||
class ChromiumFrameScroll(ChromiumPageScroll):
|
class ChromiumFrameScroll(ChromiumPageScroll):
|
||||||
def __init__(self, frame: ChromiumFrame) -> None: ...
|
def __init__(self, frame: ChromiumFrame) -> None: ...
|
||||||
|
|
||||||
|
def to_see(self, loc_or_ele: Union[str, tuple, ChromiumElement], center: bool = False) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
class ChromiumFrameSetter(ChromiumBaseSetter):
|
class ChromiumFrameSetter(ChromiumBaseSetter):
|
||||||
_page: ChromiumFrame = ...
|
_page: ChromiumFrame = ...
|
||||||
|
Loading…
x
Reference in New Issue
Block a user