mirror of
https://gitee.com/g1879/DrissionPage.git
synced 2024-12-01 19:47:35 +08:00
3.0.0
This commit is contained in:
parent
c4977a1d8c
commit
3be47a80ca
@ -36,6 +36,8 @@ class ActionChains:
|
||||
ele_loc = ele_or_loc.location if offset_x or offset_y else ele_or_loc.midpoint
|
||||
lx = ele_loc['x'] + offset_x
|
||||
ly = ele_loc['y'] + offset_y
|
||||
else:
|
||||
raise TypeError('ele_or_loc参数只能接受坐标(x, y)或ChromiumElement对象。')
|
||||
|
||||
if not _location_in_viewport(self.page, lx, ly):
|
||||
self.page.scroll.to_location(lx, ly)
|
||||
|
@ -17,7 +17,7 @@ from .base import DrissionElement, BaseElement
|
||||
from .common import make_absolute_link, get_loc, get_ele_txt, format_html, is_js_func, _location_in_viewport
|
||||
|
||||
|
||||
class ChromeElement(DrissionElement):
|
||||
class ChromiumElement(DrissionElement):
|
||||
"""ChromePage页面对象中的元素对象"""
|
||||
|
||||
def __init__(self, page, node_id: str = None, obj_id: str = None):
|
||||
@ -46,7 +46,7 @@ class ChromeElement(DrissionElement):
|
||||
|
||||
def __call__(self,
|
||||
loc_or_str: Union[Tuple[str, str], str],
|
||||
timeout: float = None) -> Union['ChromeElement', str, None]:
|
||||
timeout: float = None) -> Union['ChromiumElement', str, None]:
|
||||
"""在内部查找元素 \n
|
||||
例:ele2 = ele1('@id=ele_id') \n
|
||||
:param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
|
||||
@ -98,7 +98,7 @@ class ChromeElement(DrissionElement):
|
||||
"""返回未格式化处理的元素内文本"""
|
||||
return self.prop('innerText')
|
||||
|
||||
# -----------------driver独有属性-------------------
|
||||
# -----------------d模式独有属性-------------------
|
||||
@property
|
||||
def obj_id(self) -> str:
|
||||
"""返回js中的object id"""
|
||||
@ -193,7 +193,7 @@ class ChromeElement(DrissionElement):
|
||||
self._scroll = ChromeScroll(self)
|
||||
return self._scroll
|
||||
|
||||
def parent(self, level_or_loc: Union[tuple, str, int] = 1) -> Union['ChromeElement', None]:
|
||||
def parent(self, level_or_loc: Union[tuple, str, int] = 1) -> Union['ChromiumElement', None]:
|
||||
"""返回上面某一级父元素,可指定层数或用查询语法定位 \n
|
||||
:param level_or_loc: 第几级父元素,或定位符
|
||||
:return: 上级元素对象
|
||||
@ -203,7 +203,7 @@ class ChromeElement(DrissionElement):
|
||||
def prev(self,
|
||||
index: int = 1,
|
||||
filter_loc: Union[tuple, str] = '',
|
||||
timeout: float = 0) -> Union['ChromeElement', str, None]:
|
||||
timeout: float = 0) -> Union['ChromiumElement', str, None]:
|
||||
"""返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n
|
||||
:param index: 前面第几个查询结果元素
|
||||
:param filter_loc: 用于筛选元素的查询语法
|
||||
@ -215,7 +215,7 @@ class ChromeElement(DrissionElement):
|
||||
def next(self,
|
||||
index: int = 1,
|
||||
filter_loc: Union[tuple, str] = '',
|
||||
timeout: float = 0) -> Union['ChromeElement', str, None]:
|
||||
timeout: float = 0) -> Union['ChromiumElement', str, None]:
|
||||
"""返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n
|
||||
:param index: 后面第几个查询结果元素
|
||||
:param filter_loc: 用于筛选元素的查询语法
|
||||
@ -227,7 +227,7 @@ class ChromeElement(DrissionElement):
|
||||
def before(self,
|
||||
index: int = 1,
|
||||
filter_loc: Union[tuple, str] = '',
|
||||
timeout: float = None) -> Union['ChromeElement', str, None]:
|
||||
timeout: float = None) -> Union['ChromiumElement', str, None]:
|
||||
"""返回当前元素前面的一个元素,可指定筛选条件和第几个。查找范围不限兄弟元素,而是整个DOM文档 \n
|
||||
:param index: 前面第几个查询结果元素
|
||||
:param filter_loc: 用于筛选元素的查询语法
|
||||
@ -239,7 +239,7 @@ class ChromeElement(DrissionElement):
|
||||
def after(self,
|
||||
index: int = 1,
|
||||
filter_loc: Union[tuple, str] = '',
|
||||
timeout: float = None) -> Union['ChromeElement', str, None]:
|
||||
timeout: float = None) -> Union['ChromiumElement', str, None]:
|
||||
"""返回当前元素后面的一个元素,可指定筛选条件和第几个。查找范围不限兄弟元素,而是整个DOM文档 \n
|
||||
:param index: 后面第几个查询结果元素
|
||||
:param filter_loc: 用于筛选元素的查询语法
|
||||
@ -250,7 +250,7 @@ class ChromeElement(DrissionElement):
|
||||
|
||||
def prevs(self,
|
||||
filter_loc: Union[tuple, str] = '',
|
||||
timeout: float = 0) -> List[Union['ChromeElement', str]]:
|
||||
timeout: float = 0) -> List[Union['ChromiumElement', str]]:
|
||||
"""返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n
|
||||
:param filter_loc: 用于筛选元素的查询语法
|
||||
:param timeout: 查找元素的超时时间
|
||||
@ -260,7 +260,7 @@ class ChromeElement(DrissionElement):
|
||||
|
||||
def nexts(self,
|
||||
filter_loc: Union[tuple, str] = '',
|
||||
timeout: float = 0) -> List[Union['ChromeElement', str]]:
|
||||
timeout: float = 0) -> List[Union['ChromiumElement', str]]:
|
||||
"""返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n
|
||||
:param filter_loc: 用于筛选元素的查询语法
|
||||
:param timeout: 查找元素的超时时间
|
||||
@ -270,7 +270,7 @@ class ChromeElement(DrissionElement):
|
||||
|
||||
def befores(self,
|
||||
filter_loc: Union[tuple, str] = '',
|
||||
timeout: float = None) -> List[Union['ChromeElement', str]]:
|
||||
timeout: float = None) -> List[Union['ChromiumElement', str]]:
|
||||
"""返回当前元素后面符合条件的全部兄弟元素或节点组成的列表,可用查询语法筛选。查找范围不限兄弟元素,而是整个DOM文档 \n
|
||||
:param filter_loc: 用于筛选元素的查询语法
|
||||
:param timeout: 查找元素的超时时间
|
||||
@ -279,7 +279,7 @@ class ChromeElement(DrissionElement):
|
||||
return super().befores(filter_loc, timeout)
|
||||
|
||||
def wait_ele(self,
|
||||
loc_or_ele: Union[str, tuple, 'ChromeElement'],
|
||||
loc_or_ele: Union[str, tuple, 'ChromiumElement'],
|
||||
timeout: float = None) -> 'ChromeElementWaiter':
|
||||
"""返回用于等待子元素到达某个状态的等待器对象 \n
|
||||
:param loc_or_ele: 可以是元素、查询字符串、loc元组
|
||||
@ -320,7 +320,7 @@ class ChromeElement(DrissionElement):
|
||||
def is_alive(self) -> bool:
|
||||
"""返回元素是否仍在DOM中"""
|
||||
try:
|
||||
self.attrs
|
||||
d = self.attrs
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
@ -362,6 +362,50 @@ class ChromeElement(DrissionElement):
|
||||
else:
|
||||
return attrs.get(attr, None)
|
||||
|
||||
def set_attr(self, attr: str, value: str) -> None:
|
||||
"""设置元素attribute属性 \n
|
||||
:param attr: 属性名
|
||||
:param value: 属性值
|
||||
:return: None
|
||||
"""
|
||||
self.run_script(f'this.setAttribute(arguments[0], arguments[1]);', False, attr, str(value))
|
||||
|
||||
def remove_attr(self, attr: str) -> None:
|
||||
"""删除元素attribute属性 \n
|
||||
:param attr: 属性名
|
||||
:return: None
|
||||
"""
|
||||
self.run_script(f'this.removeAttribute("{attr}");')
|
||||
|
||||
def prop(self, prop: str) -> Union[str, int, None]:
|
||||
"""获取property属性值 \n
|
||||
:param prop: 属性名
|
||||
:return: 属性值文本
|
||||
"""
|
||||
p = self.page.driver.Runtime.getProperties(objectId=self._obj_id)['result']
|
||||
for i in p:
|
||||
if i['name'] == prop:
|
||||
if 'value' not in i or 'value' not in i['value']:
|
||||
return None
|
||||
|
||||
return format_html(i['value']['value'])
|
||||
|
||||
def set_prop(self, prop: str, value: str) -> None:
|
||||
"""设置元素property属性 \n
|
||||
:param prop: 属性名
|
||||
:param value: 属性值
|
||||
:return: None
|
||||
"""
|
||||
value = value.replace('"', r'\"')
|
||||
self.run_script(f'this.{prop}="{value}";')
|
||||
|
||||
def set_innerHTML(self, html: str) -> None:
|
||||
"""设置元素innerHTML \n
|
||||
:param html: html文本
|
||||
:return: None
|
||||
"""
|
||||
self.set_prop('innerHTML', html)
|
||||
|
||||
def run_script(self, script: str, as_expr: bool = False, *args: Any) -> Any:
|
||||
"""运行javascript代码 \n
|
||||
:param script: js文本
|
||||
@ -383,7 +427,7 @@ class ChromeElement(DrissionElement):
|
||||
|
||||
def ele(self,
|
||||
loc_or_str: Union[Tuple[str, str], str],
|
||||
timeout: float = None) -> Union['ChromeElement', str, None]:
|
||||
timeout: float = None) -> Union['ChromiumElement', str, None]:
|
||||
"""返回当前元素下级符合条件的第一个元素、属性或节点文本 \n
|
||||
:param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
|
||||
:param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致
|
||||
@ -393,7 +437,7 @@ class ChromeElement(DrissionElement):
|
||||
|
||||
def eles(self,
|
||||
loc_or_str: Union[Tuple[str, str], str],
|
||||
timeout: float = None) -> List[Union['ChromeElement', str]]:
|
||||
timeout: float = None) -> List[Union['ChromiumElement', str]]:
|
||||
"""返回当前元素下级所有符合条件的子元素、属性或节点文本 \n
|
||||
:param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
|
||||
:param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致
|
||||
@ -422,7 +466,7 @@ class ChromeElement(DrissionElement):
|
||||
def _ele(self,
|
||||
loc_or_str: Union[Tuple[str, str], str],
|
||||
timeout: float = None,
|
||||
single: bool = True) -> Union['ChromeElement', str, None, List[Union['ChromeElement', str]]]:
|
||||
single: bool = True) -> Union['ChromiumElement', str, None, List[Union['ChromiumElement', str]]]:
|
||||
"""返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个 \n
|
||||
:param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
|
||||
:param timeout: 查找元素超时时间
|
||||
@ -431,43 +475,6 @@ class ChromeElement(DrissionElement):
|
||||
"""
|
||||
return make_chrome_ele(self, loc_or_str, single, timeout)
|
||||
|
||||
def prop(self, prop: str) -> Union[str, int, None]:
|
||||
"""获取property属性值 \n
|
||||
:param prop: 属性名
|
||||
:return: 属性值文本
|
||||
"""
|
||||
p = self.page.driver.Runtime.getProperties(objectId=self._obj_id)['result']
|
||||
for i in p:
|
||||
if i['name'] == prop:
|
||||
if 'value' not in i or 'value' not in i['value']:
|
||||
return None
|
||||
|
||||
return format_html(i['value']['value'])
|
||||
|
||||
def set_prop(self, prop: str, value: str) -> None:
|
||||
"""设置元素property属性 \n
|
||||
:param prop: 属性名
|
||||
:param value: 属性值
|
||||
:return: None
|
||||
"""
|
||||
value = value.replace("'", "\\'")
|
||||
self.run_script(f'this.{prop}="{value}";')
|
||||
|
||||
def set_attr(self, attr: str, value: str) -> None:
|
||||
"""设置元素attribute属性 \n
|
||||
:param attr: 属性名
|
||||
:param value: 属性值
|
||||
:return: None
|
||||
"""
|
||||
self.run_script(f'this.setAttribute(arguments[0], arguments[1]);', False, attr, str(value))
|
||||
|
||||
def remove_attr(self, attr: str) -> None:
|
||||
"""删除元素attribute属性 \n
|
||||
:param attr: 属性名
|
||||
:return: None
|
||||
"""
|
||||
self.run_script(f'this.removeAttribute("{attr}");')
|
||||
|
||||
def style(self, style: str, pseudo_ele: str = '') -> str:
|
||||
"""返回元素样式属性值,可获取伪元素属性值 \n
|
||||
:param style: 样式属性名称
|
||||
@ -612,7 +619,7 @@ class ChromeElement(DrissionElement):
|
||||
|
||||
if not by_js:
|
||||
self.page.scroll_to_see(self)
|
||||
if self.is_in_view:
|
||||
if self.is_in_viewport:
|
||||
midpoint = self.midpoint
|
||||
client_midpoint = self.client_midpoint
|
||||
client_x = client_midpoint['x']
|
||||
@ -698,7 +705,7 @@ class ChromeElement(DrissionElement):
|
||||
self.drag_to((offset_x, offset_y), speed, shake)
|
||||
|
||||
def drag_to(self,
|
||||
ele_or_loc: Union[tuple, 'ChromeElement'],
|
||||
ele_or_loc: Union[tuple, 'ChromiumElement'],
|
||||
speed: int = 40,
|
||||
shake: bool = True) -> None:
|
||||
"""拖拽当前元素,目标为另一个元素或坐标元组 \n
|
||||
@ -708,7 +715,7 @@ class ChromeElement(DrissionElement):
|
||||
:return: None
|
||||
"""
|
||||
# x, y:目标点坐标
|
||||
if isinstance(ele_or_loc, ChromeElement):
|
||||
if isinstance(ele_or_loc, ChromiumElement):
|
||||
midpoint = ele_or_loc.midpoint
|
||||
target_x = midpoint['x']
|
||||
target_y = midpoint['y']
|
||||
@ -802,7 +809,7 @@ class ChromeElement(DrissionElement):
|
||||
class ChromeShadowRootElement(BaseElement):
|
||||
"""ChromeShadowRootElement是用于处理ShadowRoot的类,使用方法和ChromeElement基本一致"""
|
||||
|
||||
def __init__(self, parent_ele: ChromeElement, obj_id: str):
|
||||
def __init__(self, parent_ele: ChromiumElement, obj_id: str):
|
||||
super().__init__(parent_ele.page)
|
||||
self.parent_ele = parent_ele
|
||||
self._node_id = self._get_node_id(obj_id)
|
||||
@ -813,7 +820,7 @@ class ChromeShadowRootElement(BaseElement):
|
||||
|
||||
def __call__(self,
|
||||
loc_or_str: Union[Tuple[str, str], str],
|
||||
timeout: float = None) -> Union[ChromeElement, None]:
|
||||
timeout: float = None) -> Union[ChromiumElement, None]:
|
||||
"""在内部查找元素 \n
|
||||
例:ele2 = ele1('@id=ele_id') \n
|
||||
:param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
|
||||
@ -880,7 +887,7 @@ class ChromeShadowRootElement(BaseElement):
|
||||
from threading import Thread
|
||||
Thread(target=_run_script, args=(self, script, as_expr, self.page.timeouts.script, args)).start()
|
||||
|
||||
def parent(self, level_or_loc: Union[str, int] = 1) -> ChromeElement:
|
||||
def parent(self, level_or_loc: Union[str, int] = 1) -> ChromiumElement:
|
||||
"""返回上面某一级父元素,可指定层数或用查询语法定位 \n
|
||||
:param level_or_loc: 第几级父元素,或定位符
|
||||
:return: ChromeElement对象
|
||||
@ -903,7 +910,7 @@ class ChromeShadowRootElement(BaseElement):
|
||||
|
||||
def next(self,
|
||||
index: int = 1,
|
||||
filter_loc: Union[tuple, str] = '') -> Union[ChromeElement, str, None]:
|
||||
filter_loc: Union[tuple, str] = '') -> Union[ChromiumElement, str, None]:
|
||||
"""返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n
|
||||
:param index: 第几个查询结果元素
|
||||
:param filter_loc: 用于筛选元素的查询语法
|
||||
@ -914,7 +921,7 @@ class ChromeShadowRootElement(BaseElement):
|
||||
|
||||
def before(self,
|
||||
index: int = 1,
|
||||
filter_loc: Union[tuple, str] = '') -> Union[ChromeElement, str, None]:
|
||||
filter_loc: Union[tuple, str] = '') -> Union[ChromiumElement, str, None]:
|
||||
"""返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n
|
||||
:param index: 前面第几个查询结果元素
|
||||
:param filter_loc: 用于筛选元素的查询语法
|
||||
@ -924,7 +931,7 @@ class ChromeShadowRootElement(BaseElement):
|
||||
return nodes[index - 1] if nodes else None
|
||||
|
||||
def after(self, index: int = 1,
|
||||
filter_loc: Union[tuple, str] = '') -> Union[ChromeElement, str, None]:
|
||||
filter_loc: Union[tuple, str] = '') -> Union[ChromiumElement, str, None]:
|
||||
"""返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n
|
||||
:param index: 后面第几个查询结果元素
|
||||
:param filter_loc: 用于筛选元素的查询语法
|
||||
@ -933,7 +940,7 @@ class ChromeShadowRootElement(BaseElement):
|
||||
nodes = self.afters(filter_loc=filter_loc)
|
||||
return nodes[index - 1] if nodes else None
|
||||
|
||||
def nexts(self, filter_loc: Union[tuple, str] = '') -> List[Union[ChromeElement, str]]:
|
||||
def nexts(self, filter_loc: Union[tuple, str] = '') -> List[Union[ChromiumElement, str]]:
|
||||
"""返回后面所有兄弟元素或节点组成的列表 \n
|
||||
:param filter_loc: 用于筛选元素的查询语法
|
||||
:return: ChromeElement对象组成的列表
|
||||
@ -946,7 +953,7 @@ class ChromeShadowRootElement(BaseElement):
|
||||
xpath = f'xpath:./{loc}'
|
||||
return self.parent_ele.eles(xpath, timeout=0.1)
|
||||
|
||||
def befores(self, filter_loc: Union[tuple, str] = '') -> List[Union[ChromeElement, str]]:
|
||||
def befores(self, filter_loc: Union[tuple, str] = '') -> List[Union[ChromiumElement, str]]:
|
||||
"""返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n
|
||||
:param filter_loc: 用于筛选元素的查询语法
|
||||
:return: 本元素前面的元素或节点组成的列表
|
||||
@ -959,7 +966,7 @@ class ChromeShadowRootElement(BaseElement):
|
||||
xpath = f'xpath:./preceding::{loc}'
|
||||
return self.parent_ele.eles(xpath, timeout=0.1)
|
||||
|
||||
def afters(self, filter_loc: Union[tuple, str] = '') -> List[Union[ChromeElement, str]]:
|
||||
def afters(self, filter_loc: Union[tuple, str] = '') -> List[Union[ChromiumElement, str]]:
|
||||
"""返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n
|
||||
:param filter_loc: 用于筛选元素的查询语法
|
||||
:return: 本元素后面的元素或节点组成的列表
|
||||
@ -971,7 +978,7 @@ class ChromeShadowRootElement(BaseElement):
|
||||
|
||||
def ele(self,
|
||||
loc_or_str: Union[Tuple[str, str], str],
|
||||
timeout: float = None) -> Union[ChromeElement, None]:
|
||||
timeout: float = None) -> Union[ChromiumElement, None]:
|
||||
"""返回当前元素下级符合条件的第一个元素 \n
|
||||
:param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
|
||||
:param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致
|
||||
@ -981,7 +988,7 @@ class ChromeShadowRootElement(BaseElement):
|
||||
|
||||
def eles(self,
|
||||
loc_or_str: Union[Tuple[str, str], str],
|
||||
timeout: float = None) -> List[ChromeElement]:
|
||||
timeout: float = None) -> List[ChromiumElement]:
|
||||
"""返回当前元素下级所有符合条件的子元素 \n
|
||||
:param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
|
||||
:param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致
|
||||
@ -1006,7 +1013,7 @@ class ChromeShadowRootElement(BaseElement):
|
||||
def _ele(self,
|
||||
loc_or_str: Union[Tuple[str, str], str],
|
||||
timeout: float = None,
|
||||
single: bool = True) -> Union['ChromeElement', None, List[ChromeElement]]:
|
||||
single: bool = True) -> Union['ChromiumElement', None, List[ChromiumElement]]:
|
||||
"""返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个 \n
|
||||
:param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
|
||||
:param timeout: 查找元素超时时间
|
||||
@ -1029,14 +1036,14 @@ class ChromeShadowRootElement(BaseElement):
|
||||
css_paths = [i.css_path[47:] for i in eles]
|
||||
if single:
|
||||
node_id = self.page.driver.DOM.querySelector(nodeId=self._node_id, selector=css_paths[0])['nodeId']
|
||||
return ChromeElement(self.page, node_id) if node_id else None
|
||||
return ChromiumElement(self.page, node_id) if node_id else None
|
||||
|
||||
else:
|
||||
results = []
|
||||
for i in css_paths:
|
||||
node_id = self.page.driver.DOM.querySelector(nodeId=self._node_id, selector=i)['nodeId']
|
||||
if node_id:
|
||||
results.append(ChromeElement(self.page, node_id))
|
||||
results.append(ChromiumElement(self.page, node_id))
|
||||
return results
|
||||
|
||||
def _get_node_id(self, obj_id) -> str:
|
||||
@ -1044,10 +1051,10 @@ class ChromeShadowRootElement(BaseElement):
|
||||
return self.page.driver.DOM.requestNode(objectId=obj_id)['nodeId']
|
||||
|
||||
|
||||
def make_chrome_ele(ele: ChromeElement,
|
||||
def make_chrome_ele(ele: ChromiumElement,
|
||||
loc: Union[str, Tuple[str, str]],
|
||||
single: bool = True,
|
||||
timeout: float = None) -> Union[ChromeElement, str, None, List[Union[ChromeElement, str]]]:
|
||||
timeout: float = None) -> Union[ChromiumElement, str, None, List[Union[ChromiumElement, str]]]:
|
||||
"""在chrome元素中查找 \n
|
||||
:param ele: ChromeElement对象
|
||||
:param loc: 元素定位元组
|
||||
@ -1078,10 +1085,10 @@ def make_chrome_ele(ele: ChromeElement,
|
||||
return _find_by_css(ele, loc[1], single, timeout)
|
||||
|
||||
|
||||
def _find_by_xpath(ele: ChromeElement,
|
||||
def _find_by_xpath(ele: ChromiumElement,
|
||||
xpath: str,
|
||||
single: bool,
|
||||
timeout: float) -> Union[ChromeElement, List[ChromeElement]]:
|
||||
timeout: float) -> Union[ChromiumElement, List[ChromiumElement], None]:
|
||||
"""执行用xpath在元素中查找元素
|
||||
:param ele: 在此元素中查找
|
||||
:param xpath: 查找语句
|
||||
@ -1119,22 +1126,22 @@ def _find_by_xpath(ele: ChromeElement,
|
||||
if r['result']['subtype'] == 'null':
|
||||
return None
|
||||
else:
|
||||
return ChromeElement(ele.page, obj_id=r['result']['objectId'])
|
||||
return ChromiumElement(ele.page, obj_id=r['result']['objectId'])
|
||||
|
||||
else:
|
||||
if r['result']['description'] == 'NodeList(0)':
|
||||
return []
|
||||
else:
|
||||
r = ele.page.driver.Runtime.getProperties(objectId=r['result']['objectId'], ownProperties=True)['result']
|
||||
return [ChromeElement(ele.page, obj_id=i['value']['objectId'])
|
||||
return [ChromiumElement(ele.page, obj_id=i['value']['objectId'])
|
||||
if i['value']['type'] == 'object' else i['value']['value']
|
||||
for i in r[:-1]]
|
||||
|
||||
|
||||
def _find_by_css(ele: ChromeElement,
|
||||
def _find_by_css(ele: ChromiumElement,
|
||||
selector: str,
|
||||
single: bool,
|
||||
timeout: float) -> Union[ChromeElement, List[ChromeElement]]:
|
||||
timeout: float) -> Union[ChromiumElement, List[ChromiumElement], None]:
|
||||
"""执行用css selector在元素中查找元素
|
||||
:param ele: 在此元素中查找
|
||||
:param selector: 查找语句
|
||||
@ -1163,14 +1170,14 @@ def _find_by_css(ele: ChromeElement,
|
||||
if r['result']['subtype'] == 'null':
|
||||
return None
|
||||
else:
|
||||
return ChromeElement(ele.page, obj_id=r['result']['objectId'])
|
||||
return ChromiumElement(ele.page, obj_id=r['result']['objectId'])
|
||||
|
||||
else:
|
||||
if r['result']['description'] == 'NodeList(0)':
|
||||
return []
|
||||
else:
|
||||
r = ele.page.driver.Runtime.getProperties(objectId=r['result']['objectId'], ownProperties=True)['result']
|
||||
return [ChromeElement(ele.page, obj_id=i['value']['objectId']) for i in r]
|
||||
return [ChromiumElement(ele.page, obj_id=i['value']['objectId']) for i in r]
|
||||
|
||||
|
||||
def _make_js_for_find_ele_by_xpath(xpath: str, type_txt: str, node_txt: str) -> str:
|
||||
@ -1222,7 +1229,7 @@ def _run_script(page_or_ele, script: str, as_expr: bool = False, timeout: float
|
||||
:param args: 参数,按顺序在js文本中对应argument[0]、argument[2]...
|
||||
:return: js执行结果
|
||||
"""
|
||||
if isinstance(page_or_ele, (ChromeElement, ChromeShadowRootElement)):
|
||||
if isinstance(page_or_ele, (ChromiumElement, ChromeShadowRootElement)):
|
||||
page = page_or_ele.page
|
||||
obj_id = page_or_ele.obj_id
|
||||
else:
|
||||
@ -1271,7 +1278,7 @@ def _parse_js_result(page, ele, result: dict):
|
||||
if result['className'] == 'ShadowRoot':
|
||||
return ChromeShadowRootElement(ele, obj_id=result['objectId'])
|
||||
else:
|
||||
return ChromeElement(page, obj_id=result['objectId'])
|
||||
return ChromiumElement(page, obj_id=result['objectId'])
|
||||
|
||||
elif sub_type == 'array':
|
||||
r = page.driver.Runtime.getProperties(objectId=result['result']['objectId'], ownProperties=True)['result']
|
||||
@ -1289,7 +1296,7 @@ def _parse_js_result(page, ele, result: dict):
|
||||
|
||||
def _convert_argument(arg: Any) -> dict:
|
||||
"""把参数转换成js能够接收的形式"""
|
||||
if isinstance(arg, ChromeElement):
|
||||
if isinstance(arg, ChromiumElement):
|
||||
return {'objectId': arg.obj_id}
|
||||
|
||||
elif isinstance(arg, (int, float, str, bool)):
|
||||
@ -1302,9 +1309,8 @@ def _convert_argument(arg: Any) -> dict:
|
||||
return {'unserializableValue': '-Infinity'}
|
||||
|
||||
|
||||
def _send_enter(ele: ChromeElement) -> None:
|
||||
def _send_enter(ele: ChromiumElement) -> None:
|
||||
"""发送回车"""
|
||||
# todo:windows系统回车是否不一样
|
||||
data = {'type': 'keyDown', 'modifiers': 0, 'windowsVirtualKeyCode': 13, 'code': 'Enter', 'key': 'Enter',
|
||||
'text': '\r', 'autoRepeat': False, 'unmodifiedText': '\r', 'location': 0, 'isKeypad': False}
|
||||
|
||||
@ -1313,7 +1319,7 @@ def _send_enter(ele: ChromeElement) -> None:
|
||||
ele.page.run_cdp('Input.dispatchKeyEvent', **data)
|
||||
|
||||
|
||||
def _send_key(ele: ChromeElement, modifier: int, key: str) -> None:
|
||||
def _send_key(ele: ChromiumElement, modifier: int, key: str) -> None:
|
||||
"""发送一个字,在键盘中的字符触发按键,其它直接发送文本"""
|
||||
if key not in _keyDefinitions:
|
||||
ele.page.run_cdp('Input.insertText', text=key)
|
||||
@ -1365,7 +1371,7 @@ class ChromeScroll(object):
|
||||
"""
|
||||
:param page_or_ele: ChromePage或ChromeElement
|
||||
"""
|
||||
if isinstance(page_or_ele, ChromeElement):
|
||||
if isinstance(page_or_ele, ChromiumElement):
|
||||
self.t1 = self.t2 = 'this'
|
||||
self.obj_id = page_or_ele.obj_id
|
||||
self.page = page_or_ele.page
|
||||
@ -1444,7 +1450,7 @@ class ChromeScroll(object):
|
||||
class ChromeSelect(object):
|
||||
"""ChromeSelect 类专门用于处理 d 模式下 select 标签"""
|
||||
|
||||
def __init__(self, ele: ChromeElement):
|
||||
def __init__(self, ele: ChromiumElement):
|
||||
"""初始化 \n
|
||||
:param ele: select 元素对象
|
||||
"""
|
||||
@ -1470,12 +1476,12 @@ class ChromeSelect(object):
|
||||
return multi and multi.lower() != "false"
|
||||
|
||||
@property
|
||||
def options(self) -> List[ChromeElement]:
|
||||
def options(self) -> List[ChromiumElement]:
|
||||
"""返回所有选项元素组成的列表"""
|
||||
return self._ele.eles('tag:option')
|
||||
|
||||
@property
|
||||
def selected_option(self) -> Union[ChromeElement, None]:
|
||||
def selected_option(self) -> Union[ChromiumElement, None]:
|
||||
"""返回第一个被选中的option元素 \n
|
||||
:return: ChromeElement对象或None
|
||||
"""
|
||||
@ -1483,7 +1489,7 @@ class ChromeSelect(object):
|
||||
return ele
|
||||
|
||||
@property
|
||||
def selected_options(self) -> List[ChromeElement]:
|
||||
def selected_options(self) -> List[ChromiumElement]:
|
||||
"""返回所有被选中的option元素列表 \n
|
||||
:return: ChromeElement对象组成的列表
|
||||
"""
|
||||
@ -1638,14 +1644,14 @@ class ChromeElementWaiter(object):
|
||||
|
||||
def __init__(self,
|
||||
page_or_ele,
|
||||
loc_or_ele: Union[str, tuple, ChromeElement],
|
||||
loc_or_ele: Union[str, tuple, ChromiumElement],
|
||||
timeout: float = None):
|
||||
"""等待元素在dom中某种状态,如删除、显示、隐藏 \n
|
||||
:param page_or_ele: 页面或父元素
|
||||
:param loc_or_ele: 要等待的元素,可以是已有元素、定位符
|
||||
:param timeout: 超时时间,默认读取页面超时时间
|
||||
"""
|
||||
if not isinstance(loc_or_ele, (str, tuple, ChromeElement)):
|
||||
if not isinstance(loc_or_ele, (str, tuple, ChromiumElement)):
|
||||
raise TypeError('loc_or_ele只能接收定位符或元素对象。')
|
||||
|
||||
self.driver = page_or_ele
|
||||
@ -1653,11 +1659,11 @@ class ChromeElementWaiter(object):
|
||||
if timeout is not None:
|
||||
self.timeout = timeout
|
||||
else:
|
||||
self.timeout = page_or_ele.page.timeout if isinstance(page_or_ele, ChromeElement) else page_or_ele.timeout
|
||||
self.timeout = page_or_ele.page.timeout if isinstance(page_or_ele, ChromiumElement) else page_or_ele.timeout
|
||||
|
||||
def delete(self) -> bool:
|
||||
"""等待元素从dom删除"""
|
||||
if isinstance(self.loc_or_ele, ChromeElement):
|
||||
if isinstance(self.loc_or_ele, ChromiumElement):
|
||||
end_time = perf_counter() + self.timeout
|
||||
while perf_counter() < end_time:
|
||||
if not self.loc_or_ele.is_alive:
|
@ -16,10 +16,10 @@ from .config import DriverOptions, _cookies_to_tuple
|
||||
from .base import BasePage
|
||||
from .common import get_loc
|
||||
from .drission import connect_chrome
|
||||
from .chrome_element import ChromeElement, ChromeScroll, _run_script, ChromeElementWaiter
|
||||
from .chromium_element import ChromiumElement, ChromeScroll, _run_script, ChromeElementWaiter
|
||||
|
||||
|
||||
class ChromePage(BasePage):
|
||||
class ChromiumPage(BasePage):
|
||||
"""用于管理浏览器的类"""
|
||||
|
||||
def __init__(self, Tab_or_Options: Union[Tab, DriverOptions] = None,
|
||||
@ -67,14 +67,14 @@ class ChromePage(BasePage):
|
||||
self._driver.DOM.enable()
|
||||
self._driver.Page.enable()
|
||||
root = self._driver.DOM.getDocument()
|
||||
self.root = ChromeElement(self, node_id=root['root']['nodeId'])
|
||||
self.root = ChromiumElement(self, node_id=root['root']['nodeId'])
|
||||
|
||||
self._alert = Alert()
|
||||
self.driver.Page.javascriptDialogOpening = self._on_alert_open
|
||||
self.driver.Page.javascriptDialogClosed = self._on_alert_close
|
||||
|
||||
def __call__(self, loc_or_str: Union[Tuple[str, str], str, 'ChromeElement'],
|
||||
timeout: float = None) -> Union['ChromeElement', None]:
|
||||
def __call__(self, loc_or_str: Union[Tuple[str, str], str, 'ChromiumElement'],
|
||||
timeout: float = None) -> Union['ChromiumElement', None]:
|
||||
"""在内部查找元素 \n
|
||||
例:ele = page('@id=ele_id') \n
|
||||
:param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
|
||||
@ -114,7 +114,7 @@ class ChromePage(BasePage):
|
||||
@property
|
||||
def tab_ids(self) -> list:
|
||||
"""返回所有标签页id"""
|
||||
self.driver
|
||||
d = self.driver
|
||||
json = loads(requests_get(f'http://{self.address}/json').text)
|
||||
return [i['id'] for i in json if i['type'] == 'page']
|
||||
|
||||
@ -141,7 +141,7 @@ class ChromePage(BasePage):
|
||||
return {'height': h, 'width': w}
|
||||
|
||||
@property
|
||||
def active_ele(self) -> ChromeElement:
|
||||
def active_ele(self) -> ChromiumElement:
|
||||
"""返回当前焦点所在元素"""
|
||||
return self.run_script('return document.activeElement;')
|
||||
|
||||
@ -267,8 +267,8 @@ class ChromePage(BasePage):
|
||||
self.driver.Network.setCookies(cookies=result_cookies)
|
||||
|
||||
def ele(self,
|
||||
loc_or_ele: Union[Tuple[str, str], str, ChromeElement],
|
||||
timeout: float = None) -> Union[ChromeElement, None]:
|
||||
loc_or_ele: Union[Tuple[str, str], str, ChromiumElement],
|
||||
timeout: float = None) -> Union[ChromiumElement, None]:
|
||||
"""获取第一个符合条件的元素对象 \n
|
||||
:param loc_or_ele: 定位符或元素对象
|
||||
:param timeout: 查找超时时间
|
||||
@ -277,8 +277,8 @@ class ChromePage(BasePage):
|
||||
return self._ele(loc_or_ele, timeout=timeout)
|
||||
|
||||
def eles(self,
|
||||
loc_or_ele: Union[Tuple[str, str], str, ChromeElement],
|
||||
timeout: float = None) -> List[ChromeElement]:
|
||||
loc_or_ele: Union[Tuple[str, str], str, ChromiumElement],
|
||||
timeout: float = None) -> List[ChromiumElement]:
|
||||
"""获取所有符合条件的元素对象 \n
|
||||
:param loc_or_ele: 定位符或元素对象
|
||||
:param timeout: 查找超时时间
|
||||
@ -286,12 +286,13 @@ class ChromePage(BasePage):
|
||||
"""
|
||||
return self._ele(loc_or_ele, timeout=timeout, single=False)
|
||||
|
||||
def s_ele(self, loc_or_ele: Union[Tuple[str, str], str, ChromeElement] = None) -> Union[SessionElement, str, None]:
|
||||
def s_ele(self, loc_or_ele: Union[Tuple[str, str], str, ChromiumElement] = None) -> Union[
|
||||
SessionElement, str, None]:
|
||||
"""查找第一个符合条件的元素以SessionElement形式返回,处理复杂页面时效率很高 \n
|
||||
:param loc_or_ele: 元素的定位信息,可以是loc元组,或查询字符串
|
||||
:return: SessionElement对象或属性、文本
|
||||
"""
|
||||
if isinstance(loc_or_ele, ChromeElement):
|
||||
if isinstance(loc_or_ele, ChromiumElement):
|
||||
return make_session_ele(loc_or_ele)
|
||||
else:
|
||||
return make_session_ele(self, loc_or_ele)
|
||||
@ -304,9 +305,9 @@ class ChromePage(BasePage):
|
||||
return make_session_ele(self, loc_or_str, single=False)
|
||||
|
||||
def _ele(self,
|
||||
loc_or_ele: Union[Tuple[str, str], str, ChromeElement],
|
||||
loc_or_ele: Union[Tuple[str, str], str, ChromiumElement],
|
||||
timeout: float = None,
|
||||
single: bool = True) -> Union[ChromeElement, None, List[ChromeElement]]:
|
||||
single: bool = True) -> Union[ChromiumElement, None, List[ChromiumElement]]:
|
||||
"""执行元素查找
|
||||
:param loc_or_ele: 定位符或元素对象
|
||||
:param timeout: 查找超时时间
|
||||
@ -315,7 +316,7 @@ class ChromePage(BasePage):
|
||||
"""
|
||||
if isinstance(loc_or_ele, (str, tuple)):
|
||||
loc = get_loc(loc_or_ele)[1]
|
||||
elif isinstance(loc_or_ele, ChromeElement):
|
||||
elif isinstance(loc_or_ele, ChromiumElement):
|
||||
return loc_or_ele
|
||||
else:
|
||||
raise ValueError('loc_or_str参数只能是tuple、str、ChromeElement类型。')
|
||||
@ -336,12 +337,12 @@ class ChromePage(BasePage):
|
||||
count = 1 if single else count
|
||||
nodeIds = self.driver.DOM.getSearchResults(searchId=search_result['searchId'], fromIndex=0, toIndex=count)
|
||||
if count == 1:
|
||||
return ChromeElement(self, node_id=nodeIds['nodeIds'][0])
|
||||
return ChromiumElement(self, node_id=nodeIds['nodeIds'][0])
|
||||
else:
|
||||
return [ChromeElement(self, node_id=i) for i in nodeIds['nodeIds']]
|
||||
return [ChromiumElement(self, node_id=i) for i in nodeIds['nodeIds']]
|
||||
|
||||
def wait_ele(self,
|
||||
loc_or_ele: Union[str, tuple, ChromeElement],
|
||||
loc_or_ele: Union[str, tuple, ChromiumElement],
|
||||
timeout: float = None) -> ChromeElementWaiter:
|
||||
"""返回用于等待元素到达某个状态的等待器对象 \n
|
||||
:param loc_or_ele: 可以是元素、查询字符串、loc元组
|
||||
@ -405,7 +406,7 @@ class ChromePage(BasePage):
|
||||
f.write(png)
|
||||
return str(path.absolute())
|
||||
|
||||
def scroll_to_see(self, loc_or_ele: Union[str, tuple, ChromeElement]) -> None:
|
||||
def scroll_to_see(self, loc_or_ele: Union[str, tuple, ChromiumElement]) -> None:
|
||||
"""滚动页面直到元素可见 \n
|
||||
:param loc_or_ele: 元素的定位信息,可以是loc元组,或查询字符串(详见ele函数注释)
|
||||
:return: None
|
||||
@ -492,7 +493,7 @@ class ChromePage(BasePage):
|
||||
:param url: 新标签页跳转到的网址
|
||||
:return: None
|
||||
"""
|
||||
self.driver
|
||||
d = self.driver
|
||||
url = f'?{url}' if url else ''
|
||||
requests_get(f'http://{self.address}/json/new{url}')
|
||||
|
||||
@ -517,7 +518,7 @@ class ChromePage(BasePage):
|
||||
|
||||
def to_front(self) -> None:
|
||||
"""激活当前标签页使其处于最前面"""
|
||||
self.driver
|
||||
d = self.driver
|
||||
requests_get(f'http://{self.address}/json/activate/{self.current_tab_id}')
|
||||
|
||||
def close_tabs(self, num_or_ids: Union[int, str, list, tuple, set] = None, others: bool = False) -> None:
|
||||
@ -700,7 +701,7 @@ class Alert(object):
|
||||
class Timeout(object):
|
||||
"""用于保存d模式timeout信息的类"""
|
||||
|
||||
def __init__(self, page: ChromePage):
|
||||
def __init__(self, page: ChromiumPage):
|
||||
self.page = page
|
||||
self.page_load = 30
|
||||
self.script = 30
|
||||
@ -713,7 +714,7 @@ class Timeout(object):
|
||||
class WindowSizeSetter(object):
|
||||
"""用于设置窗口大小的类"""
|
||||
|
||||
def __init__(self, page: ChromePage):
|
||||
def __init__(self, page: ChromiumPage):
|
||||
self.driver = page.driver
|
||||
self.window_id = self._get_info()['windowId']
|
||||
|
||||
@ -784,7 +785,7 @@ def _get_tabs(ids: list, num_or_ids: Union[int, str, list, tuple, set]) -> set:
|
||||
return set(i if isinstance(i, str) else ids[i] for i in num_or_ids)
|
||||
|
||||
|
||||
def _show_or_hide_browser(page: ChromePage, hide: bool = True) -> None:
|
||||
def _show_or_hide_browser(page: ChromiumPage, hide: bool = True) -> None:
|
||||
"""执行显示或隐藏浏览器窗口
|
||||
:param page: ChromePage对象
|
||||
:param hide: 是否隐藏
|
@ -611,10 +611,55 @@ def _get_running_args(opt: DriverOptions) -> list:
|
||||
result.append(ext)
|
||||
|
||||
# ----------处理experimental_options-------------
|
||||
prefs = opt.experimental_options.get('prefs', None)
|
||||
if prefs and opt.user_data_path:
|
||||
prefs_file = Path(opt.user_data_path) / 'Default' / 'Preferences'
|
||||
if not prefs_file.exists():
|
||||
return result
|
||||
|
||||
from json import load, dump
|
||||
with open(prefs_file, "r") as f:
|
||||
j = load(f)
|
||||
|
||||
for pref in prefs:
|
||||
value = prefs[pref]
|
||||
pref = pref.split('.')
|
||||
_make_leave_in_dict(j, pref, 0, len(pref))
|
||||
_set_value_to_dict(j, pref, value)
|
||||
|
||||
with open(prefs_file, 'w') as f:
|
||||
dump(j, f)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _make_leave_in_dict(target_dict: dict, src: list, num: int, end: int) -> None:
|
||||
"""把prefs中a.b.c形式的属性转为a['b']['c']形式
|
||||
:param target_dict: 要处理的dict
|
||||
:param src: 属性层级列表[a, b, c]
|
||||
:param num: 当前处理第几个
|
||||
:param end: src长度
|
||||
:return: None
|
||||
"""
|
||||
if num == end:
|
||||
return
|
||||
if src[num] not in target_dict:
|
||||
target_dict[src[num]] = {}
|
||||
num += 1
|
||||
_make_leave_in_dict(target_dict[src[num - 1]], src, num, end)
|
||||
|
||||
|
||||
def _set_value_to_dict(target_dict: dict, src: list, value) -> None:
|
||||
"""把a.b.c形式的属性的值赋值到a['b']['c']形式的字典中
|
||||
:param target_dict: 要处理的dict
|
||||
:param src: 属性层级列表[a, b, c]
|
||||
:param value: 属性值
|
||||
:return: None
|
||||
"""
|
||||
src = "']['".join(src)
|
||||
src = f"target_dict['{src}']=value"
|
||||
exec(src)
|
||||
|
||||
|
||||
def _location_in_viewport(page, loc_x: int, loc_y: int) -> bool:
|
||||
"""判断给定的坐标是否在视口中 |n
|
||||
|
@ -462,6 +462,7 @@ class DriverOptions(Options):
|
||||
"""
|
||||
super().__init__()
|
||||
self._driver_path = None
|
||||
self._user_data_path = None
|
||||
self.ini_path = None
|
||||
|
||||
if read_file:
|
||||
@ -475,9 +476,13 @@ class DriverOptions(Options):
|
||||
self._extensions = options_dict.get('extensions', [])
|
||||
self._experimental_options = options_dict.get('experimental_options', {})
|
||||
self._debugger_address = options_dict.get('debugger_address', None)
|
||||
self.set_window_rect = options_dict.get('set_window_rect', None)
|
||||
self.page_load_strategy = options_dict.get('page_load_strategy', 'normal')
|
||||
|
||||
for arg in self._arguments:
|
||||
if arg.startswith('--user-data-dir='):
|
||||
self.set_paths(user_data_path=arg[16:])
|
||||
break
|
||||
|
||||
self.timeouts = options_dict.get('timeouts', {'implicit': 10, 'pageLoad': 30, 'script': 30})
|
||||
self.timeouts['implicit'] *= 1000
|
||||
self.timeouts['pageLoad'] *= 1000
|
||||
@ -496,6 +501,11 @@ class DriverOptions(Options):
|
||||
"""浏览器启动文件路径"""
|
||||
return self.binary_location or 'chrome'
|
||||
|
||||
@property
|
||||
def user_data_path(self) -> str:
|
||||
"""返回用户文件夹路径"""
|
||||
return self._user_data_path
|
||||
|
||||
# -------------重写父类方法,实现链式操作-------------
|
||||
def add_argument(self, argument) -> 'DriverOptions':
|
||||
"""添加一个配置项 \n
|
||||
@ -727,10 +737,10 @@ class DriverOptions(Options):
|
||||
:return: 当前对象
|
||||
"""
|
||||
if driver_path is not None:
|
||||
self._driver_path = driver_path
|
||||
self._driver_path = str(driver_path)
|
||||
|
||||
if chrome_path is not None:
|
||||
self.binary_location = chrome_path
|
||||
self.binary_location = str(chrome_path)
|
||||
|
||||
if local_port is not None:
|
||||
self.debugger_address = '' if local_port == '' else f'127.0.0.1:{local_port}'
|
||||
@ -739,13 +749,14 @@ class DriverOptions(Options):
|
||||
self.debugger_address = debugger_address
|
||||
|
||||
if download_path is not None:
|
||||
self.experimental_options['prefs']['download.default_directory'] = download_path
|
||||
self.experimental_options['prefs']['download.default_directory'] = str(download_path)
|
||||
|
||||
if user_data_path is not None:
|
||||
self.set_argument('--user-data-dir', user_data_path)
|
||||
self.set_argument('--user-data-dir', str(user_data_path))
|
||||
self._user_data_path = user_data_path
|
||||
|
||||
if cache_path is not None:
|
||||
self.set_argument('--disk-cache-dir', cache_path)
|
||||
self.set_argument('--disk-cache-dir', str(cache_path))
|
||||
|
||||
return self
|
||||
|
||||
|
@ -5,11 +5,10 @@ tmp_path =
|
||||
[chrome_options]
|
||||
debugger_address = 127.0.0.1:9222
|
||||
binary_location = chrome
|
||||
arguments = ['--no-sandbox', '--disable-gpu', '--ignore-certificate-errors', '--disable-infobars', '--disable-popup-blocking']
|
||||
arguments = ['--no-first-run', '--no-sandbox', '--disable-gpu', '--ignore-certificate-errors', '--disable-infobars', '--disable-popup-blocking']
|
||||
extensions = []
|
||||
experimental_options = {'prefs': {'profile.default_content_settings.popups': 0, 'profile.default_content_setting_values': {'notifications': 2}, 'plugins.plugins_list': [{'enabled': False, 'name': 'Chrome PDF Viewer'}]}, 'useAutomationExtension': False, 'excludeSwitches': ['enable-automation']}
|
||||
timeouts = {'implicit': 10.0, 'pageLoad': 30.0, 'script': 30.0}
|
||||
set_window_rect = None
|
||||
page_load_strategy = normal
|
||||
|
||||
[session_options]
|
||||
|
@ -7,15 +7,15 @@ from requests import Session, Response
|
||||
from requests.structures import CaseInsensitiveDict
|
||||
from tldextract import extract
|
||||
|
||||
from .chrome_element import ChromeElement
|
||||
from .chromium_element import ChromiumElement
|
||||
from .session_element import SessionElement
|
||||
from .base import BasePage
|
||||
from .config import DriverOptions, SessionOptions, _cookies_to_tuple
|
||||
from .chrome_page import ChromePage
|
||||
from .chromium_page import ChromiumPage
|
||||
from .session_page import SessionPage
|
||||
|
||||
|
||||
class WebPage(SessionPage, ChromePage, BasePage):
|
||||
class WebPage(SessionPage, ChromiumPage, BasePage):
|
||||
"""整合浏览器和request的页面类"""
|
||||
|
||||
def __init__(self,
|
||||
@ -34,7 +34,7 @@ class WebPage(SessionPage, ChromePage, BasePage):
|
||||
if self._mode not in ('s', 'd'):
|
||||
raise ValueError('mode参数只能是s或d。')
|
||||
|
||||
super(ChromePage, self).__init__(timeout) # 调用Base的__init__()
|
||||
super(ChromiumPage, self).__init__(timeout) # 调用Base的__init__()
|
||||
self._session = None
|
||||
self._driver = None
|
||||
self._set_session_options(session_or_options)
|
||||
@ -44,11 +44,11 @@ class WebPage(SessionPage, ChromePage, BasePage):
|
||||
self._response = None
|
||||
|
||||
if self._mode == 'd':
|
||||
self.driver
|
||||
d = self.driver
|
||||
|
||||
def __call__(self,
|
||||
loc_or_str: Union[Tuple[str, str], str, ChromeElement, SessionElement],
|
||||
timeout: float = None) -> Union[ChromeElement, SessionElement, None]:
|
||||
loc_or_str: Union[Tuple[str, str], str, ChromiumElement, SessionElement],
|
||||
timeout: float = None) -> Union[ChromiumElement, SessionElement, None]:
|
||||
"""在内部查找元素 \n
|
||||
例:ele = page('@id=ele_id') \n
|
||||
:param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
|
||||
@ -152,8 +152,8 @@ class WebPage(SessionPage, ChromePage, BasePage):
|
||||
return super().get(url, show_errmsg, retry, interval, timeout, **kwargs)
|
||||
|
||||
def ele(self,
|
||||
loc_or_ele: Union[Tuple[str, str], str, ChromeElement, SessionElement],
|
||||
timeout: float = None) -> Union[ChromeElement, SessionElement, str, None]:
|
||||
loc_or_ele: Union[Tuple[str, str], str, ChromiumElement, SessionElement],
|
||||
timeout: float = None) -> Union[ChromiumElement, SessionElement, str, None]:
|
||||
"""返回第一个符合条件的元素、属性或节点文本 \n
|
||||
:param loc_or_ele: 元素的定位信息,可以是元素对象,loc元组,或查询字符串
|
||||
:param timeout: 查找元素超时时间,默认与页面等待时间一致
|
||||
@ -166,7 +166,7 @@ class WebPage(SessionPage, ChromePage, BasePage):
|
||||
|
||||
def eles(self,
|
||||
loc_or_str: Union[Tuple[str, str], str],
|
||||
timeout: float = None) -> List[Union[ChromeElement, SessionElement, str]]:
|
||||
timeout: float = None) -> List[Union[ChromiumElement, SessionElement, str]]:
|
||||
"""返回页面中所有符合条件的元素、属性或节点文本 \n
|
||||
:param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
|
||||
:param timeout: 查找元素超时时间,默认与页面等待时间一致
|
||||
@ -177,7 +177,7 @@ class WebPage(SessionPage, ChromePage, BasePage):
|
||||
elif self._mode == 'd':
|
||||
return super(SessionPage, self).eles(loc_or_str, timeout=timeout)
|
||||
|
||||
def s_ele(self, loc_or_ele: Union[Tuple[str, str], str, ChromeElement, SessionElement] = None) \
|
||||
def s_ele(self, loc_or_ele: Union[Tuple[str, str], str, ChromiumElement, SessionElement] = None) \
|
||||
-> Union[SessionElement, str, None]:
|
||||
"""查找第一个符合条件的元素以SessionElement形式返回,d模式处理复杂页面时效率很高 \n
|
||||
:param loc_or_ele: 元素的定位信息,可以是loc元组,或查询字符串
|
||||
@ -214,7 +214,7 @@ class WebPage(SessionPage, ChromePage, BasePage):
|
||||
# s模式转d模式
|
||||
if self._mode == 'd':
|
||||
if not self._has_driver:
|
||||
self.driver
|
||||
d = self.driver
|
||||
self._url = None if not self._has_driver else super(SessionPage, self).url
|
||||
self._has_driver = True
|
||||
|
||||
@ -353,10 +353,10 @@ class WebPage(SessionPage, ChromePage, BasePage):
|
||||
return super().download
|
||||
|
||||
def _ele(self,
|
||||
loc_or_ele: Union[Tuple[str, str], str, ChromeElement, SessionElement],
|
||||
loc_or_ele: Union[Tuple[str, str], str, ChromiumElement, SessionElement],
|
||||
timeout: float = None, single: bool = True) \
|
||||
-> Union[ChromeElement, SessionElement, str, None, List[Union[SessionElement, str]], List[
|
||||
Union[ChromeElement, str]]]:
|
||||
-> Union[ChromiumElement, SessionElement, str, None, List[Union[SessionElement, str]], List[
|
||||
Union[ChromiumElement, str]]]:
|
||||
"""返回页面中符合条件的元素、属性或节点文本,默认返回第一个 \n
|
||||
:param loc_or_ele: 元素的定位信息,可以是元素对象,loc元组,或查询字符串
|
||||
:param timeout: 查找元素超时时间,d模式专用
|
||||
|
40
README.md
40
README.md
@ -4,8 +4,8 @@ DrissionPage,即 driver 和 session 组合而成的 page。
|
||||
是个基于 python 的 Web 自动化操作集成工具。
|
||||
它用 POM 模式封装了页面和元素常用的方法,
|
||||
自带一套简洁直观优雅的元素定位语法,
|
||||
实现了 selenium 和 requests 之间的无缝切换,
|
||||
可兼顾 selenium 的便利性和 requests 的高效率,
|
||||
实现了浏览器和 requests 之间的无缝切换,
|
||||
可兼顾浏览器自动化的便利性和 requests 的高效率,
|
||||
更棒的是,它的使用方式非常简洁和人性化,代码量少,对新手友好。
|
||||
|
||||
**使用文档:** 📒[点击打开](http://g1879.gitee.io/drissionpage)
|
||||
@ -17,10 +17,35 @@ DrissionPage,即 driver 和 session 组合而成的 page。
|
||||
## 📕 背景
|
||||
|
||||
用 requests 做数据采集面对要登录的网站时,要分析数据包、JS 源码,构造复杂的请求,往往还要应付验证码、JS 混淆、签名参数等反爬手段,门槛较高。若数据是由 JS 计算生成的,还须重现计算过程,体验不好,开发效率不高。
|
||||
使用 selenium,可以很大程度上绕过这些坑,但 selenium 效率不高。因此,这个库将 selenium 和 requests 合而为一,不同须要时切换相应模式,并提供一种人性化的使用方法,提高开发和运行效率。
|
||||
除了合并两者,本库还以网页为单位封装了常用功能,简化了 selenium 的操作和语句,在用于网页自动化操作时,减少考虑细节,专注功能实现,使用更方便。
|
||||
使用浏览器,可以很大程度上绕过这些坑,但浏览器运行效率不高。因此,这个库将它们合而为一,不同须要时切换相应模式,并提供一种人性化的使用方法,提高开发和运行效率。
|
||||
除了合并两者,本库还以网页为单位封装了常用功能,提供非常简便的操作和语句,在用于网页自动化操作时,减少考虑细节,专注功能实现,使用更方便。
|
||||
一切从简,尽量提供简单直接的使用方法,对新手更友好。
|
||||
|
||||
# 🔆 3.0 版隆重推出
|
||||
|
||||
以前的版本是对 selenium 进行重新封装实现的。从 3.0 开始,作者另起炉灶,对底层进行了重新开发,摆脱对 selenium 的依赖,增强了功能,提升了运行效率。
|
||||
3.0 全新开发的页面对象是`WebPage`,支持 chromium 内核的浏览器(如 chrome 和 edge)。除了保持之前的功能,比依赖 selenium 的`MixPage`有以下优点:
|
||||
|
||||
- 无 webdriver 特征,不会被网站识别
|
||||
|
||||
- 无需为不同版本的浏览器下载不同的驱动
|
||||
|
||||
- 运行速度更快
|
||||
|
||||
- 可以跨 iframe 查找元素,无需切入切出
|
||||
|
||||
- 把 iframe 看作普通元素,获取后可直接在其中查找元素,逻辑更清晰
|
||||
|
||||
- 可以同时操作浏览器中的多个标签页,即使标签页为非激活状态
|
||||
|
||||
- 可以直接读取浏览器缓存来保持图片,无需用 GUI 点击保存
|
||||
|
||||
- 可以对整个网页截图,包括视口外的部分(90以上版本浏览器支持)
|
||||
|
||||
新版是自己实现的功能,开发不会受太多限制,以后将主要对`WebPage`进行更新。
|
||||
|
||||
3.0 版已经发布,目前正在测试,欢迎试用并提出意见,让我做得更好。
|
||||
|
||||
# 💡 特性和亮点
|
||||
|
||||
作者踩过无数坑,总结出的经验全写到这个库里了。内置了 N 多实用功能,对常用功能作了整合和优化。
|
||||
@ -28,7 +53,7 @@ DrissionPage,即 driver 和 session 组合而成的 page。
|
||||
## 🎉 特性
|
||||
|
||||
- 代码高度集成,以简洁的代码为第一追求。
|
||||
- 页面对象可在 selenium 和 requests 模式间任意切换,保留登录状态。
|
||||
- 页面对象可在浏览器和 requests 间任意切换,保留登录状态。
|
||||
- 极简单但强大的元素定位语法,支持链式操作,代码极其简洁。
|
||||
- 两种模式提供一致的 API,使用体验一致。
|
||||
- 人性化设计,集成众多实用功能,大大降低开发工作量。
|
||||
@ -160,8 +185,7 @@ ele.click()
|
||||
🌿 用 xpath 直接获取属性或文本节点(返回文本)
|
||||
|
||||
```python
|
||||
# 使用 selenium:
|
||||
相当复杂
|
||||
# 使用 selenium 无此功能
|
||||
|
||||
# 使用 DrissionPage:
|
||||
class_name = element('xpath://div[@id="div_id"]/@class')
|
||||
@ -178,7 +202,7 @@ page.hide_browser() # 让浏览器窗口消失
|
||||
page.show_browser() # 重新显示浏览器窗口
|
||||
```
|
||||
|
||||
注:本功能只支持 Windows,且须设置了`local_port`或`debugger_address`参数时才能生效。
|
||||
注:本功能只支持 Windows。
|
||||
|
||||
☘️ **与 requests 代码对比**
|
||||
|
||||
|
@ -4,12 +4,10 @@ DrissionPage,即 driver 和 session 组合而成的 page。
|
||||
是个基于 python 的 Web 自动化操作集成工具。
|
||||
它用 POM 模式封装了页面和元素常用的方法,
|
||||
自带一套简洁直观优雅的元素定位语法,
|
||||
实现了 selenium 和 requests 之间的无缝切换,
|
||||
可兼顾 selenium 的便利性和 requests 的高效率,
|
||||
实现了浏览器和 requests 之间的无缝切换,
|
||||
可兼顾浏览器自动化的便利性和 requests 的高效率,
|
||||
更棒的是,它的使用方式非常简洁和人性化,代码量少,对新手友好。
|
||||
|
||||
|
||||
|
||||
**交流QQ群:** 897838127
|
||||
|
||||
**联系邮箱:** g1879@qq.com
|
||||
@ -17,11 +15,36 @@ DrissionPage,即 driver 和 session 组合而成的 page。
|
||||
## 📕 背景
|
||||
|
||||
用 requests 做数据采集面对要登录的网站时,要分析数据包、JS 源码,构造复杂的请求,往往还要应付验证码、JS 混淆、签名参数等反爬手段,门槛较高。若数据是由 JS 计算生成的,还须重现计算过程,体验不好,开发效率不高。
|
||||
使用 selenium,可以很大程度上绕过这些坑,但 selenium 效率不高。因此,这个库将 selenium 和 requests 合而为一,不同须要时切换相应模式,并提供一种人性化的使用方法,提高开发和运行效率。
|
||||
除了合并两者,本库还以网页为单位封装了常用功能,简化了 selenium 的操作和语句,在用于网页自动化操作时,减少考虑细节,专注功能实现,使用更方便。
|
||||
使用浏览器,可以很大程度上绕过这些坑,但浏览器运行效率不高。因此,这个库将它们合而为一,不同须要时切换相应模式,并提供一种人性化的使用方法,提高开发和运行效率。
|
||||
除了合并两者,本库还以网页为单位封装了常用功能,提供非常简便的操作和语句,在用于网页自动化操作时,减少考虑细节,专注功能实现,使用更方便。
|
||||
一切从简,尽量提供简单直接的使用方法,对新手更友好。
|
||||
|
||||
# 🍀 特性和亮点
|
||||
# 🔆 3.0 版隆重推出
|
||||
|
||||
以前的版本是对 selenium 进行重新封装实现的。从 3.0 开始,作者另起炉灶,对底层进行了重新开发,摆脱对 selenium 的依赖,增强了功能,提升了运行效率。
|
||||
3.0 全新开发的页面对象是`WebPage`,支持 chromium 内核的浏览器(如 chrome 和 edge)。除了保持之前的功能,比依赖 selenium 的`MixPage`有以下优点:
|
||||
|
||||
- 无 webdriver 特征,不会被网站识别
|
||||
|
||||
- 无需为不同版本的浏览器下载不同的驱动
|
||||
|
||||
- 运行速度更快
|
||||
|
||||
- 可以跨 iframe 查找元素,无需切入切出
|
||||
|
||||
- 把 iframe 看作普通元素,获取后可直接在其中查找元素,逻辑更清晰
|
||||
|
||||
- 可以同时操作浏览器中的多个标签页,即使标签页为非激活状态
|
||||
|
||||
- 可以直接读取浏览器缓存来保持图片,无需用 GUI 点击保存
|
||||
|
||||
- 可以对整个网页截图,包括视口外的部分(90以上版本浏览器支持)
|
||||
|
||||
新版是自己实现的功能,开发不会受太多限制,以后将主要对`WebPage`进行更新。
|
||||
|
||||
3.0 版已经发布,目前正在测试,欢迎试用并提出意见,让我做得更好。
|
||||
|
||||
# 💡 特性和亮点
|
||||
|
||||
作者踩过无数坑,总结出的经验全写到这个库里了。内置了 N 多实用功能,对常用功能作了整合和优化。
|
||||
|
||||
|
32
docs/版本历史.md
32
docs/版本历史.md
@ -1,4 +1,34 @@
|
||||
# 2.7.3
|
||||
# v3.0.0
|
||||
|
||||
重大更新。推出`WebPage`,重新开发底层逻辑,摆脱对 selenium 的依赖,增强了功能,提升了运行效率。支持 chromium 内核的浏览器(如 chrome 和 edge)。比`MixPage`有以下优点:
|
||||
|
||||
- 无 webdriver 特征,不会被网站识别
|
||||
|
||||
- 无需为不同版本的浏览器下载不同的驱动
|
||||
|
||||
- 运行速度更快
|
||||
|
||||
- 可以跨 iframe 查找元素,无需切入切出
|
||||
|
||||
- 把 iframe 看作普通元素,获取后可直接在其中查找元素,逻辑更清晰
|
||||
|
||||
- 可以同时操作浏览器中的多个标签页,即使标签页为非激活状态
|
||||
|
||||
- 可以直接读取浏览器缓存来保持图片,无需用 GUI 点击保存
|
||||
|
||||
- 可以对整个网页截图,包括视口外的部分(90以上版本浏览器支持)
|
||||
|
||||
其它更新:
|
||||
|
||||
- 新增与`WebPage`配合的动作链接`ActionChains`
|
||||
|
||||
- ini 文件和`DriverOption`删除`set_window_rect`属性
|
||||
|
||||
- 浏览器启动配置实现对插件的支持
|
||||
|
||||
- 浏览器启动配置实现对`experimental_options`的`prefs`属性支持
|
||||
|
||||
# v2.7.3
|
||||
|
||||
- 页面对象和元素对象的`screenshot_as_bytes()`方法合并到`screenshot()`
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user