This commit is contained in:
g1879 2022-11-12 22:17:19 +08:00
parent c4977a1d8c
commit 3be47a80ca
10 changed files with 300 additions and 159 deletions

View File

@ -36,6 +36,8 @@ class ActionChains:
ele_loc = ele_or_loc.location if offset_x or offset_y else ele_or_loc.midpoint ele_loc = ele_or_loc.location if offset_x or offset_y else ele_or_loc.midpoint
lx = ele_loc['x'] + offset_x lx = ele_loc['x'] + offset_x
ly = ele_loc['y'] + offset_y ly = ele_loc['y'] + offset_y
else:
raise TypeError('ele_or_loc参数只能接受坐标(x, y)或ChromiumElement对象。')
if not _location_in_viewport(self.page, lx, ly): if not _location_in_viewport(self.page, lx, ly):
self.page.scroll.to_location(lx, ly) self.page.scroll.to_location(lx, ly)

View File

@ -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 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页面对象中的元素对象""" """ChromePage页面对象中的元素对象"""
def __init__(self, page, node_id: str = None, obj_id: str = None): def __init__(self, page, node_id: str = None, obj_id: str = None):
@ -46,7 +46,7 @@ class ChromeElement(DrissionElement):
def __call__(self, def __call__(self,
loc_or_str: Union[Tuple[str, str], str], loc_or_str: Union[Tuple[str, str], str],
timeout: float = None) -> Union['ChromeElement', str, None]: timeout: float = None) -> Union['ChromiumElement', str, None]:
"""在内部查找元素 \n """在内部查找元素 \n
ele2 = ele1('@id=ele_id') \n ele2 = ele1('@id=ele_id') \n
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串 :param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
@ -98,7 +98,7 @@ class ChromeElement(DrissionElement):
"""返回未格式化处理的元素内文本""" """返回未格式化处理的元素内文本"""
return self.prop('innerText') return self.prop('innerText')
# -----------------driver独有属性------------------- # -----------------d模式独有属性-------------------
@property @property
def obj_id(self) -> str: def obj_id(self) -> str:
"""返回js中的object id""" """返回js中的object id"""
@ -193,7 +193,7 @@ class ChromeElement(DrissionElement):
self._scroll = ChromeScroll(self) self._scroll = ChromeScroll(self)
return self._scroll 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 """返回上面某一级父元素,可指定层数或用查询语法定位 \n
:param level_or_loc: 第几级父元素或定位符 :param level_or_loc: 第几级父元素或定位符
:return: 上级元素对象 :return: 上级元素对象
@ -203,7 +203,7 @@ class ChromeElement(DrissionElement):
def prev(self, def prev(self,
index: int = 1, index: int = 1,
filter_loc: Union[tuple, str] = '', filter_loc: Union[tuple, str] = '',
timeout: float = 0) -> Union['ChromeElement', str, None]: timeout: float = 0) -> Union['ChromiumElement', str, None]:
"""返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n
:param index: 前面第几个查询结果元素 :param index: 前面第几个查询结果元素
:param filter_loc: 用于筛选元素的查询语法 :param filter_loc: 用于筛选元素的查询语法
@ -215,7 +215,7 @@ class ChromeElement(DrissionElement):
def next(self, def next(self,
index: int = 1, index: int = 1,
filter_loc: Union[tuple, str] = '', filter_loc: Union[tuple, str] = '',
timeout: float = 0) -> Union['ChromeElement', str, None]: timeout: float = 0) -> Union['ChromiumElement', str, None]:
"""返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n
:param index: 后面第几个查询结果元素 :param index: 后面第几个查询结果元素
:param filter_loc: 用于筛选元素的查询语法 :param filter_loc: 用于筛选元素的查询语法
@ -227,7 +227,7 @@ class ChromeElement(DrissionElement):
def before(self, def before(self,
index: int = 1, index: int = 1,
filter_loc: Union[tuple, str] = '', filter_loc: Union[tuple, str] = '',
timeout: float = None) -> Union['ChromeElement', str, None]: timeout: float = None) -> Union['ChromiumElement', str, None]:
"""返回当前元素前面的一个元素可指定筛选条件和第几个。查找范围不限兄弟元素而是整个DOM文档 \n """返回当前元素前面的一个元素可指定筛选条件和第几个。查找范围不限兄弟元素而是整个DOM文档 \n
:param index: 前面第几个查询结果元素 :param index: 前面第几个查询结果元素
:param filter_loc: 用于筛选元素的查询语法 :param filter_loc: 用于筛选元素的查询语法
@ -239,7 +239,7 @@ class ChromeElement(DrissionElement):
def after(self, def after(self,
index: int = 1, index: int = 1,
filter_loc: Union[tuple, str] = '', filter_loc: Union[tuple, str] = '',
timeout: float = None) -> Union['ChromeElement', str, None]: timeout: float = None) -> Union['ChromiumElement', str, None]:
"""返回当前元素后面的一个元素可指定筛选条件和第几个。查找范围不限兄弟元素而是整个DOM文档 \n """返回当前元素后面的一个元素可指定筛选条件和第几个。查找范围不限兄弟元素而是整个DOM文档 \n
:param index: 后面第几个查询结果元素 :param index: 后面第几个查询结果元素
:param filter_loc: 用于筛选元素的查询语法 :param filter_loc: 用于筛选元素的查询语法
@ -250,7 +250,7 @@ class ChromeElement(DrissionElement):
def prevs(self, def prevs(self,
filter_loc: Union[tuple, str] = '', filter_loc: Union[tuple, str] = '',
timeout: float = 0) -> List[Union['ChromeElement', str]]: timeout: float = 0) -> List[Union['ChromiumElement', str]]:
"""返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n
:param filter_loc: 用于筛选元素的查询语法 :param filter_loc: 用于筛选元素的查询语法
:param timeout: 查找元素的超时时间 :param timeout: 查找元素的超时时间
@ -260,7 +260,7 @@ class ChromeElement(DrissionElement):
def nexts(self, def nexts(self,
filter_loc: Union[tuple, str] = '', filter_loc: Union[tuple, str] = '',
timeout: float = 0) -> List[Union['ChromeElement', str]]: timeout: float = 0) -> List[Union['ChromiumElement', str]]:
"""返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n
:param filter_loc: 用于筛选元素的查询语法 :param filter_loc: 用于筛选元素的查询语法
:param timeout: 查找元素的超时时间 :param timeout: 查找元素的超时时间
@ -270,7 +270,7 @@ class ChromeElement(DrissionElement):
def befores(self, def befores(self,
filter_loc: Union[tuple, str] = '', filter_loc: Union[tuple, str] = '',
timeout: float = None) -> List[Union['ChromeElement', str]]: timeout: float = None) -> List[Union['ChromiumElement', str]]:
"""返回当前元素后面符合条件的全部兄弟元素或节点组成的列表可用查询语法筛选。查找范围不限兄弟元素而是整个DOM文档 \n """返回当前元素后面符合条件的全部兄弟元素或节点组成的列表可用查询语法筛选。查找范围不限兄弟元素而是整个DOM文档 \n
:param filter_loc: 用于筛选元素的查询语法 :param filter_loc: 用于筛选元素的查询语法
:param timeout: 查找元素的超时时间 :param timeout: 查找元素的超时时间
@ -279,7 +279,7 @@ class ChromeElement(DrissionElement):
return super().befores(filter_loc, timeout) return super().befores(filter_loc, timeout)
def wait_ele(self, def wait_ele(self,
loc_or_ele: Union[str, tuple, 'ChromeElement'], loc_or_ele: Union[str, tuple, 'ChromiumElement'],
timeout: float = None) -> 'ChromeElementWaiter': timeout: float = None) -> 'ChromeElementWaiter':
"""返回用于等待子元素到达某个状态的等待器对象 \n """返回用于等待子元素到达某个状态的等待器对象 \n
:param loc_or_ele: 可以是元素查询字符串loc元组 :param loc_or_ele: 可以是元素查询字符串loc元组
@ -320,7 +320,7 @@ class ChromeElement(DrissionElement):
def is_alive(self) -> bool: def is_alive(self) -> bool:
"""返回元素是否仍在DOM中""" """返回元素是否仍在DOM中"""
try: try:
self.attrs d = self.attrs
return True return True
except Exception: except Exception:
return False return False
@ -362,6 +362,50 @@ class ChromeElement(DrissionElement):
else: else:
return attrs.get(attr, None) 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: def run_script(self, script: str, as_expr: bool = False, *args: Any) -> Any:
"""运行javascript代码 \n """运行javascript代码 \n
:param script: js文本 :param script: js文本
@ -383,7 +427,7 @@ class ChromeElement(DrissionElement):
def ele(self, def ele(self,
loc_or_str: Union[Tuple[str, str], str], loc_or_str: Union[Tuple[str, str], str],
timeout: float = None) -> Union['ChromeElement', str, None]: timeout: float = None) -> Union['ChromiumElement', str, None]:
"""返回当前元素下级符合条件的第一个元素、属性或节点文本 \n """返回当前元素下级符合条件的第一个元素、属性或节点文本 \n
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串 :param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 查找元素超时时间默认与元素所在页面等待时间一致 :param timeout: 查找元素超时时间默认与元素所在页面等待时间一致
@ -393,7 +437,7 @@ class ChromeElement(DrissionElement):
def eles(self, def eles(self,
loc_or_str: Union[Tuple[str, str], str], loc_or_str: Union[Tuple[str, str], str],
timeout: float = None) -> List[Union['ChromeElement', str]]: timeout: float = None) -> List[Union['ChromiumElement', str]]:
"""返回当前元素下级所有符合条件的子元素、属性或节点文本 \n """返回当前元素下级所有符合条件的子元素、属性或节点文本 \n
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串 :param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 查找元素超时时间默认与元素所在页面等待时间一致 :param timeout: 查找元素超时时间默认与元素所在页面等待时间一致
@ -422,7 +466,7 @@ class ChromeElement(DrissionElement):
def _ele(self, def _ele(self,
loc_or_str: Union[Tuple[str, str], str], loc_or_str: Union[Tuple[str, str], str],
timeout: float = None, 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 """返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个 \n
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串 :param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 查找元素超时时间 :param timeout: 查找元素超时时间
@ -431,43 +475,6 @@ class ChromeElement(DrissionElement):
""" """
return make_chrome_ele(self, loc_or_str, single, timeout) 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: def style(self, style: str, pseudo_ele: str = '') -> str:
"""返回元素样式属性值,可获取伪元素属性值 \n """返回元素样式属性值,可获取伪元素属性值 \n
:param style: 样式属性名称 :param style: 样式属性名称
@ -612,7 +619,7 @@ class ChromeElement(DrissionElement):
if not by_js: if not by_js:
self.page.scroll_to_see(self) self.page.scroll_to_see(self)
if self.is_in_view: if self.is_in_viewport:
midpoint = self.midpoint midpoint = self.midpoint
client_midpoint = self.client_midpoint client_midpoint = self.client_midpoint
client_x = client_midpoint['x'] client_x = client_midpoint['x']
@ -698,7 +705,7 @@ class ChromeElement(DrissionElement):
self.drag_to((offset_x, offset_y), speed, shake) self.drag_to((offset_x, offset_y), speed, shake)
def drag_to(self, def drag_to(self,
ele_or_loc: Union[tuple, 'ChromeElement'], ele_or_loc: Union[tuple, 'ChromiumElement'],
speed: int = 40, speed: int = 40,
shake: bool = True) -> None: shake: bool = True) -> None:
"""拖拽当前元素,目标为另一个元素或坐标元组 \n """拖拽当前元素,目标为另一个元素或坐标元组 \n
@ -708,7 +715,7 @@ class ChromeElement(DrissionElement):
:return: None :return: None
""" """
# x, y目标点坐标 # x, y目标点坐标
if isinstance(ele_or_loc, ChromeElement): if isinstance(ele_or_loc, ChromiumElement):
midpoint = ele_or_loc.midpoint midpoint = ele_or_loc.midpoint
target_x = midpoint['x'] target_x = midpoint['x']
target_y = midpoint['y'] target_y = midpoint['y']
@ -802,7 +809,7 @@ class ChromeElement(DrissionElement):
class ChromeShadowRootElement(BaseElement): class ChromeShadowRootElement(BaseElement):
"""ChromeShadowRootElement是用于处理ShadowRoot的类使用方法和ChromeElement基本一致""" """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) super().__init__(parent_ele.page)
self.parent_ele = parent_ele self.parent_ele = parent_ele
self._node_id = self._get_node_id(obj_id) self._node_id = self._get_node_id(obj_id)
@ -813,7 +820,7 @@ class ChromeShadowRootElement(BaseElement):
def __call__(self, def __call__(self,
loc_or_str: Union[Tuple[str, str], str], loc_or_str: Union[Tuple[str, str], str],
timeout: float = None) -> Union[ChromeElement, None]: timeout: float = None) -> Union[ChromiumElement, None]:
"""在内部查找元素 \n """在内部查找元素 \n
ele2 = ele1('@id=ele_id') \n ele2 = ele1('@id=ele_id') \n
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串 :param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
@ -880,7 +887,7 @@ class ChromeShadowRootElement(BaseElement):
from threading import Thread from threading import Thread
Thread(target=_run_script, args=(self, script, as_expr, self.page.timeouts.script, args)).start() 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 """返回上面某一级父元素,可指定层数或用查询语法定位 \n
:param level_or_loc: 第几级父元素或定位符 :param level_or_loc: 第几级父元素或定位符
:return: ChromeElement对象 :return: ChromeElement对象
@ -903,7 +910,7 @@ class ChromeShadowRootElement(BaseElement):
def next(self, def next(self,
index: int = 1, index: int = 1,
filter_loc: Union[tuple, str] = '') -> Union[ChromeElement, str, None]: filter_loc: Union[tuple, str] = '') -> Union[ChromiumElement, str, None]:
"""返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n
:param index: 第几个查询结果元素 :param index: 第几个查询结果元素
:param filter_loc: 用于筛选元素的查询语法 :param filter_loc: 用于筛选元素的查询语法
@ -914,7 +921,7 @@ class ChromeShadowRootElement(BaseElement):
def before(self, def before(self,
index: int = 1, index: int = 1,
filter_loc: Union[tuple, str] = '') -> Union[ChromeElement, str, None]: filter_loc: Union[tuple, str] = '') -> Union[ChromiumElement, str, None]:
"""返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n
:param index: 前面第几个查询结果元素 :param index: 前面第几个查询结果元素
:param filter_loc: 用于筛选元素的查询语法 :param filter_loc: 用于筛选元素的查询语法
@ -924,7 +931,7 @@ class ChromeShadowRootElement(BaseElement):
return nodes[index - 1] if nodes else None return nodes[index - 1] if nodes else None
def after(self, index: int = 1, 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 """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n
:param index: 后面第几个查询结果元素 :param index: 后面第几个查询结果元素
:param filter_loc: 用于筛选元素的查询语法 :param filter_loc: 用于筛选元素的查询语法
@ -933,7 +940,7 @@ class ChromeShadowRootElement(BaseElement):
nodes = self.afters(filter_loc=filter_loc) nodes = self.afters(filter_loc=filter_loc)
return nodes[index - 1] if nodes else None 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 """返回后面所有兄弟元素或节点组成的列表 \n
:param filter_loc: 用于筛选元素的查询语法 :param filter_loc: 用于筛选元素的查询语法
:return: ChromeElement对象组成的列表 :return: ChromeElement对象组成的列表
@ -946,7 +953,7 @@ class ChromeShadowRootElement(BaseElement):
xpath = f'xpath:./{loc}' xpath = f'xpath:./{loc}'
return self.parent_ele.eles(xpath, timeout=0.1) 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 """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n
:param filter_loc: 用于筛选元素的查询语法 :param filter_loc: 用于筛选元素的查询语法
:return: 本元素前面的元素或节点组成的列表 :return: 本元素前面的元素或节点组成的列表
@ -959,7 +966,7 @@ class ChromeShadowRootElement(BaseElement):
xpath = f'xpath:./preceding::{loc}' xpath = f'xpath:./preceding::{loc}'
return self.parent_ele.eles(xpath, timeout=0.1) 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 """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n
:param filter_loc: 用于筛选元素的查询语法 :param filter_loc: 用于筛选元素的查询语法
:return: 本元素后面的元素或节点组成的列表 :return: 本元素后面的元素或节点组成的列表
@ -971,7 +978,7 @@ class ChromeShadowRootElement(BaseElement):
def ele(self, def ele(self,
loc_or_str: Union[Tuple[str, str], str], loc_or_str: Union[Tuple[str, str], str],
timeout: float = None) -> Union[ChromeElement, None]: timeout: float = None) -> Union[ChromiumElement, None]:
"""返回当前元素下级符合条件的第一个元素 \n """返回当前元素下级符合条件的第一个元素 \n
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串 :param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 查找元素超时时间默认与元素所在页面等待时间一致 :param timeout: 查找元素超时时间默认与元素所在页面等待时间一致
@ -981,7 +988,7 @@ class ChromeShadowRootElement(BaseElement):
def eles(self, def eles(self,
loc_or_str: Union[Tuple[str, str], str], loc_or_str: Union[Tuple[str, str], str],
timeout: float = None) -> List[ChromeElement]: timeout: float = None) -> List[ChromiumElement]:
"""返回当前元素下级所有符合条件的子元素 \n """返回当前元素下级所有符合条件的子元素 \n
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串 :param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 查找元素超时时间默认与元素所在页面等待时间一致 :param timeout: 查找元素超时时间默认与元素所在页面等待时间一致
@ -1006,7 +1013,7 @@ class ChromeShadowRootElement(BaseElement):
def _ele(self, def _ele(self,
loc_or_str: Union[Tuple[str, str], str], loc_or_str: Union[Tuple[str, str], str],
timeout: float = None, timeout: float = None,
single: bool = True) -> Union['ChromeElement', None, List[ChromeElement]]: single: bool = True) -> Union['ChromiumElement', None, List[ChromiumElement]]:
"""返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个 \n """返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个 \n
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串 :param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 查找元素超时时间 :param timeout: 查找元素超时时间
@ -1029,14 +1036,14 @@ class ChromeShadowRootElement(BaseElement):
css_paths = [i.css_path[47:] for i in eles] css_paths = [i.css_path[47:] for i in eles]
if single: if single:
node_id = self.page.driver.DOM.querySelector(nodeId=self._node_id, selector=css_paths[0])['nodeId'] 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: else:
results = [] results = []
for i in css_paths: for i in css_paths:
node_id = self.page.driver.DOM.querySelector(nodeId=self._node_id, selector=i)['nodeId'] node_id = self.page.driver.DOM.querySelector(nodeId=self._node_id, selector=i)['nodeId']
if node_id: if node_id:
results.append(ChromeElement(self.page, node_id)) results.append(ChromiumElement(self.page, node_id))
return results return results
def _get_node_id(self, obj_id) -> str: 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'] 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]], loc: Union[str, Tuple[str, str]],
single: bool = True, 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 """在chrome元素中查找 \n
:param ele: ChromeElement对象 :param ele: ChromeElement对象
:param loc: 元素定位元组 :param loc: 元素定位元组
@ -1078,10 +1085,10 @@ def make_chrome_ele(ele: ChromeElement,
return _find_by_css(ele, loc[1], single, timeout) return _find_by_css(ele, loc[1], single, timeout)
def _find_by_xpath(ele: ChromeElement, def _find_by_xpath(ele: ChromiumElement,
xpath: str, xpath: str,
single: bool, single: bool,
timeout: float) -> Union[ChromeElement, List[ChromeElement]]: timeout: float) -> Union[ChromiumElement, List[ChromiumElement], None]:
"""执行用xpath在元素中查找元素 """执行用xpath在元素中查找元素
:param ele: 在此元素中查找 :param ele: 在此元素中查找
:param xpath: 查找语句 :param xpath: 查找语句
@ -1119,22 +1126,22 @@ def _find_by_xpath(ele: ChromeElement,
if r['result']['subtype'] == 'null': if r['result']['subtype'] == 'null':
return None return None
else: else:
return ChromeElement(ele.page, obj_id=r['result']['objectId']) return ChromiumElement(ele.page, obj_id=r['result']['objectId'])
else: else:
if r['result']['description'] == 'NodeList(0)': if r['result']['description'] == 'NodeList(0)':
return [] return []
else: else:
r = ele.page.driver.Runtime.getProperties(objectId=r['result']['objectId'], ownProperties=True)['result'] 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'] if i['value']['type'] == 'object' else i['value']['value']
for i in r[:-1]] for i in r[:-1]]
def _find_by_css(ele: ChromeElement, def _find_by_css(ele: ChromiumElement,
selector: str, selector: str,
single: bool, single: bool,
timeout: float) -> Union[ChromeElement, List[ChromeElement]]: timeout: float) -> Union[ChromiumElement, List[ChromiumElement], None]:
"""执行用css selector在元素中查找元素 """执行用css selector在元素中查找元素
:param ele: 在此元素中查找 :param ele: 在此元素中查找
:param selector: 查找语句 :param selector: 查找语句
@ -1163,14 +1170,14 @@ def _find_by_css(ele: ChromeElement,
if r['result']['subtype'] == 'null': if r['result']['subtype'] == 'null':
return None return None
else: else:
return ChromeElement(ele.page, obj_id=r['result']['objectId']) return ChromiumElement(ele.page, obj_id=r['result']['objectId'])
else: else:
if r['result']['description'] == 'NodeList(0)': if r['result']['description'] == 'NodeList(0)':
return [] return []
else: else:
r = ele.page.driver.Runtime.getProperties(objectId=r['result']['objectId'], ownProperties=True)['result'] 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: 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]... :param args: 参数按顺序在js文本中对应argument[0]argument[2]...
:return: js执行结果 :return: js执行结果
""" """
if isinstance(page_or_ele, (ChromeElement, ChromeShadowRootElement)): if isinstance(page_or_ele, (ChromiumElement, ChromeShadowRootElement)):
page = page_or_ele.page page = page_or_ele.page
obj_id = page_or_ele.obj_id obj_id = page_or_ele.obj_id
else: else:
@ -1271,7 +1278,7 @@ def _parse_js_result(page, ele, result: dict):
if result['className'] == 'ShadowRoot': if result['className'] == 'ShadowRoot':
return ChromeShadowRootElement(ele, obj_id=result['objectId']) return ChromeShadowRootElement(ele, obj_id=result['objectId'])
else: else:
return ChromeElement(page, obj_id=result['objectId']) return ChromiumElement(page, obj_id=result['objectId'])
elif sub_type == 'array': elif sub_type == 'array':
r = page.driver.Runtime.getProperties(objectId=result['result']['objectId'], ownProperties=True)['result'] 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: def _convert_argument(arg: Any) -> dict:
"""把参数转换成js能够接收的形式""" """把参数转换成js能够接收的形式"""
if isinstance(arg, ChromeElement): if isinstance(arg, ChromiumElement):
return {'objectId': arg.obj_id} return {'objectId': arg.obj_id}
elif isinstance(arg, (int, float, str, bool)): elif isinstance(arg, (int, float, str, bool)):
@ -1302,9 +1309,8 @@ def _convert_argument(arg: Any) -> dict:
return {'unserializableValue': '-Infinity'} 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', data = {'type': 'keyDown', 'modifiers': 0, 'windowsVirtualKeyCode': 13, 'code': 'Enter', 'key': 'Enter',
'text': '\r', 'autoRepeat': False, 'unmodifiedText': '\r', 'location': 0, 'isKeypad': False} '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) 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: if key not in _keyDefinitions:
ele.page.run_cdp('Input.insertText', text=key) ele.page.run_cdp('Input.insertText', text=key)
@ -1365,7 +1371,7 @@ class ChromeScroll(object):
""" """
:param page_or_ele: ChromePage或ChromeElement :param page_or_ele: ChromePage或ChromeElement
""" """
if isinstance(page_or_ele, ChromeElement): if isinstance(page_or_ele, ChromiumElement):
self.t1 = self.t2 = 'this' self.t1 = self.t2 = 'this'
self.obj_id = page_or_ele.obj_id self.obj_id = page_or_ele.obj_id
self.page = page_or_ele.page self.page = page_or_ele.page
@ -1444,7 +1450,7 @@ class ChromeScroll(object):
class ChromeSelect(object): class ChromeSelect(object):
"""ChromeSelect 类专门用于处理 d 模式下 select 标签""" """ChromeSelect 类专门用于处理 d 模式下 select 标签"""
def __init__(self, ele: ChromeElement): def __init__(self, ele: ChromiumElement):
"""初始化 \n """初始化 \n
:param ele: select 元素对象 :param ele: select 元素对象
""" """
@ -1470,12 +1476,12 @@ class ChromeSelect(object):
return multi and multi.lower() != "false" return multi and multi.lower() != "false"
@property @property
def options(self) -> List[ChromeElement]: def options(self) -> List[ChromiumElement]:
"""返回所有选项元素组成的列表""" """返回所有选项元素组成的列表"""
return self._ele.eles('tag:option') return self._ele.eles('tag:option')
@property @property
def selected_option(self) -> Union[ChromeElement, None]: def selected_option(self) -> Union[ChromiumElement, None]:
"""返回第一个被选中的option元素 \n """返回第一个被选中的option元素 \n
:return: ChromeElement对象或None :return: ChromeElement对象或None
""" """
@ -1483,7 +1489,7 @@ class ChromeSelect(object):
return ele return ele
@property @property
def selected_options(self) -> List[ChromeElement]: def selected_options(self) -> List[ChromiumElement]:
"""返回所有被选中的option元素列表 \n """返回所有被选中的option元素列表 \n
:return: ChromeElement对象组成的列表 :return: ChromeElement对象组成的列表
""" """
@ -1638,14 +1644,14 @@ class ChromeElementWaiter(object):
def __init__(self, def __init__(self,
page_or_ele, page_or_ele,
loc_or_ele: Union[str, tuple, ChromeElement], loc_or_ele: Union[str, tuple, ChromiumElement],
timeout: float = None): timeout: float = None):
"""等待元素在dom中某种状态如删除、显示、隐藏 \n """等待元素在dom中某种状态如删除、显示、隐藏 \n
:param page_or_ele: 页面或父元素 :param page_or_ele: 页面或父元素
:param loc_or_ele: 要等待的元素可以是已有元素定位符 :param loc_or_ele: 要等待的元素可以是已有元素定位符
:param timeout: 超时时间默认读取页面超时时间 :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只能接收定位符或元素对象。') raise TypeError('loc_or_ele只能接收定位符或元素对象。')
self.driver = page_or_ele self.driver = page_or_ele
@ -1653,11 +1659,11 @@ class ChromeElementWaiter(object):
if timeout is not None: if timeout is not None:
self.timeout = timeout self.timeout = timeout
else: 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: def delete(self) -> bool:
"""等待元素从dom删除""" """等待元素从dom删除"""
if isinstance(self.loc_or_ele, ChromeElement): if isinstance(self.loc_or_ele, ChromiumElement):
end_time = perf_counter() + self.timeout end_time = perf_counter() + self.timeout
while perf_counter() < end_time: while perf_counter() < end_time:
if not self.loc_or_ele.is_alive: if not self.loc_or_ele.is_alive:

View File

@ -16,10 +16,10 @@ from .config import DriverOptions, _cookies_to_tuple
from .base import BasePage from .base import BasePage
from .common import get_loc from .common import get_loc
from .drission import connect_chrome 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, def __init__(self, Tab_or_Options: Union[Tab, DriverOptions] = None,
@ -67,14 +67,14 @@ class ChromePage(BasePage):
self._driver.DOM.enable() self._driver.DOM.enable()
self._driver.Page.enable() self._driver.Page.enable()
root = self._driver.DOM.getDocument() 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._alert = Alert()
self.driver.Page.javascriptDialogOpening = self._on_alert_open self.driver.Page.javascriptDialogOpening = self._on_alert_open
self.driver.Page.javascriptDialogClosed = self._on_alert_close self.driver.Page.javascriptDialogClosed = self._on_alert_close
def __call__(self, loc_or_str: Union[Tuple[str, str], str, 'ChromeElement'], def __call__(self, loc_or_str: Union[Tuple[str, str], str, 'ChromiumElement'],
timeout: float = None) -> Union['ChromeElement', None]: timeout: float = None) -> Union['ChromiumElement', None]:
"""在内部查找元素 \n """在内部查找元素 \n
ele = page('@id=ele_id') \n ele = page('@id=ele_id') \n
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串 :param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
@ -114,7 +114,7 @@ class ChromePage(BasePage):
@property @property
def tab_ids(self) -> list: def tab_ids(self) -> list:
"""返回所有标签页id""" """返回所有标签页id"""
self.driver d = self.driver
json = loads(requests_get(f'http://{self.address}/json').text) json = loads(requests_get(f'http://{self.address}/json').text)
return [i['id'] for i in json if i['type'] == 'page'] return [i['id'] for i in json if i['type'] == 'page']
@ -141,7 +141,7 @@ class ChromePage(BasePage):
return {'height': h, 'width': w} return {'height': h, 'width': w}
@property @property
def active_ele(self) -> ChromeElement: def active_ele(self) -> ChromiumElement:
"""返回当前焦点所在元素""" """返回当前焦点所在元素"""
return self.run_script('return document.activeElement;') return self.run_script('return document.activeElement;')
@ -267,8 +267,8 @@ class ChromePage(BasePage):
self.driver.Network.setCookies(cookies=result_cookies) self.driver.Network.setCookies(cookies=result_cookies)
def ele(self, def ele(self,
loc_or_ele: Union[Tuple[str, str], str, ChromeElement], loc_or_ele: Union[Tuple[str, str], str, ChromiumElement],
timeout: float = None) -> Union[ChromeElement, None]: timeout: float = None) -> Union[ChromiumElement, None]:
"""获取第一个符合条件的元素对象 \n """获取第一个符合条件的元素对象 \n
:param loc_or_ele: 定位符或元素对象 :param loc_or_ele: 定位符或元素对象
:param timeout: 查找超时时间 :param timeout: 查找超时时间
@ -277,8 +277,8 @@ class ChromePage(BasePage):
return self._ele(loc_or_ele, timeout=timeout) return self._ele(loc_or_ele, timeout=timeout)
def eles(self, def eles(self,
loc_or_ele: Union[Tuple[str, str], str, ChromeElement], loc_or_ele: Union[Tuple[str, str], str, ChromiumElement],
timeout: float = None) -> List[ChromeElement]: timeout: float = None) -> List[ChromiumElement]:
"""获取所有符合条件的元素对象 \n """获取所有符合条件的元素对象 \n
:param loc_or_ele: 定位符或元素对象 :param loc_or_ele: 定位符或元素对象
:param timeout: 查找超时时间 :param timeout: 查找超时时间
@ -286,12 +286,13 @@ class ChromePage(BasePage):
""" """
return self._ele(loc_or_ele, timeout=timeout, single=False) 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 """查找第一个符合条件的元素以SessionElement形式返回处理复杂页面时效率很高 \n
:param loc_or_ele: 元素的定位信息可以是loc元组或查询字符串 :param loc_or_ele: 元素的定位信息可以是loc元组或查询字符串
:return: SessionElement对象或属性文本 :return: SessionElement对象或属性文本
""" """
if isinstance(loc_or_ele, ChromeElement): if isinstance(loc_or_ele, ChromiumElement):
return make_session_ele(loc_or_ele) return make_session_ele(loc_or_ele)
else: else:
return make_session_ele(self, loc_or_ele) 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) return make_session_ele(self, loc_or_str, single=False)
def _ele(self, def _ele(self,
loc_or_ele: Union[Tuple[str, str], str, ChromeElement], loc_or_ele: Union[Tuple[str, str], str, ChromiumElement],
timeout: float = None, timeout: float = None,
single: bool = True) -> Union[ChromeElement, None, List[ChromeElement]]: single: bool = True) -> Union[ChromiumElement, None, List[ChromiumElement]]:
"""执行元素查找 """执行元素查找
:param loc_or_ele: 定位符或元素对象 :param loc_or_ele: 定位符或元素对象
:param timeout: 查找超时时间 :param timeout: 查找超时时间
@ -315,7 +316,7 @@ class ChromePage(BasePage):
""" """
if isinstance(loc_or_ele, (str, tuple)): if isinstance(loc_or_ele, (str, tuple)):
loc = get_loc(loc_or_ele)[1] loc = get_loc(loc_or_ele)[1]
elif isinstance(loc_or_ele, ChromeElement): elif isinstance(loc_or_ele, ChromiumElement):
return loc_or_ele return loc_or_ele
else: else:
raise ValueError('loc_or_str参数只能是tuple、str、ChromeElement类型。') raise ValueError('loc_or_str参数只能是tuple、str、ChromeElement类型。')
@ -336,12 +337,12 @@ class ChromePage(BasePage):
count = 1 if single else count count = 1 if single else count
nodeIds = self.driver.DOM.getSearchResults(searchId=search_result['searchId'], fromIndex=0, toIndex=count) nodeIds = self.driver.DOM.getSearchResults(searchId=search_result['searchId'], fromIndex=0, toIndex=count)
if count == 1: if count == 1:
return ChromeElement(self, node_id=nodeIds['nodeIds'][0]) return ChromiumElement(self, node_id=nodeIds['nodeIds'][0])
else: 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, def wait_ele(self,
loc_or_ele: Union[str, tuple, ChromeElement], loc_or_ele: Union[str, tuple, ChromiumElement],
timeout: float = None) -> ChromeElementWaiter: timeout: float = None) -> ChromeElementWaiter:
"""返回用于等待元素到达某个状态的等待器对象 \n """返回用于等待元素到达某个状态的等待器对象 \n
:param loc_or_ele: 可以是元素查询字符串loc元组 :param loc_or_ele: 可以是元素查询字符串loc元组
@ -405,7 +406,7 @@ class ChromePage(BasePage):
f.write(png) f.write(png)
return str(path.absolute()) 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 """滚动页面直到元素可见 \n
:param loc_or_ele: 元素的定位信息可以是loc元组或查询字符串详见ele函数注释 :param loc_or_ele: 元素的定位信息可以是loc元组或查询字符串详见ele函数注释
:return: None :return: None
@ -492,7 +493,7 @@ class ChromePage(BasePage):
:param url: 新标签页跳转到的网址 :param url: 新标签页跳转到的网址
:return: None :return: None
""" """
self.driver d = self.driver
url = f'?{url}' if url else '' url = f'?{url}' if url else ''
requests_get(f'http://{self.address}/json/new{url}') requests_get(f'http://{self.address}/json/new{url}')
@ -517,7 +518,7 @@ class ChromePage(BasePage):
def to_front(self) -> None: def to_front(self) -> None:
"""激活当前标签页使其处于最前面""" """激活当前标签页使其处于最前面"""
self.driver d = self.driver
requests_get(f'http://{self.address}/json/activate/{self.current_tab_id}') 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: 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): class Timeout(object):
"""用于保存d模式timeout信息的类""" """用于保存d模式timeout信息的类"""
def __init__(self, page: ChromePage): def __init__(self, page: ChromiumPage):
self.page = page self.page = page
self.page_load = 30 self.page_load = 30
self.script = 30 self.script = 30
@ -713,7 +714,7 @@ class Timeout(object):
class WindowSizeSetter(object): class WindowSizeSetter(object):
"""用于设置窗口大小的类""" """用于设置窗口大小的类"""
def __init__(self, page: ChromePage): def __init__(self, page: ChromiumPage):
self.driver = page.driver self.driver = page.driver
self.window_id = self._get_info()['windowId'] 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) 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 page: ChromePage对象
:param hide: 是否隐藏 :param hide: 是否隐藏

View File

@ -611,10 +611,55 @@ def _get_running_args(opt: DriverOptions) -> list:
result.append(ext) result.append(ext)
# ----------处理experimental_options------------- # ----------处理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 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: def _location_in_viewport(page, loc_x: int, loc_y: int) -> bool:
"""判断给定的坐标是否在视口中 |n """判断给定的坐标是否在视口中 |n

View File

@ -462,6 +462,7 @@ class DriverOptions(Options):
""" """
super().__init__() super().__init__()
self._driver_path = None self._driver_path = None
self._user_data_path = None
self.ini_path = None self.ini_path = None
if read_file: if read_file:
@ -475,9 +476,13 @@ class DriverOptions(Options):
self._extensions = options_dict.get('extensions', []) self._extensions = options_dict.get('extensions', [])
self._experimental_options = options_dict.get('experimental_options', {}) self._experimental_options = options_dict.get('experimental_options', {})
self._debugger_address = options_dict.get('debugger_address', None) 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') 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 = options_dict.get('timeouts', {'implicit': 10, 'pageLoad': 30, 'script': 30})
self.timeouts['implicit'] *= 1000 self.timeouts['implicit'] *= 1000
self.timeouts['pageLoad'] *= 1000 self.timeouts['pageLoad'] *= 1000
@ -496,6 +501,11 @@ class DriverOptions(Options):
"""浏览器启动文件路径""" """浏览器启动文件路径"""
return self.binary_location or 'chrome' return self.binary_location or 'chrome'
@property
def user_data_path(self) -> str:
"""返回用户文件夹路径"""
return self._user_data_path
# -------------重写父类方法,实现链式操作------------- # -------------重写父类方法,实现链式操作-------------
def add_argument(self, argument) -> 'DriverOptions': def add_argument(self, argument) -> 'DriverOptions':
"""添加一个配置项 \n """添加一个配置项 \n
@ -727,10 +737,10 @@ class DriverOptions(Options):
:return: 当前对象 :return: 当前对象
""" """
if driver_path is not None: if driver_path is not None:
self._driver_path = driver_path self._driver_path = str(driver_path)
if chrome_path is not None: if chrome_path is not None:
self.binary_location = chrome_path self.binary_location = str(chrome_path)
if local_port is not None: if local_port is not None:
self.debugger_address = '' if local_port == '' else f'127.0.0.1:{local_port}' 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 self.debugger_address = debugger_address
if download_path is not None: 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: 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: 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 return self

View File

@ -5,11 +5,10 @@ tmp_path =
[chrome_options] [chrome_options]
debugger_address = 127.0.0.1:9222 debugger_address = 127.0.0.1:9222
binary_location = chrome 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 = [] 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']} 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} timeouts = {'implicit': 10.0, 'pageLoad': 30.0, 'script': 30.0}
set_window_rect = None
page_load_strategy = normal page_load_strategy = normal
[session_options] [session_options]

View File

@ -7,15 +7,15 @@ from requests import Session, Response
from requests.structures import CaseInsensitiveDict from requests.structures import CaseInsensitiveDict
from tldextract import extract from tldextract import extract
from .chrome_element import ChromeElement from .chromium_element import ChromiumElement
from .session_element import SessionElement from .session_element import SessionElement
from .base import BasePage from .base import BasePage
from .config import DriverOptions, SessionOptions, _cookies_to_tuple from .config import DriverOptions, SessionOptions, _cookies_to_tuple
from .chrome_page import ChromePage from .chromium_page import ChromiumPage
from .session_page import SessionPage from .session_page import SessionPage
class WebPage(SessionPage, ChromePage, BasePage): class WebPage(SessionPage, ChromiumPage, BasePage):
"""整合浏览器和request的页面类""" """整合浏览器和request的页面类"""
def __init__(self, def __init__(self,
@ -34,7 +34,7 @@ class WebPage(SessionPage, ChromePage, BasePage):
if self._mode not in ('s', 'd'): if self._mode not in ('s', 'd'):
raise ValueError('mode参数只能是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._session = None
self._driver = None self._driver = None
self._set_session_options(session_or_options) self._set_session_options(session_or_options)
@ -44,11 +44,11 @@ class WebPage(SessionPage, ChromePage, BasePage):
self._response = None self._response = None
if self._mode == 'd': if self._mode == 'd':
self.driver d = self.driver
def __call__(self, def __call__(self,
loc_or_str: Union[Tuple[str, str], str, ChromeElement, SessionElement], loc_or_str: Union[Tuple[str, str], str, ChromiumElement, SessionElement],
timeout: float = None) -> Union[ChromeElement, SessionElement, None]: timeout: float = None) -> Union[ChromiumElement, SessionElement, None]:
"""在内部查找元素 \n """在内部查找元素 \n
ele = page('@id=ele_id') \n ele = page('@id=ele_id') \n
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串 :param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
@ -152,8 +152,8 @@ class WebPage(SessionPage, ChromePage, BasePage):
return super().get(url, show_errmsg, retry, interval, timeout, **kwargs) return super().get(url, show_errmsg, retry, interval, timeout, **kwargs)
def ele(self, 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) -> Union[ChromeElement, SessionElement, str, None]: timeout: float = None) -> Union[ChromiumElement, SessionElement, str, None]:
"""返回第一个符合条件的元素、属性或节点文本 \n """返回第一个符合条件的元素、属性或节点文本 \n
:param loc_or_ele: 元素的定位信息可以是元素对象loc元组或查询字符串 :param loc_or_ele: 元素的定位信息可以是元素对象loc元组或查询字符串
:param timeout: 查找元素超时时间默认与页面等待时间一致 :param timeout: 查找元素超时时间默认与页面等待时间一致
@ -166,7 +166,7 @@ class WebPage(SessionPage, ChromePage, BasePage):
def eles(self, def eles(self,
loc_or_str: Union[Tuple[str, str], str], 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 """返回页面中所有符合条件的元素、属性或节点文本 \n
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串 :param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 查找元素超时时间默认与页面等待时间一致 :param timeout: 查找元素超时时间默认与页面等待时间一致
@ -177,7 +177,7 @@ class WebPage(SessionPage, ChromePage, BasePage):
elif self._mode == 'd': elif self._mode == 'd':
return super(SessionPage, self).eles(loc_or_str, timeout=timeout) 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]: -> Union[SessionElement, str, None]:
"""查找第一个符合条件的元素以SessionElement形式返回d模式处理复杂页面时效率很高 \n """查找第一个符合条件的元素以SessionElement形式返回d模式处理复杂页面时效率很高 \n
:param loc_or_ele: 元素的定位信息可以是loc元组或查询字符串 :param loc_or_ele: 元素的定位信息可以是loc元组或查询字符串
@ -214,7 +214,7 @@ class WebPage(SessionPage, ChromePage, BasePage):
# s模式转d模式 # s模式转d模式
if self._mode == 'd': if self._mode == 'd':
if not self._has_driver: if not self._has_driver:
self.driver d = self.driver
self._url = None if not self._has_driver else super(SessionPage, self).url self._url = None if not self._has_driver else super(SessionPage, self).url
self._has_driver = True self._has_driver = True
@ -353,10 +353,10 @@ class WebPage(SessionPage, ChromePage, BasePage):
return super().download return super().download
def _ele(self, 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) \ timeout: float = None, single: bool = True) \
-> Union[ChromeElement, SessionElement, str, None, List[Union[SessionElement, str]], List[ -> Union[ChromiumElement, SessionElement, str, None, List[Union[SessionElement, str]], List[
Union[ChromeElement, str]]]: Union[ChromiumElement, str]]]:
"""返回页面中符合条件的元素、属性或节点文本,默认返回第一个 \n """返回页面中符合条件的元素、属性或节点文本,默认返回第一个 \n
:param loc_or_ele: 元素的定位信息可以是元素对象loc元组或查询字符串 :param loc_or_ele: 元素的定位信息可以是元素对象loc元组或查询字符串
:param timeout: 查找元素超时时间d模式专用 :param timeout: 查找元素超时时间d模式专用

View File

@ -4,8 +4,8 @@ DrissionPage即 driver 和 session 组合而成的 page。
是个基于 python 的 Web 自动化操作集成工具。 是个基于 python 的 Web 自动化操作集成工具。
它用 POM 模式封装了页面和元素常用的方法, 它用 POM 模式封装了页面和元素常用的方法,
自带一套简洁直观优雅的元素定位语法, 自带一套简洁直观优雅的元素定位语法,
实现了 selenium 和 requests 之间的无缝切换, 实现了浏览器和 requests 之间的无缝切换,
可兼顾 selenium 的便利性和 requests 的高效率, 可兼顾浏览器自动化的便利性和 requests 的高效率,
更棒的是,它的使用方式非常简洁和人性化,代码量少,对新手友好。 更棒的是,它的使用方式非常简洁和人性化,代码量少,对新手友好。
**使用文档:** 📒[点击打开](http://g1879.gitee.io/drissionpage) **使用文档:** 📒[点击打开](http://g1879.gitee.io/drissionpage)
@ -17,10 +17,35 @@ DrissionPage即 driver 和 session 组合而成的 page。
## 📕 背景 ## 📕 背景
用 requests 做数据采集面对要登录的网站时要分析数据包、JS 源码构造复杂的请求往往还要应付验证码、JS 混淆、签名参数等反爬手段,门槛较高。若数据是由 JS 计算生成的,还须重现计算过程,体验不好,开发效率不高。 用 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 多实用功能,对常用功能作了整合和优化。 作者踩过无数坑,总结出的经验全写到这个库里了。内置了 N 多实用功能,对常用功能作了整合和优化。
@ -28,7 +53,7 @@ DrissionPage即 driver 和 session 组合而成的 page。
## 🎉 特性 ## 🎉 特性
- 代码高度集成,以简洁的代码为第一追求。 - 代码高度集成,以简洁的代码为第一追求。
- 页面对象可在 selenium 和 requests 模式间任意切换,保留登录状态。 - 页面对象可在浏览器和 requests 间任意切换,保留登录状态。
- 极简单但强大的元素定位语法,支持链式操作,代码极其简洁。 - 极简单但强大的元素定位语法,支持链式操作,代码极其简洁。
- 两种模式提供一致的 API使用体验一致。 - 两种模式提供一致的 API使用体验一致。
- 人性化设计,集成众多实用功能,大大降低开发工作量。 - 人性化设计,集成众多实用功能,大大降低开发工作量。
@ -160,8 +185,7 @@ ele.click()
🌿 用 xpath 直接获取属性或文本节点(返回文本) 🌿 用 xpath 直接获取属性或文本节点(返回文本)
```python ```python
# 使用 selenium # 使用 selenium 无此功能
相当复杂
# 使用 DrissionPage # 使用 DrissionPage
class_name = element('xpath://div[@id="div_id"]/@class') class_name = element('xpath://div[@id="div_id"]/@class')
@ -178,7 +202,7 @@ page.hide_browser() # 让浏览器窗口消失
page.show_browser() # 重新显示浏览器窗口 page.show_browser() # 重新显示浏览器窗口
``` ```
注:本功能只支持 Windows,且须设置了`local_port``debugger_address`参数时才能生效 注:本功能只支持 Windows。
☘️ **与 requests 代码对比** ☘️ **与 requests 代码对比**

View File

@ -4,12 +4,10 @@ DrissionPage即 driver 和 session 组合而成的 page。
是个基于 python 的 Web 自动化操作集成工具。 是个基于 python 的 Web 自动化操作集成工具。
它用 POM 模式封装了页面和元素常用的方法, 它用 POM 模式封装了页面和元素常用的方法,
自带一套简洁直观优雅的元素定位语法, 自带一套简洁直观优雅的元素定位语法,
实现了 selenium 和 requests 之间的无缝切换, 实现了浏览器和 requests 之间的无缝切换,
可兼顾 selenium 的便利性和 requests 的高效率, 可兼顾浏览器自动化的便利性和 requests 的高效率,
更棒的是,它的使用方式非常简洁和人性化,代码量少,对新手友好。 更棒的是,它的使用方式非常简洁和人性化,代码量少,对新手友好。
**交流QQ群** 897838127 **交流QQ群** 897838127
**联系邮箱:** g1879@qq.com **联系邮箱:** g1879@qq.com
@ -17,11 +15,36 @@ DrissionPage即 driver 和 session 组合而成的 page。
## 📕 背景 ## 📕 背景
用 requests 做数据采集面对要登录的网站时要分析数据包、JS 源码构造复杂的请求往往还要应付验证码、JS 混淆、签名参数等反爬手段,门槛较高。若数据是由 JS 计算生成的,还须重现计算过程,体验不好,开发效率不高。 用 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 多实用功能,对常用功能作了整合和优化。 作者踩过无数坑,总结出的经验全写到这个库里了。内置了 N 多实用功能,对常用功能作了整合和优化。

View File

@ -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()` - 页面对象和元素对象的`screenshot_as_bytes()`方法合并到`screenshot()`