3.0.31run_script()、run_async_scripy()改为run_js()、run_async_js();坐标信息以int格式返回

This commit is contained in:
g1879 2023-01-08 00:09:04 +08:00
parent 5c55f54dd3
commit 252011f353
16 changed files with 288 additions and 327 deletions

View File

@ -264,6 +264,6 @@ class ActionChains:
def location_to_client(page, lx, ly):
"""绝对坐标转换为视口坐标"""
scrool_x = page.run_script('return document.documentElement.scrollLeft;')
scrool_y = page.run_script('return document.documentElement.scrollTop;')
return lx + scrool_x, ly + scrool_y
scroll_x = page.run_js('return document.documentElement.scrollLeft;')
scroll_y = page.run_js('return document.documentElement.scrollTop;')
return lx + scroll_x, ly + scroll_y

View File

@ -9,7 +9,7 @@ from time import perf_counter, sleep
from requests import Session
from .base import BasePage
from .chromium_element import ChromiumElementWaiter, ChromiumScroll, ChromiumElement, run_script, make_chromium_ele
from .chromium_element import ChromiumElementWaiter, ChromiumScroll, ChromiumElement, run_js, make_chromium_ele
from .common import get_loc
from .config import cookies_to_tuple
from .session_element import make_session_ele
@ -246,14 +246,14 @@ class ChromiumBase(BasePage):
@property
def size(self):
"""返回页面总长高,格式:(长, 高)"""
w = self.run_script('document.body.scrollWidth;', as_expr=True)
h = self.run_script('document.body.scrollHeight;', as_expr=True)
w = self.run_js('document.body.scrollWidth;', as_expr=True)
h = self.run_js('document.body.scrollHeight;', as_expr=True)
return w, h
@property
def active_ele(self):
"""返回当前焦点所在元素"""
return self.run_script('return document.activeElement;')
return self.run_js('return document.activeElement;')
@property
def page_load_strategy(self):
@ -288,16 +288,16 @@ class ChromiumBase(BasePage):
if script is not None:
self.timeouts.script = script
def run_script(self, script, as_expr=False, *args):
def run_js(self, script, as_expr=False, *args):
"""运行javascript代码 \n
:param script: js文本
:param as_expr: 是否作为表达式运行为True时args无效
:param args: 参数按顺序在js文本中对应argument[0]argument[1]...
:return: 运行的结果
"""
return run_script(self, script, as_expr, self.timeouts.script, args)
return run_js(self, script, as_expr, self.timeouts.script, args)
def run_async_script(self, script, as_expr=False, *args):
def run_async_js(self, script, as_expr=False, *args):
"""以异步方式执行js代码 \n
:param script: js文本
:param as_expr: 是否作为表达式运行为True时args无效
@ -305,7 +305,7 @@ class ChromiumBase(BasePage):
:return: None
"""
from threading import Thread
Thread(target=run_script, args=(self, script, as_expr, self.timeouts.script, args)).start()
Thread(target=run_js, args=(self, script, as_expr, self.timeouts.script, args)).start()
def get(self, url, show_errmsg=False, retry=None, interval=None, timeout=None):
"""访问url \n
@ -463,7 +463,7 @@ class ChromiumBase(BasePage):
try:
self._wait_driver.DOM.scrollIntoViewIfNeeded(nodeId=node_id)
except Exception:
self.ele(loc_or_ele).run_script("this.scrollIntoView();")
self.ele(loc_or_ele).run_js("this.scrollIntoView();")
def refresh(self, ignore_cache=False):
"""刷新当前页面 \n
@ -556,7 +556,7 @@ class ChromiumBase(BasePage):
:return: sessionStorage一个或所有项内容
"""
js = f'sessionStorage.getItem("{item}");' if item else 'sessionStorage;'
return self.run_script(js, as_expr=True)
return self.run_js(js, as_expr=True)
def get_local_storage(self, item=None):
"""获取localStorage信息不设置item则获取全部 \n
@ -564,7 +564,7 @@ class ChromiumBase(BasePage):
:return: localStorage一个或所有项内容
"""
js = f'localStorage.getItem("{item}");' if item else 'localStorage;'
return self.run_script(js, as_expr=True)
return self.run_js(js, as_expr=True)
def set_session_storage(self, item, value):
"""设置或删除某项sessionStorage信息 \n
@ -573,7 +573,7 @@ class ChromiumBase(BasePage):
:return: None
"""
js = f'sessionStorage.removeItem("{item}");' if item is False else f'sessionStorage.setItem("{item}","{value}");'
return self.run_script(js, as_expr=True)
return self.run_js(js, as_expr=True)
def set_local_storage(self, item, value):
"""设置或删除某项localStorage信息 \n
@ -582,7 +582,7 @@ class ChromiumBase(BasePage):
:return: None
"""
js = f'localStorage.removeItem("{item}");' if item is False else f'localStorage.setItem("{item}","{value}");'
return self.run_script(js, as_expr=True)
return self.run_js(js, as_expr=True)
def clear_cache(self, session_storage=True, local_storage=True, cache=True, cookies=True):
"""清除缓存,可选要清除的项 \n
@ -593,9 +593,9 @@ class ChromiumBase(BasePage):
:return: None
"""
if session_storage:
self.run_script('sessionStorage.clear();', as_expr=True)
self.run_js('sessionStorage.clear();', as_expr=True)
if local_storage:
self.run_script('localStorage.clear();', as_expr=True)
self.run_js('localStorage.clear();', as_expr=True)
if cache:
self._wait_driver.Network.clearBrowserCache()
if cookies:

View File

@ -93,7 +93,7 @@ class ChromiumBase(BasePage):
def ready_state(self) -> str: ...
@property
def size(self) -> tuple: ...
def size(self) -> Tuple[int, int]: ...
@property
def active_ele(self) -> ChromiumElement: ...
@ -109,9 +109,9 @@ class ChromiumBase(BasePage):
def set_timeouts(self, implicit: float = ..., page_load: float = ..., script: float = ...) -> None: ...
def run_script(self, script: str, as_expr: bool = ..., *args: Any) -> Any: ...
def run_js(self, script: str, as_expr: bool = ..., *args: Any) -> Any: ...
def run_async_script(self, script: str, as_expr: bool = ..., *args: Any) -> None: ...
def run_async_js(self, script: str, as_expr: bool = ..., *args: Any) -> None: ...
def get(self,
url: str,

View File

@ -46,7 +46,7 @@ class ChromiumElement(DrissionElement):
else:
raise RuntimeError('元素可能已失效。')
doc = self.run_script('return this.ownerDocument;')
doc = self.run_js('return this.ownerDocument;')
self._doc_id = doc['objectId'] if doc else None
def __repr__(self):
@ -79,7 +79,7 @@ class ChromiumElement(DrissionElement):
@property
def inner_html(self):
"""返回元素innerHTML文本"""
return self.run_script('return this.innerHTML;')
return self.run_js('return this.innerHTML;')
@property
def attrs(self):
@ -132,13 +132,13 @@ class ChromiumElement(DrissionElement):
def client_location(self):
"""返回元素左上角在视口中的坐标"""
m = self._get_client_rect('border')
return (m[0], m[1]) if m else (0, 0)
return (int(m[0]), int(m[1])) if m else (0, 0)
@property
def client_midpoint(self):
"""返回元素中间点在视口中的坐标"""
m = self._get_client_rect('border')
return (m[0] + (m[2] - m[0]) // 2, m[3] + (m[5] - m[3]) // 2) if m else (0, 0)
return (int(m[0] + (m[2] - m[0]) // 2), int(m[3] + (m[5] - m[3]) // 2)) if m else (0, 0)
@property
def location(self):
@ -156,7 +156,7 @@ class ChromiumElement(DrissionElement):
def _client_click_point(self):
"""返回元素左上角可接受点击的点视口坐标"""
m = self._get_client_rect('padding')
return (self.client_midpoint[0], m[1]) if m else (0, 0)
return (int(self.client_midpoint[0]), int(m[1])) if m else (0, 0)
@property
def _click_point(self):
@ -284,19 +284,19 @@ class ChromiumElement(DrissionElement):
@property
def is_selected(self):
"""返回元素是否被选择"""
return self.run_script('return this.selected;')
return self.run_js('return this.selected;')
@property
def is_displayed(self):
"""返回元素是否显示"""
return not (self.style('visibility') == 'hidden'
or self.run_script('return this.offsetParent === null;')
or self.run_js('return this.offsetParent === null;')
or self.style('display') == 'none')
@property
def is_enabled(self):
"""返回元素是否可用"""
return not self.run_script('return this.disabled;')
return not self.run_js('return this.disabled;')
@property
def is_alive(self):
@ -350,14 +350,14 @@ class ChromiumElement(DrissionElement):
:param value: 属性值
:return: None
"""
self.run_script(f'this.setAttribute(arguments[0], arguments[1]);', False, attr, str(value))
self.run_js(f'this.setAttribute(arguments[0], arguments[1]);', False, attr, str(value))
def remove_attr(self, attr):
"""删除元素attribute属性 \n
:param attr: 属性名
:return: None
"""
self.run_script(f'this.removeAttribute("{attr}");')
self.run_js(f'this.removeAttribute("{attr}");')
def prop(self, prop):
"""获取property属性值 \n
@ -379,7 +379,7 @@ class ChromiumElement(DrissionElement):
:return: None
"""
value = value.replace('"', r'\"')
self.run_script(f'this.{prop}="{value}";')
self.run_js(f'this.{prop}="{value}";')
def set_innerHTML(self, html):
"""设置元素innerHTML \n
@ -388,16 +388,16 @@ class ChromiumElement(DrissionElement):
"""
self.set_prop('innerHTML', html)
def run_script(self, script, as_expr=False, *args):
def run_js(self, script, as_expr=False, *args):
"""运行javascript代码 \n
:param script: js文本
:param as_expr: 是否作为表达式运行为True时args无效
:param args: 参数按顺序在js文本中对应argument[0]argument[1]...
:return: 运行的结果
"""
return run_script(self, script, as_expr, self.page.timeouts.script, args, True)
return run_js(self, script, as_expr, self.page.timeouts.script, args, True)
def run_async_script(self, script, as_expr=False, *args):
def run_async_js(self, script, as_expr=False, *args):
"""以异步方式执行js代码 \n
:param script: js文本
:param as_expr: 是否作为表达式运行为True时args无效
@ -405,7 +405,7 @@ class ChromiumElement(DrissionElement):
:return: None
"""
from threading import Thread
Thread(target=run_script, args=(self, script, as_expr, self.page.timeouts.script, args, True)).start()
Thread(target=run_js, args=(self, script, as_expr, self.page.timeouts.script, args, True)).start()
def ele(self, loc_or_str, timeout=None):
"""返回当前元素下级符合条件的第一个元素、属性或节点文本 \n
@ -459,7 +459,7 @@ class ChromiumElement(DrissionElement):
if pseudo_ele:
pseudo_ele = f', "{pseudo_ele}"' if pseudo_ele.startswith(':') else f', "::{pseudo_ele}"'
js = f'return window.getComputedStyle(this{pseudo_ele}).getPropertyValue("{style}");'
return self.run_script(js)
return self.run_js(js)
def get_src(self):
"""返回元素src资源base64的会转为bytes返回其它返回str"""
@ -472,7 +472,7 @@ class ChromiumElement(DrissionElement):
'&& this.naturalWidth > 0 && typeof this.naturalHeight != "undefined" '
'&& this.naturalHeight > 0')
end_time = perf_counter() + self.page.timeout
while not self.run_script(js) and perf_counter() < end_time:
while not self.run_js(js) and perf_counter() < end_time:
sleep(.1)
node = self.page.run_cdp('DOM.describeNode', nodeId=self._node_id, not_change=True)['node']
@ -519,7 +519,7 @@ class ChromiumElement(DrissionElement):
'&& this.naturalWidth > 0 && typeof this.naturalHeight != "undefined" '
'&& this.naturalHeight > 0')
end_time = perf_counter() + self.page.timeout
while not self.run_script(js) and perf_counter() < end_time:
while not self.run_js(js) and perf_counter() < end_time:
sleep(.1)
left, top = self.location
@ -577,7 +577,7 @@ class ChromiumElement(DrissionElement):
:return: None
"""
if by_js:
self.run_script("this.value='';")
self.run_js("this.value='';")
else:
self.input(('\ue009', 'a', '\ue017'), clear=False)
@ -627,7 +627,7 @@ class ChromiumElement(DrissionElement):
return True
if by_js is not False:
self.run_script('this.click();')
self.run_js('this.click();')
self.page.wait_loading(wait_loading)
return True
@ -801,7 +801,7 @@ class ChromiumElement(DrissionElement):
}
return e(this);}
'''
t = self.run_script(js)
t = self.run_js(js)
return f':root{t}' if mode == 'css' else t
def _get_client_rect(self, quad):
@ -817,9 +817,9 @@ class ChromiumElement(DrissionElement):
def _get_absolute_rect(self, x, y):
"""根据绝对坐标获取窗口坐标"""
js = 'return document.documentElement.scrollLeft+" "+document.documentElement.scrollTop;'
xy = self.run_script(js)
xy = self.run_js(js)
sx, sy = xy.split(' ')
return x + int(float(sx)), y + int(float(sy))
return int(x + float(sx)), int(y + float(sy))
class ChromiumShadowRootElement(BaseElement):
@ -857,7 +857,7 @@ class ChromiumShadowRootElement(BaseElement):
@property
def is_enabled(self):
"""返回元素是否可用"""
return not self.run_script('return this.disabled;')
return not self.run_js('return this.disabled;')
@property
def is_alive(self):
@ -896,26 +896,26 @@ class ChromiumShadowRootElement(BaseElement):
@property
def inner_html(self):
"""返回内部的html文本"""
return self.run_script('return this.innerHTML;')
return self.run_js('return this.innerHTML;')
def run_script(self, script, as_expr=False, *args):
def run_js(self, script, as_expr=False, *args):
"""运行javascript代码 \n
:param script: js文本
:param as_expr: 是否作为表达式运行为True时args无效
:param args: 参数按顺序在js文本中对应argument[0]argument[2]...
:param args: 参数按顺序在js文本中对应argument[0]argument[1]...
:return: 运行的结果
"""
return run_script(self, script, as_expr, self.page.timeouts.script, args)
return run_js(self, script, as_expr, self.page.timeouts.script, args)
def run_async_script(self, script, as_expr=False, *args):
def run_async_js(self, script, as_expr=False, *args):
"""以异步方式执行js代码 \n
:param script: js文本
:param as_expr: 是否作为表达式运行为True时args无效
:param args: 参数按顺序在js文本中对应argument[0]argument[2]...
:param args: 参数按顺序在js文本中对应argument[0]argument[1]...
:return: None
"""
from threading import Thread
Thread(target=run_script, args=(self, script, as_expr, self.page.timeouts.script, args)).start()
Thread(target=run_js, args=(self, script, as_expr, self.page.timeouts.script, args)).start()
def parent(self, level_or_loc=1):
"""返回上面某一级父元素,可指定层数或用查询语法定位 \n
@ -1248,7 +1248,7 @@ else{a.push(e.snapshotItem(i));}}"""
return js
def run_script(page_or_ele, script, as_expr=False, timeout=None, args=None, not_change=False):
def run_js(page_or_ele, script, as_expr=False, timeout=None, args=None, not_change=False):
"""运行javascript代码 \n
:param page_or_ele: 页面对象或元素对象
:param script: js文本
@ -1416,32 +1416,32 @@ class ChromiumScroll(object):
self.t1 = 'window'
self.t2 = 'document.documentElement'
def _run_script(self, js):
def _run_js(self, js):
js = js.format(self.t1, self.t2, self.t2)
if self.t1 == 'this': # 在元素上滚动
self.page_or_ele.run_script(js)
self.page_or_ele.run_js(js)
else:
self.page_or_ele.run_script(js, as_expr=True)
self.page_or_ele.run_js(js, as_expr=True)
def to_top(self):
"""滚动到顶端,水平位置不变"""
self._run_script('{}.scrollTo({}.scrollLeft,0);')
self._run_js('{}.scrollTo({}.scrollLeft,0);')
def to_bottom(self):
"""滚动到底端,水平位置不变"""
self._run_script('{}.scrollTo({}.scrollLeft,{}.scrollHeight);')
self._run_js('{}.scrollTo({}.scrollLeft,{}.scrollHeight);')
def to_half(self):
"""滚动到垂直中间位置,水平位置不变"""
self._run_script('{}.scrollTo({}.scrollLeft,{}.scrollHeight/2);')
self._run_js('{}.scrollTo({}.scrollLeft,{}.scrollHeight/2);')
def to_rightmost(self):
"""滚动到最右边,垂直位置不变"""
self._run_script('{}.scrollTo({}.scrollWidth,{}.scrollTop);')
self._run_js('{}.scrollTo({}.scrollWidth,{}.scrollTop);')
def to_leftmost(self):
"""滚动到最左边,垂直位置不变"""
self._run_script('{}.scrollTo(0,{}.scrollTop);')
self._run_js('{}.scrollTo(0,{}.scrollTop);')
def to_location(self, x, y):
"""滚动到指定位置 \n
@ -1449,7 +1449,7 @@ class ChromiumScroll(object):
:param y: 垂直距离
:return: None
"""
self._run_script(f'{{}}.scrollTo({x},{y});')
self._run_js(f'{{}}.scrollTo({x},{y});')
def up(self, pixel=300):
"""向上滚动若干像素,水平位置不变 \n
@ -1457,14 +1457,14 @@ class ChromiumScroll(object):
:return: None
"""
pixel = -pixel
self._run_script(f'{{}}.scrollBy(0,{pixel});')
self._run_js(f'{{}}.scrollBy(0,{pixel});')
def down(self, pixel=300):
"""向下滚动若干像素,水平位置不变 \n
:param pixel: 滚动的像素
:return: None
"""
self._run_script(f'{{}}.scrollBy(0,{pixel});')
self._run_js(f'{{}}.scrollBy(0,{pixel});')
def left(self, pixel=300):
"""向左滚动若干像素,垂直位置不变 \n
@ -1472,14 +1472,14 @@ class ChromiumScroll(object):
:return: None
"""
pixel = -pixel
self._run_script(f'{{}}.scrollBy({pixel},0);')
self._run_js(f'{{}}.scrollBy({pixel},0);')
def right(self, pixel=300):
"""向右滚动若干像素,垂直位置不变 \n
:param pixel: 滚动的像素
:return: None
"""
self._run_script(f'{{}}.scrollBy({pixel},0);')
self._run_js(f'{{}}.scrollBy({pixel},0);')
class ChromiumSelect(object):
@ -1520,7 +1520,7 @@ class ChromiumSelect(object):
"""返回第一个被选中的option元素 \n
:return: ChromiumElement对象或None
"""
ele = self._ele.run_script('return this.options[this.selectedIndex];')
ele = self._ele.run_js('return this.options[this.selectedIndex];')
return ele
@property
@ -1624,7 +1624,7 @@ class ChromiumSelect(object):
return False
js = 'false' if deselect else 'true'
ele.run_script(f'this.selected={js};')
ele.run_js(f'this.selected={js};')
return True

View File

@ -66,25 +66,25 @@ class ChromiumElement(DrissionElement):
def doc_id(self) -> str: ...
@property
def size(self) -> tuple: ...
def size(self) -> Tuple[int, int]: ...
@property
def client_location(self) -> tuple: ...
def client_location(self) -> Tuple[int, int]: ...
@property
def client_midpoint(self) -> tuple: ...
def client_midpoint(self) -> Tuple[int, int]: ...
@property
def location(self) -> tuple: ...
def location(self) -> Tuple[int, int]: ...
@property
def midpoint(self) -> tuple: ...
def midpoint(self) -> Tuple[int, int]: ...
@property
def _client_click_point(self) -> tuple: ...
def _client_click_point(self) -> Tuple[int, int]: ...
@property
def _click_point(self) -> tuple: ...
def _click_point(self) -> Tuple[int, int]: ...
@property
def shadow_root(self) -> Union[None, ChromiumShadowRootElement]: ...
@ -169,9 +169,9 @@ class ChromiumElement(DrissionElement):
def set_innerHTML(self, html: str) -> None: ...
def run_script(self, script: str, as_expr: bool = ..., *args: Any) -> Any: ...
def run_js(self, script: str, as_expr: bool = ..., *args: Any) -> Any: ...
def run_async_script(self, script: str, as_expr: bool = ..., *args: Any) -> None: ...
def run_async_js(self, script: str, as_expr: bool = ..., *args: Any) -> None: ...
def ele(self,
loc_or_str: Union[Tuple[str, str], str],
@ -236,7 +236,7 @@ class ChromiumElement(DrissionElement):
def _get_client_rect(self, quad: str) -> Union[list, None]: ...
def _get_absolute_rect(self, x: int, y: int) -> tuple: ...
def _get_absolute_rect(self, x: int, y: int) -> Tuple[int, int]: ...
class ChromiumShadowRootElement(BaseElement):
@ -281,9 +281,9 @@ class ChromiumShadowRootElement(BaseElement):
@property
def inner_html(self) -> str: ...
def run_script(self, script: str, as_expr: bool = ..., *args: Any) -> Any: ...
def run_js(self, script: str, as_expr: bool = ..., *args: Any) -> Any: ...
def run_async_script(self, script: str, as_expr: bool = ..., *args: Any) -> None: ...
def run_async_js(self, script: str, as_expr: bool = ..., *args: Any) -> None: ...
def parent(self, level_or_loc: Union[str, int] = ...) -> ChromiumElement: ...
@ -358,8 +358,8 @@ def make_chromium_ele(page: ChromiumBase, node_id: str = ..., obj_id: str = ...)
def _make_js_for_find_ele_by_xpath(xpath: str, type_txt: str, node_txt: str) -> str: ...
def run_script(page_or_ele: Union[ChromiumBase, ChromiumElement, ChromiumShadowRootElement], script: str,
as_expr: bool = ..., timeout: float = ..., args: tuple = ..., not_change: bool = ...) -> Any: ...
def run_js(page_or_ele: Union[ChromiumBase, ChromiumElement, ChromiumShadowRootElement], script: str,
as_expr: bool = ..., timeout: float = ..., args: tuple = ..., not_change: bool = ...) -> Any: ...
def _parse_js_result(page: ChromiumBase, ele: ChromiumElement, result: dict): ...
@ -384,7 +384,7 @@ class ChromiumScroll(object):
self.t2: str = ...
self.page_or_ele: Union[ChromiumPage, ChromiumElement] = ...
def _run_script(self, js: str): ...
def _run_js(self, js: str): ...
def to_top(self) -> None: ...

View File

@ -26,7 +26,7 @@ class ChromiumFrame(ChromiumBase):
else:
self._is_diff_domain = True
super().__init__(page.address, self.frame_id, page.timeout)
obj_id = super().run_script('document;', as_expr=True)['objectId']
obj_id = super().run_js('document;', as_expr=True)['objectId']
self.doc_ele = ChromiumElement(self, obj_id=obj_id)
def __call__(self, loc_or_str, timeout=None):
@ -55,7 +55,7 @@ class ChromiumFrame(ChromiumBase):
self._is_diff_domain = True
self._tab_obj.stop()
super().__init__(self.address, self.frame_id, self.page.timeout)
obj_id = super().run_script('document;', as_expr=True)['objectId']
obj_id = super().run_js('document;', as_expr=True)['objectId']
self.doc_ele = ChromiumElement(self, obj_id=obj_id)
def _check_ok(self):
@ -148,7 +148,7 @@ class ChromiumFrame(ChromiumBase):
def url(self):
"""返回frame当前访问的url"""
self._check_ok()
return self.doc_ele.run_script('return this.location.href;')
return self.doc_ele.run_js('return this.location.href;')
@property
def html(self):
@ -164,7 +164,7 @@ class ChromiumFrame(ChromiumBase):
def inner_html(self):
"""返回元素innerHTML文本"""
self._check_ok()
return self.doc_ele.run_script('return this.documentElement.outerHTML;')
return self.doc_ele.run_js('return this.documentElement.outerHTML;')
@property
def title(self):
@ -176,7 +176,7 @@ class ChromiumFrame(ChromiumBase):
def cookies(self):
"""以dict格式返回cookies"""
self._check_ok()
return super().cookies if self._is_diff_domain else self.doc_ele.run_script('return this.cookie;')
return super().cookies if self._is_diff_domain else self.doc_ele.run_js('return this.cookie;')
@property
def attrs(self):
@ -188,8 +188,8 @@ class ChromiumFrame(ChromiumBase):
def frame_size(self):
"""返回frame内页面尺寸格式(长, 高)"""
self._check_ok()
w = self.doc_ele.run_script('return this.body.scrollWidth')
h = self.doc_ele.run_script('return this.body.scrollHeight')
w = self.doc_ele.run_js('return this.body.scrollWidth')
h = self.doc_ele.run_js('return this.body.scrollHeight')
return w, h
@property
@ -202,7 +202,7 @@ class ChromiumFrame(ChromiumBase):
def active_ele(self):
"""返回当前焦点所在元素"""
self._check_ok()
return self.doc_ele.run_script('return this.activeElement;')
return self.doc_ele.run_js('return this.activeElement;')
@property
def location(self):
@ -240,14 +240,14 @@ class ChromiumFrame(ChromiumBase):
else:
while True:
try:
return self.doc_ele.run_script('return this.readyState;')
return self.doc_ele.run_js('return this.readyState;')
except:
pass
def refresh(self):
"""刷新frame页面"""
self._check_ok()
self.doc_ele.run_script('this.location.reload();')
self.doc_ele.run_js('this.location.reload();')
def attr(self, attr):
"""返回frame元素attribute属性值 \n
@ -274,7 +274,7 @@ class ChromiumFrame(ChromiumBase):
self._check_ok()
self.frame_ele.remove_attr(attr)
def run_script(self, script, as_expr=False, *args):
def run_js(self, script, as_expr=False, *args):
"""运行javascript代码 \n
:param script: js文本
:param as_expr: 是否作为表达式运行为True时args无效
@ -282,7 +282,7 @@ class ChromiumFrame(ChromiumBase):
:return: 运行的结果
"""
self._check_ok()
return self.doc_ele.run_script(script, as_expr=as_expr, *args)
return self.doc_ele.run_js(script, as_expr=as_expr, *args)
def parent(self, level_or_loc=1):
"""返回上面某一级父元素,可指定层数或用查询语法定位 \n

View File

@ -75,16 +75,16 @@ class ChromiumFrame(ChromiumBase):
def attrs(self) -> dict: ...
@property
def frame_size(self) -> tuple: ...
def frame_size(self) -> Tuple[int, int]: ...
@property
def size(self) -> tuple: ...
def size(self) -> Tuple[int, int]: ...
@property
def active_ele(self) -> ChromiumElement: ...
@property
def location(self) -> dict: ...
def location(self) -> Tuple[int, int]: ...
@property
def is_displayed(self) -> bool: ...
@ -103,7 +103,7 @@ class ChromiumFrame(ChromiumBase):
def remove_attr(self, attr: str) -> None: ...
def run_script(self, script: str, as_expr: bool = ..., *args: Any) -> Any: ...
def run_js(self, script: str, as_expr: bool = ..., *args: Any) -> Any: ...
def parent(self, level_or_loc: Union[tuple, str, int] = ...) -> Union[ChromiumElement, None]: ...

View File

@ -672,4 +672,4 @@ def _location_in_viewport(page, loc_x: int, loc_y: int) -> bool:
if (x< document.documentElement.scrollLeft || y < document.documentElement.scrollTop
|| x > vWidth || y > vHeight){{return false;}}
return true;}}'''
return page.run_script(js)
return page.run_js(js)

View File

@ -1,28 +1,37 @@
# ✨️简洁!易用 !方便!✨️
DrissionPage 是一个基于 python 的网页自动化工具。
DrissionPage即 driver 和 session 组合而成的 page。
是个基于 python 的 Web 自动化操作集成工具。
它用 POM 模式封装了页面和元素常用的方法,
自带一套简洁直观优雅的元素定位语法,
实现了浏览器和 requests 之间的无缝切换,
可兼顾浏览器自动化的便利性和 requests 的高效率,
更棒的是,它的使用方式非常简洁和人性化,代码量少,对新手友好。
它既能控制浏览器,也能收发数据包,甚至能把两者合而为一,
可兼顾浏览器自动化的便利性和 requests 的高效率。
它功能强大,内置无数人性化设计和便捷功能。
它的语法简洁而优雅,代码量少,对新手友好。
**交流QQ群** 897838127
**联系邮箱:** g1879@qq.com
## 📕 背景
# 📕 背景
用 requests 做数据采集面对要登录的网站时要分析数据包、JS 源码构造复杂的请求往往还要应付验证码、JS 混淆、签名参数等反爬手段,门槛较高。若数据是由 JS 计算生成的,还须重现计算过程,体验不好,开发效率不高。
使用浏览器,可以很大程度上绕过这些坑,但浏览器运行效率不高。因此,这个库将它们合而为一,不同须要时切换相应模式,并提供一种人性化的使用方法,提高开发和运行效率。
用 requests 做数据采集面对要登录的网站时要分析数据包、JS 源码构造复杂的请求往往还要应付验证码、JS 混淆、签名参数等反爬手段,门槛较高。若数据是由 JS 计算生成的,还须重现计算过程,体验不好,开发效率不高。
使用浏览器,可以很大程度上绕过这些坑,但浏览器运行效率不高。
因此,这个库设计初衷,是将它们合而为一,能够在不同须要时切换相应模式,并提供一种人性化的使用方法,提高开发和运行效率。
除了合并两者,本库还以网页为单位封装了常用功能,提供非常简便的操作和语句,在用于网页自动化操作时,减少考虑细节,专注功能实现,使用更方便。
一切从简,尽量提供简单直接的使用方法,对新手更友好。
# 🔆 3.0 版隆重推出
# ✨️ 理念
以前的版本是对 selenium 进行重新封装实现的。从 3.0 开始,作者另起炉灶,对底层进行了重新开发,摆脱对 selenium 的依赖,增强了功能,提升了运行效率。
3.0 全新开发的页面对象是`WebPage`,支持 chromium 内核的浏览器(如 chrome 和 edge。除了保持之前的功能比依赖 selenium 的`MixPage`有以下优点:
简洁!易用 !方便!
# 💡 特性和亮点
作者经过长期实践,踩过无数坑,总结出的经验全写到这个库里了。
## 🎇 强大的自研内核
本库采用全自研的内核,内置了 N 多实用功能,对常用功能作了整合和优化,对比 selenium有以下优点
- 无 webdriver 特征,不会被网站识别
@ -40,52 +49,20 @@ DrissionPage即 driver 和 session 组合而成的 page。
- 可以对整个网页截图包括视口外的部分90以上版本浏览器支持
- 对 Linux 提供良好支持
新版是自己实现的功能,开发不会受太多限制,以后将主要对`WebPage`进行更新。
3.0 版已经发布,目前正在测试,欢迎试用并提出意见,让我做得更好。
# 💡 特性和亮点
作者踩过无数坑,总结出的经验全写到这个库里了。内置了 N 多实用功能,对常用功能作了整合和优化。
## 🎉 特性
- 代码高度集成,以简洁的代码为第一追求。
- 页面对象可在浏览器和 requests 模式间任意切换,保留登录状态。
- 极简单但强大的元素定位语法,支持链式操作,代码极其简洁。
- 两种模式提供一致的 API使用体验一致。
- 人性化设计,集成众多实用功能,大大降低开发工作量。
## 🎇 亮点功能
- 每次运行程序可以反复使用已经打开的浏览器。如手动设置网页到某个状态,再用程序接管,或手动处理登录,再用程序爬内容。无须每次运行从头启动浏览器,超级方便。
- 使用 ini 文件保存常用配置,自动调用,也提供便捷的设置 API远离繁杂的配置项。
- 极致简明的定位语法,支持直接按文本定位元素,支持直接获取前后兄弟元素和父元素等。
- 强大的下载工具,操作浏览器时也能享受快捷可靠的下载功能。
- 下载工具支持多种方式处理文件名冲突、自动创建目标路径、断链重试等。
- 访问网址带自动重试功能,可设置间隔和超时时间。
- 访问网页能自动识别编码,无须手动设置。
- 链接参数默认自动生成`Host``Referer`属性。
- 可随时直接隐藏或显示浏览器进程窗口,非 headless 或最小化。
- 可自动下载合适版本的 chromedriver免去麻烦的配置。
- d 模式查找元素内置等待,可任意设置全局等待时间或单次查找等待时间。
- 点击元素集成 js 点击方式,一个参数即可切换点击方式。
- 点击支持失败重试,可用于保证点击成功、判读网页遮罩层是否消失等。
- 输入文本能自动判断是否成功并重试,避免某些情况下输入或清空失效的情况。
- d 模式下支持全功能的 xpath可直接获取元素的某个属性selenium 原生无此功能。
- 支持直接获取`shadow-root`,和普通元素一样操作其下的元素。
- 支持直接获取`after``before`伪元素的内容。
- 可以在元素下直接使用 > 以 css selector 方式获取当前元素直接子元素。原生不支持这种写法。
- 可简单地使用 lxml 来解析 d 模式的页面或元素,爬取复杂页面数据时速度大幅提高。
- 输出的数据均已转码及处理基本排版,减少重复劳动。
- 可方便地与 selenium 或 requests 原生代码对接,便于项目迁移。
- 使用 POM 模式封装,可直接用于测试,便于扩展。
- d 模式配置可同时兼容`debugger_address`和其它参数,原生不能兼容。
- 还有很多这里不一一列举…………
除了以上优点,本库还内置了无数人性化设计。
- 极简的语法规则。集成大量常用功能,代码更优雅
- 定位元素更加容易,功能更强大稳定
- 无处不在的等待和自动重试功能。使不稳定的网络变得易于控制,程序更稳定,编写更省心
- 提供强大的下载工具。操作浏览器时也能享受快捷可靠的下载功能
- 允许反复使用已经打开的浏览器。无须每次运行从头启动浏览器,调试超方便
- 使用 ini 文件保存常用配置,自动调用,提供便捷的设置,远离繁杂的配置项
- 内置 lxml 作为解析引擎,解析速度成几个数量级提升
- 使用 POM 模式封装,可直接用于测试,便于扩展
- 高度集成的便利功能,从每个细节中体现
- 还有很多细节,这里不一一列举,欢迎实际使用中体验:)
# 🖐🏻 免责声明

View File

@ -40,7 +40,7 @@ ele.click(by_js=True) # 无视遮罩层,直接用 js 点击下方元素
## 📍 `click_at()`
此方法用于带偏移量点击元素,偏移量相对于元素左上角坐标。不传入`offset_x``offset_y`值时点击元素左上角可接受点击的点。
此方法用于带偏移量点击元素,偏移量相对于元素左上角坐标。不传入`offset_x``offset_y`值时点击元素中上部可接受点击的点。
可用于点击一些奇怪的东西,比如用伪元素表示的控件。
点击的目标不一定在元素上,可以传入负值,或大于元素大小的值,点击元素附近的区域。向右和向下为正值,向左和向上为负值。
@ -48,7 +48,7 @@ ele.click(by_js=True) # 无视遮罩层,直接用 js 点击下方元素
- `offset_x`:相对元素左上角坐标的 x 轴偏移量
- `offset_y`:相对元素左上角坐标的 y 轴偏移量
- `button`:点击哪个键,传入`'left'``right`
- `button`:点击哪个键,传入`'left'``'right'``'middle'`
**返回:**`None`
@ -161,9 +161,11 @@ from DrissionPage.keys import Keys
ele.input((Keys.CTRL, 'a', Keys.DEL)) # ctrl+a+del
```
## 📍 上传文件控件
## 📍 上传文件
上传文件也是用`input()`输入,用法与输入文本一致,稍有不同的是无论`clear`是什么,都会清空原控件内容。
上传文件是用`input()``type`属性为`'file'``<input>`元素进行输入,把文件路径输入到元素即可,用法与输入文本一致。
稍有不同的是,无论`clear`参数是什么,都会清空原控件内容。
多文件上传控件,多个路径以`list``tuple`或以`\n`分隔的字符串传入。
@ -304,7 +306,7 @@ ele.remove_attr('href')
# ✔️ 执行 js 脚本
## 📍 `run_script()`
## 📍 `run_js()`
此方法用于对元素执行 js 代码,代码中用`this`表示元素自己。
@ -320,10 +322,10 @@ ele.remove_attr('href')
```python
# 用执行 js 的方式点击元素
ele.run_script('this.click();')
ele.run_js('this.click();')
# 用 js 获取元素高度
height = ele.run_script('return this.offsetHeight;')
height = ele.run_js('return this.offsetHeight;')
```
## 📍 `run_async_script()`

View File

@ -164,7 +164,7 @@ print('登录后title', page.title)
# ✔️ 执行脚本或命令
## 📍 `run_script()`
## 📍 `run_js()`
此方法用于执行 js 脚本d 模式独有。
@ -178,7 +178,7 @@ print('登录后title', page.title)
```python
# 用传入参数的方式执行 js 脚本显示弹出框显示 Hello world!
page.run_script('alert(arguments[0]+arguments[1]);', 'Hello', ' world!')
page.run_js('alert(arguments[0]+arguments[1]);', 'Hello', ' world!')
```
## 📍 `run_async_script()`

View File

@ -243,7 +243,7 @@ iframe.refresh()
此属性以`tuple`形式返回页面大小。
## 📍 `run_script()`
## 📍 `run_js()`
此方法用于在`<iframe>`内执行 js 脚本。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

View File

@ -1,5 +1,6 @@
本节讲解 DrissionPage 的一些基本概念。了解它大概的构成和运作原理。
默认读者对网络协议、html、网页自动化、requests 等有基本认识。
本节讲解 DrissionPage 的一些基本概念。了解它大概的构成。
如果您觉得有点懵,可直接跳过本节,从下一节直接上手代码。
# ✔️ 网页自动化
@ -17,9 +18,136 @@
在旧版本,本库是通过对 selenium 和 requests 的重新封装实现的。
从 3.0 版开始,作者另起炉灶,用 chromium 协议自行实现了 selenium 全部功能,从而摆脱了对 selenium 的依赖,功能更多更强,运行效率更高,开发更灵活。
本节只介绍新版的内容,如果您想了解旧版,请查阅“`MixPage`使用方法”章节。
# ✔️ 主要对象
无论是控制浏览器,还是收发数据包,其操作逻辑是一致的,即先创建页面对象,通过页面对象控制浏览器或收发数据包,再从浏览器或返回的数据中获取元素对象,从而实现数据的获取或页面的控制。因此,最主要的对象就是两种:页面对象,及其生成的元素对象。
## 📍 `ChromiumPage`
`ChromiumPage`是用于操作浏览器的页面对象,它仅用于操作浏览器,而不能收发数据包。支持 chroium 内核浏览器,如 chrome、edge 等。创建页面对象时,程序会自动启动浏览器,如果指定端口已存在浏览器,就接管该浏览器。
!>**注意:**<br>尝试以下代码前,请先关闭已经打开的 Chrome 浏览器。<br>如果启动失败,请查看“快速上手”一节配置浏览器路径。
```python
from DrissionPage import ChromiumPage
# 创建页面对象
page = ChromiumPage()
# 控制浏览器访问百度
page.get('https://www.baidu.com') # 使用页面对象访问
# 定位输入框并输入关键字
page.ele('#kw').input('DrissionPage')
# 点击“百度一下”按钮
page.ele('@value=百度一下').click()
```
## 📍 `ChromiumElement`
`ChromiumElemnet`对象是浏览器页面元素对象,可对浏览器中的元素进行点击、文本输入、拖拽、运行 js 脚本等操作,也可以基于这个元素查找其下级或周围的元素。
```python
# 获取id为'kw'的元素对象
ele = page('#kw')
# 点击元素
ele.click()
# 输入文本
ele.input('some text')
# 获取 class 属性
attr = ele.attr('class')
# 设置 style 属性
ele.set_attr('style', 'display:none;')
# 获取其子元素中所有 a 元素
links = ele.eles('tag:a')
```
除了最常用的`ChromiumElement`对象,浏览器还会产生`ChromiumFrame``ChromiumShadowRootElement`等对象,详细使用方法见“使用方法”中相关章节。
## 📍 `SessionPage`
`SessionPage`是用于收发数据包的页面对象,它仅用于收发数据包,而不能操作浏览器。
```python
from DrissionPage import SessionPage
# 创建页面对象
page = SessionPage()
# 访问百度
page.get('https://www.baidu.com')
# 获取元素对象
ele = page('#kw')
# 打印元素html
print(ele.html)
```
输出:
```console
<input id="kw" name="wd" class="s_ipt" value="" maxlength="255" autocomplete="off">
```
## 📍 `SessionElement`
`SessionElement`对象是`SessionPage`产生的元素对象,可以读取元素信息,或基于它进行下级元素查找、相对定位其它元素,但不能执行点击等操作。
这种对象解析效率非常高,当浏览器页面太复杂时,可把`ChromiumElement`元素转换为`SessionElement`进行解析,提高速度。转换的同时可以执行下级元素的查找。
```python
# 获取元素 tag 属性
tag = ele.tag
# 在元素下查找第一个 name 为 name1 的子元素
ele1 = ele.ele('@name=name1')
```
## 📍 `WebPage`
`WebPage`是整合了上面两者的页面对象,既可控制浏览器,又可收发数据包,并且可以在这两者之间共享登录信息。
它有两种工作模式d 模式和 s 模式。d 模式用于控制浏览器s 模式用于收发数据包。`WebPage`可在两种模式间切换,但同一时间只能处于其中一种模式。
在 d 模式时,`WebPage`获取的元素是`ChromiumElement`,在 s 模式时,获取到的元素是`SessionElement`
```python
from DrissionPage import WebPage
# 创建页面对象初始d模式
page = WebPage('d')
# 访问百度
page.get('http://www.baidu.com')
# 定位输入框并输入关键字
page.ele('#kw').input('DrissionPage')
# 点击“百度一下”按钮
page.ele('@value=百度一下').click(wait_loading=True)
# 切换到s模式
page.change_mode()
# 获取所有结果元素
results = page.eles('tag:h3')
# 遍历所有结果
for result in results:
# 打印结果文本
print(result.text)
```
输出:
```console
DrissionPage: 一个整合了selenium和requests_html的模块,...
g1879/DrissionPage- GitHub
python采集库DrissionPage- 腾讯云开发者社区-腾讯云
DrissionPage- Web应用开发 - 青少年人工智能资源与创新...
DrissionPage-demos: 使用DrissionPage爬取常见网站的示例。
DrissionPagev1.9.0 已经发布,WEB 自动化测试集成工具_程...
DrissionPage首页、文档和下载 - WEB 自动化测试集成工具 ...
DrissionPage—— Web 自动化测试集成工具 - OSCHINA - ...
DrissionPagev1.10.0 已经发布,WEB 自动化测试集成工具 |...
DrissionPagev2.2.1 发布,WEB 自动化测试集成工具
```
详细使用方法见“创建页面对象”和“操作页面”章节。
# ✔️ 工作模式
如上所述DrissionPage 既可控制浏览器也可用数据包方式访问网络数据。它有两种工作方式d 模式和 s 模式。
如上所述,`WebPage`既可控制浏览器也可用数据包方式访问网络数据。它有两种工作方式d 模式和 s 模式。
页面对象可以在这两种模式间切换,两种模式拥有一致的使用方法,但任一时间只能处于其中一种模式。
## 📍 d 模式
@ -36,7 +164,7 @@ s 模式的运行速度比 d 模式快几个数量级,但只能基于数据包
## 📍 模式切换
页面对象可以在 d 模式和 s 模式之间切换,这通常用于以下情况:
`WebPage`对象可以在 d 模式和 s 模式之间切换,这通常用于以下情况:
- 当登录验证很严格,难以解构,如有验证码的时候,用浏览器处理登录,然后转换成 s 模式爬取数据。既避免了处理烧脑的 js又能享受 s 模式的速度。
- 页面数据由 js 产生,且页面结构极其复杂,可以用 d 模式读取页面元素,然后把元素转成 s 模式的元素进行分析。可以极大地提高 d 模式的处理速度。
@ -58,136 +186,12 @@ ele = ele.s_ele()
rows = ele.eles('tag:tr')
```
# ✔️ 主要对象
## 📍 `WebPage`
是从 3.0 版开始推出的页面页面控制对象整合了两种模式d 模式所有页面的跳转、读取、操作、标签页控s 模式发送和解析数据都由该对象进行。
该对象是作者用 chromium devtools protocol 开发而成,不依赖 selenium 对浏览器进行控制。
```python
# 创建页面对象
page = WebPage('d')
# 访问百度
page.get('http://www.baidu.com')
# 定位输入框并输入关键字
page.ele('#kw').input('python')
# 点击“百度一下”按钮
page.ele('@value=百度一下').click()
```
详细使用方法见“创建页面对象”和“操作页面”章节。
## 📍 `ChromiumElement`
`ChromiumElemnet`对象是 `WebPage`在 d 模式所产生的页面元素对象,用于可对其进行点击、文本输入、拖拽、运行 js 脚本等操作,也可以基于这个元素查找其下级或周围的元素。
```python
# 点击一个元素
ele1.click()
# 输入文本
ele1.input('some text')
# 获取 class 属性
attr = ele1.attr('class')
# 设置 style 属性
ele1.set_attr('style', 'display:none;')
# 获取其子元素中所有 a 元素
links = ele1.eles('tag:a')
```
详细使用方法见“获取页面元素”、“获取元素信息”和“操作页面元素”章节。
## 📍 `SessionElement`
`SessionElement`对象是 s 模式所产生的页面元素对象,可以读取元素信息,或基于它进行下级元素查找、相对定位其它元素,但不能执行点击等操作。
这种对象解析效率非常高,当 d 模式页面太复杂时,可把 d 模式的元素转换为`SessionElement`进行解析,提高速度。转换的同时可以执行下级元素的查找。
```python
# 获取元素 tag 属性
tag = ele1.tag
# 在元素下查找第一个 name 为 name1 的子元素
ele2 = ele1.ele('@name=name1')
# 假设 d_ele 是一个 ChromiumElement使用其 SessionElement 版本进行子元素查找
ele2 = d_ele.s_ele('@name=name1')
```
## 📍 `MixPage`
`MixPage`对象使用方法与`WebPage`大致相同,不同的是它控制浏览器方面的功能是通过封装 selenium 实现的。作者以后主要对`WebPage`进行更新,`MixPage`会一直维持当前状态。
```python
# 创建页面对象
page = MixPage('d')
# 访问百度
page.get('http://www.baidu.com')
# 定位输入框并输入关键字
page.ele('#kw').input('python')
# 点击“百度一下”按钮
page.ele('@value=百度一下').click()
```
详细使用方法见“创建页面对象”和“操作页面”章节。
## 📍 `DriverElement`
`DriverElemnet`对象与`ChromiumElement`大致相同。区别是它是由`MiPage`所产生。
```python
# 点击一个元素
ele1.click()
# 输入文本
ele1.input('some text')
# 获取 class 属性
attr = ele1.attr('class')
# 设置 style 属性
ele1.set_attr('style', 'display:none;')
# 获取其子元素中所有 a 元素
links = ele1.eles('tag:a')
```
详细使用方法见“获取页面元素”、“获取元素信息”和“操作页面元素”章节。
## 📍 `Drission`
`Drission`对象用于管理`MixPage`中与网页通讯的`WebDriver`对象和`Session`对象,相当于驱动器的角色。能实现这两个对象间的登录状态传递等。但它提供的功能往往通过`MixPage`
调用,所以出场几率并不高。
`WebPage`自行实现了这些功能,所以`WebPage`不需要`Drission`对象。
```python
from DrissionPage import Drission
# 创建 Drission 对象
d = Drission()
# 通过创建 MixPage 对象
page = MixPage(drission=d)
```
实际上,如果创建时不传入`Drission`对象,`MixPage`会自动根据配置信息创建一个。
```python
from DrissionPage import MixPage
# 直接创建 MixPage 对象,隐性创建 Drission 对象
page = MixPage()
```
# ✔️ 结构图
## 📍 `WebPage`结构图
`WebPage`继承自`ChromiumPage``SessionPage`,前者负责控制浏览器,后者负责数据包收发。
`WebPage`继承自`ChromiumPage``SessionPage`,前者负责控制浏览器,后者负责数据包收发,因此`WebPage`既能控制浏览器,也能收发数据包,并且能在两种模式中共享登录状态。
![](https://gitee.com/g1879/DrissionPage/raw/master/docs/imgs/webpage.jpg)
## 📍 `MixPage`结构图
`Drission`对象负责链接的创建、共享登录状态等工作,类似 selenium 中 driver 的概念。
`MixPage`对象负责对获取到的页面进行解析、操作。
`DriverElement``SessionElement`则是从页面对象中获取到的元素对象。负责对元素进行解析和操作。
![](https://gitee.com/g1879/DrissionPage/raw/master/docs/imgs/mixpage.jpg)
# ✔️ 配置管理
无论 requests 还是浏览器,都通常须要一些配置信息才能正常工作,如长长的`user_agent`、driver 路径、浏览器配置等。这些代码往往是繁琐而重复的,不利于代码的简洁。
@ -195,7 +199,7 @@ page = MixPage()
这个功能支持用户保存不同的配置文件,按情况调研,也可以支持直接把配置写在代码里面,屏蔽读取配置文件。
?> **Tips** <br>当须要打包程序时,必须把配置写到代码里,或打包后手动复制配置文件到运行路径,否则会报错。
?> **Tips** <br>当须要打包程序时,必须把配置写到代码里,或打包后手动复制配置文件到运行路径,否则会报错。详见“”章节。
# ✔️ 定位符

View File

@ -1,71 +1,53 @@
# v3.0.31
- `run_script()``run_async_script()`更名为`run_js``run_async_js()`
- 返回的坐标数据全部转为`int`类型组成的`tuple`
- 修改注释
# v3.0.30
- 元素增加`m_click()`方法
- 动作链增加`type()``m_click()``r_hold()``r_release()``m_hold()``m_release()`方法
- 动作链的`on_ele`参数可接收文本定位符
- `WebPage``SessionPage``ChromiumPage`增加`set_headers()`方法
# v3.0.28
- 各种大小、位置信息从`dict`改为用`tuple`返回
- 改进`ChromiumFrame`
- 修复小窗时定位不准问题,修复 iframe 内元素无法获取 s_ele() 问题
- 增加`wait_loading`方法和参数
- 其它优化和问题修复
# v3.0.22
- `change_mode()`增加`copy_cookies`参数
- 调整`WebPage`生成的元素对象的`prev()``next()``before()``after()`参数顺序
- 修复读取页面时小概率失效问题
- 用存根文件取代类型注解
# v3.0.20
重大更新。推出`WebPage`,重新开发底层逻辑,摆脱对 selenium 的依赖,增强了功能,提升了运行效率。支持 chromium 内核的浏览器(如 chrome 和 edge。比`MixPage`有以下优点:
- 无 webdriver 特征,不会被网站识别
- 无需为不同版本的浏览器下载不同的驱动
- 运行速度更快
- 可以跨 iframe 查找元素,无需切入切出
- 把 iframe 看作普通元素,获取后可直接在其中查找元素,逻辑更清晰
- 可以同时操作浏览器中的多个标签页,即使标签页为非激活状态
- 可以直接读取浏览器缓存来保持图片,无需用 GUI 点击保存
- 可以对整个网页截图包括视口外的部分90以上版本浏览器支持
其它更新:
- 增加`ChromiumTab``ChromiumFrame`类用于处理 tab 和 frame 元素
- 新增与`WebPage`配合的动作链接`ActionChains`
- ini 文件和`DriverOption`删除`set_window_rect`属性
- 浏览器启动配置实现对插件的支持
- 浏览器启动配置实现对`experimental_options``prefs`属性支持
# v2.7.3
- 页面对象和元素对象的`screenshot_as_bytes()`方法合并到`screenshot()`
- `input()`方法接收非文本参数时自动转成文本输入
# v2.7.2
@ -75,13 +57,9 @@
# v2.7.1
- DriverPage
- 增加`get_session_storage()``get_local_storage()``set_session_storage()``set_local_storage()``clean_cache()`方法
- `run_cdp()``cmd_args`参数改为`**cmd_args`
- 关闭 driver 时会主动关闭 chromedriver.exe 的进程
- 优化关闭浏览器进程逻辑
# v2.6.2

View File

@ -6,7 +6,7 @@ with open("README.md", "r", encoding='utf-8') as fh:
setup(
name="DrissionPage",
version="3.0.30",
version="3.0.31",
author="g1879",
author_email="g1879@qq.com",
description="A module that integrates selenium and requests session, encapsulates common page operations.",