cookies()增加as_json()方法;src()支持获取<link>内容;Chromium对象增加states属性

This commit is contained in:
g1879 2024-08-13 17:38:54 +08:00
parent ad81575546
commit cdced6e8d7
15 changed files with 1507 additions and 1224 deletions

View File

@ -57,6 +57,24 @@ class BaseElement(BaseParser):
self.owner = owner
self._type = 'BaseElement'
def get_frame(self, loc_or_ind, timeout=None):
if not isinstance(loc_or_ind, (int, str, tuple)):
raise TypeError('loc_or_ind参数是定位符或序号。')
return get_frame(self, loc_ind_ele=loc_or_ind, timeout=timeout)
def _ele(self, locator, timeout=None, index=1, relative=False, raise_err=None, method=None):
if hasattr(locator, '_type'):
return locator
r = self._find_elements(locator, timeout=timeout, index=index, relative=relative, raise_err=raise_err)
if r or isinstance(r, list):
return r
if Settings.raise_when_ele_not_found or raise_err is True:
raise ElementNotFoundError(None, method, {'locator': locator, 'index': index})
r.method = method
r.args = {'locator': locator, 'index': index}
return r
# ----------------以下属性或方法由后代实现----------------
@property
def tag(self):
@ -71,78 +89,31 @@ class BaseElement(BaseParser):
def nexts(self):
pass
def get_frame(self, loc_or_ind, timeout=None):
"""获取元素中一个frame对象
:param loc_or_ind: 定位符iframe序号序号从1开始可传入负数获取倒数第几个
:param timeout: 查找元素超时时间
:return: ChromiumFrame对象
"""
if not isinstance(loc_or_ind, (int, str, tuple)):
raise TypeError('loc_or_ind参数是定位符或序号。')
return get_frame(self, loc_ind_ele=loc_or_ind, timeout=timeout)
def _ele(self, locator, timeout=None, index=1, relative=False, raise_err=None, method=None):
"""调用获取元素的方法
:param locator: 定位符
:param timeout: 超时时间
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:param relative: 是否相对定位
:param raise_err: 找不到时是否抛出异常
:param method: 调用的方法名
:return: 元素对象或它们组成的列表
"""
if hasattr(locator, '_type'):
return locator
r = self._find_elements(locator, timeout=timeout, index=index, relative=relative, raise_err=raise_err)
if r or isinstance(r, list):
return r
if Settings.raise_when_ele_not_found or raise_err is True:
raise ElementNotFoundError(None, method, {'locator': locator, 'index': index})
r.method = method
r.args = {'locator': locator, 'index': index}
return r
class DrissionElement(BaseElement):
"""ChromiumElement 和 SessionElement的基类但不是ShadowRoot的基类"""
@property
def link(self):
"""返回href或src绝对url"""
return self.attr('href') or self.attr('src')
@property
def css_path(self):
"""返回css path路径"""
return self._get_ele_path('css')
@property
def xpath(self):
"""返回xpath路径"""
return self._get_ele_path('xpath')
@property
def comments(self):
"""返回元素注释文本组成的列表"""
return self.eles('xpath:.//comment()')
def texts(self, text_node_only=False):
"""返回元素内所有直接子节点的文本,包括元素和文本节点
:param text_node_only: 是否只返回文本节点
:return: 文本列表
"""
texts = self.eles('xpath:/text()') if text_node_only else [x if isinstance(x, str) else x.text
for x in self.eles('xpath:./text() | *')]
return [format_html(x.strip(' ').rstrip('\n')) for x in texts if x and sub('[\r\n\t ]', '', x) != '']
def parent(self, level_or_loc=1, index=1, timeout=None):
"""返回上面某一级父元素,可指定层数或用查询语法定位
:param level_or_loc: 第几级父元素1开始或定位符
:param index: 当level_or_loc传入定位符使用此参数选择第几个结果1开始
:param timeout: 时间
:return: 上级元素对象
"""
if isinstance(level_or_loc, int):
loc = f'xpath:./ancestor::*[{level_or_loc}]'
@ -158,13 +129,6 @@ class DrissionElement(BaseElement):
return self._ele(loc, timeout=timeout, relative=True, raise_err=False, method='parent()')
def child(self, locator='', index=1, timeout=None, ele_only=True):
"""返回直接子元素元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param index: 第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 直接子元素或节点文本组成的列表
"""
if isinstance(locator, int):
index = locator
locator = ''
@ -181,52 +145,18 @@ class DrissionElement(BaseElement):
{'locator': locator, 'index': index, 'ele_only': ele_only})
def prev(self, locator='', index=1, timeout=None, ele_only=True):
"""返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param locator: 用于筛选的查询语法
:param index: 前面第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 兄弟元素
"""
return self._get_relative('prev()', 'preceding', True, locator, index, timeout, ele_only)
def next(self, locator='', index=1, timeout=None, ele_only=True):
"""返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param locator: 用于筛选的查询语法
:param index: 后面第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 兄弟元素
"""
return self._get_relative('next()', 'following', True, locator, index, timeout, ele_only)
def before(self, locator='', index=1, timeout=None, ele_only=True):
"""返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param locator: 用于筛选的查询语法
:param index: 前面第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素前面的某个元素或节点
"""
return self._get_relative('before()', 'preceding', False, locator, index, timeout, ele_only)
def after(self, locator='', index=1, timeout=None, ele_only=True):
"""返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param locator: 用于筛选的查询语法
:param index: 后面第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素后面的某个元素或节点
"""
return self._get_relative('after()', 'following', False, locator, index, timeout, ele_only)
def children(self, locator='', timeout=None, ele_only=True):
"""返回直接子元素元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 直接子元素或节点文本组成的列表
"""
if not locator:
loc = '*' if ele_only else 'node()'
else:
@ -240,53 +170,20 @@ class DrissionElement(BaseElement):
return [e for e in nodes if not (isinstance(e, str) and sub('[ \n\t\r]', '', e) == '')]
def prevs(self, locator='', timeout=None, ele_only=True):
"""返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 兄弟元素或节点文本组成的列表
"""
return self._get_relatives(locator=locator, direction='preceding', timeout=timeout, ele_only=ele_only)
def nexts(self, locator='', timeout=None, ele_only=True):
"""返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 兄弟元素或节点文本组成的列表
"""
return self._get_relatives(locator=locator, direction='following', timeout=timeout, ele_only=ele_only)
def befores(self, locator='', timeout=None, ele_only=True):
"""返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素前面的元素或节点组成的列表
"""
return self._get_relatives(locator=locator, direction='preceding',
brother=False, timeout=timeout, ele_only=ele_only)
def afters(self, locator='', timeout=None, ele_only=True):
"""返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素后面的元素或节点组成的列表
"""
return self._get_relatives(locator=locator, direction='following',
brother=False, timeout=timeout, ele_only=ele_only)
def _get_relative(self, func, direction, brother, locator='', index=1, timeout=None, ele_only=True):
"""获取一个亲戚元素或节点,可用查询语法筛选,可指定返回筛选结果的第几个
:param func: 方法名称
:param direction: 方向'following' 'preceding'
:param locator: 用于筛选的查询语法
:param index: 前面第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素前面的某个元素或节点
"""
if isinstance(locator, int):
index = locator
locator = ''
@ -295,14 +192,6 @@ class DrissionElement(BaseElement):
{'locator': locator, 'index': index, 'ele_only': ele_only})
def _get_relatives(self, index=None, locator='', direction='following', brother=True, timeout=.5, ele_only=True):
"""按要求返回兄弟元素或节点组成的列表
:param index: 获取第几个该参数不为None时只获取该编号的元素
:param locator: 用于筛选的查询语法
:param direction: 'following' 'preceding'查找的方向
:param brother: 查找范围在同级查找还是整个dom前后查找
:param timeout: 查找等待时间
:return: 元素对象或字符串
"""
brother = '-sibling' if brother else ''
if not locator:
@ -348,10 +237,8 @@ class DrissionElement(BaseElement):
class BasePage(BaseParser):
"""页面类的基类"""
def __init__(self):
"""初始化函数"""
self._url = None
self._url_available = None
self.retry_times = 3
@ -364,34 +251,24 @@ class BasePage(BaseParser):
@property
def title(self):
"""返回网页title"""
ele = self._ele('xpath://title', raise_err=False, method='title')
return ele.text if ele else None
@property
def url_available(self):
"""返回当前访问的url有效性"""
return self._url_available
@property
def download_path(self):
"""返回默认下载路径"""
return self._download_path
@property
def download(self):
"""返回下载器对象"""
if self._DownloadKit is None:
self._DownloadKit = DownloadKit(driver=self, goal_path=self.download_path)
return self._DownloadKit
def _before_connect(self, url, retry, interval):
"""连接前的准备
:param url: 要访问的url
:param retry: 重试次数
:param interval: 重试间隔
:return: 重试次数间隔是否文件组成的tuple
"""
is_file = False
if isinstance(url, Path) or ('://' not in url and ':\\\\' not in url):
p = Path(url)
@ -422,14 +299,6 @@ class BasePage(BaseParser):
pass
def _ele(self, locator, timeout=None, index=1, raise_err=None, method=None):
"""调用获取元素的方法
:param locator: 定位符
:param timeout: 超时时间
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:param raise_err: 找不到时是否抛出异常
:param method: 调用的方法名
:return: 元素对象或它们组成的列表
"""
if not locator:
raise ElementNotFoundError(None, method, {'locator': locator})

View File

@ -57,22 +57,14 @@ class BaseParser(object):
class BaseElement(BaseParser):
owner: BasePage = ...
def __init__(self, owner: BasePage = None):
self.owner: BasePage = ...
def __init__(self, owner: BasePage = None): ...
# ----------------以下属性或方法由后代实现----------------
@property
def tag(self) -> str: ...
def _ele(self,
locator: Union[Tuple[str, str], str],
timeout: float = None,
index: Optional[int] = 1,
relative: bool = False,
raise_err: bool = None,
method: str = None): ...
def parent(self, level_or_loc: Union[tuple, str, int] = 1): ...
def prev(self, index: int = 1) -> None: ...
@ -83,86 +75,206 @@ class BaseElement(BaseParser):
def nexts(self): ...
def get_frame(self, loc_or_ind, timeout=None) -> ChromiumFrame: ...
def get_frame(self, loc_or_ind, timeout=None) -> ChromiumFrame:
"""获取元素中一个frame对象
:param loc_or_ind: 定位符iframe序号序号从1开始可传入负数获取倒数第几个
:param timeout: 查找元素超时时间
:return: ChromiumFrame对象
"""
...
def _ele(self,
locator: Union[Tuple[str, str], str],
timeout: float = None,
index: Optional[int] = 1,
relative: bool = False,
raise_err: bool = None,
method: str = None):
"""调用获取元素的方法
:param locator: 定位符
:param timeout: 超时时间
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:param relative: 是否相对定位
:param raise_err: 找不到时是否抛出异常
:param method: 调用的方法名
:return: 元素对象或它们组成的列表
"""
...
class DrissionElement(BaseElement):
"""ChromiumElement 和 SessionElement的基类但不是ShadowRoot的基类"""
def __init__(self, owner: BasePage = None): ...
@property
def link(self) -> str: ...
def link(self) -> str:
"""返回href或src绝对url"""
...
@property
def css_path(self) -> str: ...
def css_path(self) -> str:
"""返回css path路径"""
...
@property
def xpath(self) -> str: ...
def xpath(self) -> str:
"""返回xpath路径"""
...
@property
def comments(self) -> list: ...
def comments(self) -> list:
"""返回元素注释文本组成的列表"""
...
def texts(self, text_node_only: bool = False) -> list: ...
def texts(self, text_node_only: bool = False) -> list:
"""返回元素内所有直接子节点的文本,包括元素和文本节点
:param text_node_only: 是否只返回文本节点
:return: 文本列表
"""
...
def parent(self,
level_or_loc: Union[tuple, str, int] = 1,
index: int = 1,
timeout: float = None) -> Union[DrissionElement, None]: ...
timeout: float = None) -> Union[DrissionElement, None]:
"""返回上面某一级父元素,可指定层数或用查询语法定位
:param level_or_loc: 第几级父元素1开始或定位符
:param index: 当level_or_loc传入定位符使用此参数选择第几个结果1开始
:param timeout: 时间
:return: 上级元素对象
"""
...
def child(self,
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]: ...
ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]:
"""返回直接子元素元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param index: 第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 直接子元素或节点文本组成的列表
"""
...
def prev(self,
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]: ...
ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]:
"""返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param locator: 用于筛选的查询语法
:param index: 前面第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 兄弟元素
"""
...
def next(self,
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]: ...
ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]:
"""返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param locator: 用于筛选的查询语法
:param index: 后面第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 兄弟元素
"""
...
def before(self,
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]: ...
ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]:
"""返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param locator: 用于筛选的查询语法
:param index: 前面第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素前面的某个元素或节点
"""
...
def after(self,
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]: ...
ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]:
"""返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param locator: 用于筛选的查询语法
:param index: 后面第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素后面的某个元素或节点
"""
...
def children(self,
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> List[Union[DrissionElement, str]]: ...
ele_only: bool = True) -> List[Union[DrissionElement, str]]:
"""返回直接子元素元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 直接子元素或节点文本组成的列表
"""
...
def prevs(self,
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> List[Union[DrissionElement, str]]: ...
ele_only: bool = True) -> List[Union[DrissionElement, str]]:
"""返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 兄弟元素或节点文本组成的列表
"""
...
def nexts(self,
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> List[Union[DrissionElement, str]]: ...
ele_only: bool = True) -> List[Union[DrissionElement, str]]:
"""返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 兄弟元素或节点文本组成的列表
"""
...
def befores(self,
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> List[Union[DrissionElement, str]]: ...
ele_only: bool = True) -> List[Union[DrissionElement, str]]:
"""返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素前面的元素或节点组成的列表
"""
...
def afters(self,
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> List[Union[DrissionElement, str]]: ...
ele_only: bool = True) -> List[Union[DrissionElement, str]]:
"""返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素后面的元素或节点组成的列表
"""
...
def _get_relative(self,
func: str,
@ -171,7 +283,17 @@ class DrissionElement(BaseElement):
locator: Union[Tuple[str, str], str] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> DrissionElement: ...
ele_only: bool = True) -> DrissionElement:
"""获取一个亲戚元素或节点,可用查询语法筛选,可指定返回筛选结果的第几个
:param func: 方法名称
:param direction: 方向'following' 'preceding'
:param locator: 用于筛选的查询语法
:param index: 前面第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素前面的某个元素或节点
"""
...
def _get_relatives(self,
index: int = None,
@ -179,7 +301,16 @@ class DrissionElement(BaseElement):
direction: str = 'following',
brother: bool = True,
timeout: float = 0.5,
ele_only: bool = True) -> List[Union[DrissionElement, str]]: ...
ele_only: bool = True) -> List[Union[DrissionElement, str]]:
"""按要求返回兄弟元素或节点组成的列表
:param index: 获取第几个该参数不为None时只获取该编号的元素
:param locator: 用于筛选的查询语法
:param direction: 'following' 'preceding'查找的方向
:param brother: 查找范围在同级查找还是整个dom前后查找
:param timeout: 查找等待时间
:return: 元素对象或字符串
"""
...
# ----------------以下属性或方法由后代实现----------------
@property
@ -198,30 +329,47 @@ class DrissionElement(BaseElement):
class BasePage(BaseParser):
"""页面类的基类"""
def __init__(self):
self._url_available: bool = ...
self.retry_times: int = ...
self.retry_interval: float = ...
self._download_path: str = ...
self._DownloadKit: DownloadKit = ...
self._none_ele_return_value: bool = ...
self._none_ele_value: Any = ...
self._page: Union[ChromiumPage, SessionPage, MixPage] = ...
_url_available: Optional[bool] = ...
retry_times: int = ...
retry_interval: float = ...
_download_path: Optional[str] = ...
_DownloadKit: Optional[DownloadKit] = ...
_none_ele_return_value: bool = ...
_none_ele_value: Any = ...
_page: Union[ChromiumPage, SessionPage, MixPage] = ...
def __init__(self): ...
@property
def title(self) -> Union[str, None]: ...
def title(self) -> Union[str, None]:
"""返回网页title"""
...
@property
def url_available(self) -> bool: ...
def url_available(self) -> bool:
"""返回当前访问的url有效性"""
...
@property
def download_path(self) -> str: ...
def download_path(self) -> str:
"""返回默认下载路径"""
...
@property
def download(self) -> DownloadKit: ...
def download(self) -> DownloadKit:
"""返回下载器对象"""
...
def _before_connect(self, url: str, retry: int, interval: float) -> tuple: ...
def _before_connect(self, url: str, retry: int, interval: float) -> tuple:
"""连接前的准备
:param url: 要访问的url
:param retry: 重试次数
:param interval: 重试间隔
:return: 重试次数间隔是否文件组成的tuple
"""
...
# ----------------以下属性或方法由后代实现----------------
@property
@ -241,4 +389,13 @@ class BasePage(BaseParser):
timeout: float = None,
index: Optional[int] = 1,
raise_err: bool = None,
method: str = None): ...
method: str = None):
"""调用获取元素的方法
:param locator: 定位符
:param timeout: 超时时间
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:param raise_err: 找不到时是否抛出异常
:param method: 调用的方法名
:return: 元素对象或它们组成的列表
"""
...

View File

@ -37,10 +37,6 @@ class Chromium(object):
_lock = Lock()
def __new__(cls, addr_or_opts=None, session_options=None):
"""
:param addr_or_opts: 浏览器地址:端口ChromiumOptions对象或端口数字int
:param session_options: 使用双模Tab时使用的默认Session配置为True使用ini文件配置
"""
opt = handle_options(addr_or_opts)
is_headless, browser_id, is_exists = run_browser(opt)
with cls._lock:
@ -51,17 +47,13 @@ class Chromium(object):
return r
r = object.__new__(cls)
r._chromium_options = opt
r.is_headless = is_headless
r._is_headless = is_headless
r._is_exists = is_exists
r.id = browser_id
cls._BROWSERS[browser_id] = r
return r
def __init__(self, addr_or_opts=None, session_options=None):
"""
:param addr_or_opts: 浏览器地址:端口ChromiumOptions对象或端口数字int
:param session_options: 使用双模Tab时使用的默认Session配置为True使用ini文件配置
"""
if hasattr(self, '_created'):
return
self._created = True
@ -81,7 +73,7 @@ class Chromium(object):
self.address = self._chromium_options.address
self._driver = BrowserDriver(self.id, 'browser', self.address, self)
if (not self._chromium_options._ua_set and self.is_headless != self._chromium_options.is_headless) or (
if (not self._chromium_options._ua_set and self._is_headless != self._chromium_options.is_headless) or (
self._is_exists and self._chromium_options._new_env):
self.quit(3, True)
connect_browser(self._chromium_options)
@ -118,32 +110,26 @@ class Chromium(object):
@property
def user_data_path(self):
"""返回用户文件夹路径"""
return self._chromium_options.user_data_path
@property
def process_id(self):
"""返回浏览器进程id"""
return self._process_id
@property
def timeout(self):
"""返回timeouts设置"""
return self._timeouts.base
@property
def timeouts(self):
"""返回timeouts设置"""
return self._timeouts
@property
def load_mode(self):
"""返回加载模式"""
return self._load_mode
@property
def download_path(self):
"""返回默认下载路径"""
return self._download_path
@property
@ -154,69 +140,38 @@ class Chromium(object):
@property
def wait(self):
"""返回用于等待的对象"""
if self._wait is None:
self._wait = BrowserWaiter(self)
return self._wait
@property
def tabs_count(self):
"""返回标签页数量"""
j = self._run_cdp('Target.getTargets')['targetInfos'] # 不要改用get避免卡死
return len([i for i in j if i['type'] in ('page', 'webview') and not i['url'].startswith('devtools://')])
@property
def tab_ids(self):
"""返回所有标签页id组成的列表"""
j = self._driver.get(f'http://{self.address}/json').json() # 不要改用cdp因为顺序不对
return [i['id'] for i in j if i['type'] in ('page', 'webview') and not i['url'].startswith('devtools://')]
@property
def latest_tab(self):
"""返回最新的标签页,最新标签页指最后创建或最后被激活的
当Settings.singleton_tab_obj==True时返回Tab对象否则返回tab id"""
return self.get_tab(self.tab_ids[0], as_id=not Settings.singleton_tab_obj)
def cookies(self, all_info=False):
"""以list格式返回所有域名的cookies
:param all_info: 是否返回所有内容False则只返回name, value, domain
:return: cookies组成的列表
"""
cks = self._run_cdp(f'Storage.getCookies')['cookies']
r = cks if all_info else [{'name': c['name'], 'value': c['value'], 'domain': c['domain']} for c in cks]
return CookiesList(r)
def new_tab(self, url=None, new_window=False, background=False, new_context=False):
"""新建一个标签页
:param url: 新标签页跳转到的网址
:param new_window: 是否在新窗口打开标签页
:param background: 是否不激活新标签页如new_window为True则无效
:param new_context: 是否创建新的上下文
:return: 新标签页对象
"""
return self._new_tab(ChromiumTab, url=url, new_window=new_window,
background=background, new_context=new_context)
def new_mix_tab(self, url=None, new_window=False, background=False, new_context=False):
"""新建一个标签页
:param url: 新标签页跳转到的网址
:param new_window: 是否在新窗口打开标签页
:param background: 是否不激活新标签页如new_window为True则无效
:param new_context: 是否创建新的上下文
:return: 新标签页对象
"""
return self._new_tab(MixTab, url=url, new_window=new_window,
background=background, new_context=new_context)
def _new_tab(self, obj, url=None, new_window=False, background=False, new_context=False):
"""新建一个标签页
:param obj: 要创建的Tab类型
:param url: 新标签页跳转到的网址
:param new_window: 是否在新窗口打开标签页
:param background: 是否不激活新标签页如new_window为True则无效
:param new_context: 是否创建新的上下文
:return: 新标签页对象
"""
tab = None
if new_context:
tab = self._run_cdp('Target.createBrowserContext')['browserContextId']
@ -244,57 +199,18 @@ class Chromium(object):
return tab
def get_tab(self, id_or_num=None, title=None, url=None, tab_type='page', as_id=False):
"""获取一个标签页对象id_or_num不为None时后面几个参数无效
:param id_or_num: 要获取的标签页id或序号序号从1开始可传入负数获取倒数第几个不是视觉排列顺序而是激活顺序
:param title: 要匹配title的文本模糊匹配为None则匹配所有
:param url: 要匹配url的文本模糊匹配为None则匹配所有
:param tab_type: tab类型可用列表输入多个 'page', 'iframe' 为None则匹配所有
:param as_id: 是否返回标签页id而不是标签页对象
:return: Tab对象
"""
return self._get_tab(id_or_num=id_or_num, title=title, url=url, tab_type=tab_type, as_id=as_id)
def get_tabs(self, title=None, url=None, tab_type='page', as_id=False):
"""查找符合条件的tab返回它们组成的列表title和url是与关系
:param title: 要匹配title的文本
:param url: 要匹配url的文本
:param tab_type: tab类型可用列表输入多个
:param as_id: 是否返回标签页id而不是标签页对象
:return: Tab对象列表
"""
return self._get_tabs(title=title, url=url, tab_type=tab_type, as_id=as_id)
def get_mix_tab(self, id_or_num=None, title=None, url=None, tab_type='page', as_id=False):
"""获取一个标签页对象id_or_num不为None时后面几个参数无效
:param id_or_num: 要获取的标签页id或序号序号从1开始可传入负数获取倒数第几个不是视觉排列顺序而是激活顺序
:param title: 要匹配title的文本模糊匹配为None则匹配所有
:param url: 要匹配url的文本模糊匹配为None则匹配所有
:param tab_type: tab类型可用列表输入多个 'page', 'iframe' 为None则匹配所有
:param as_id: 是否返回标签页id而不是标签页对象
:return: Tab对象
"""
return self._get_tab(id_or_num=id_or_num, title=title, url=url, tab_type=tab_type, mix=True, as_id=as_id)
def get_mix_tabs(self, title=None, url=None, tab_type='page', as_id=False):
"""查找符合条件的tab返回它们组成的列表title和url是与关系
:param title: 要匹配title的文本
:param url: 要匹配url的文本
:param tab_type: tab类型可用列表输入多个
:param as_id: 是否返回标签页id而不是标签页对象
:return: Tab对象列表
"""
return self._get_tabs(title=title, url=url, tab_type=tab_type, mix=True, as_id=as_id)
def _get_tab(self, id_or_num=None, title=None, url=None, tab_type='page', mix=False, as_id=False):
"""获取一个标签页对象id_or_num不为None时后面几个参数无效
:param id_or_num: 要获取的标签页id或序号序号从1开始可传入负数获取倒数第几个不是视觉排列顺序而是激活顺序
:param title: 要匹配title的文本模糊匹配为None则匹配所有
:param url: 要匹配url的文本模糊匹配为None则匹配所有
:param tab_type: tab类型可用列表输入多个 'page', 'iframe' 为None则匹配所有
:param mix: 是否返回可切换模式的Tab对象
:param as_id: 是否返回标签页id而不是标签页对象mix=False时无效
:return: Tab对象
"""
if id_or_num is not None:
if isinstance(id_or_num, str):
id_or_num = id_or_num
@ -319,14 +235,6 @@ class Chromium(object):
return MixTab(self, id_or_num) if mix else ChromiumTab(self, id_or_num)
def _get_tabs(self, title=None, url=None, tab_type='page', mix=False, as_id=False):
"""查找符合条件的tab返回它们组成的列表title和url是与关系
:param title: 要匹配title的文本
:param url: 要匹配url的文本
:param tab_type: tab类型可用列表输入多个
:param mix: 是否返回可切换模式的Tab对象
:param as_id: 是否返回标签页id而不是标签页对象mix=False时无效
:return: Tab对象列表
"""
tabs = self._driver.get(f'http://{self.address}/json').json() # 不要改用cdp
if isinstance(tab_type, str):
@ -347,11 +255,6 @@ class Chromium(object):
return [ChromiumTab(self, tab['id']) for tab in tabs]
def close_tabs(self, tabs_or_ids=None, others=False):
"""关闭传入的标签页,默认关闭当前页。可传入多个
:param tabs_or_ids: 要关闭的标签页对象或id可传入列表或元组为None时关闭最后操作的
:param others: 是否关闭指定标签页之外的
:return: None
"""
all_tabs = set(self.tab_ids)
if isinstance(tabs_or_ids, str):
tabs = {tabs_or_ids}
@ -381,10 +284,6 @@ class Chromium(object):
sleep(.1)
def activate_tab(self, id_ind_tab):
"""使标签页变为活动状态
:param id_ind_tab: 标签页idstrTab对象或标签页序号int序号从1开始
:return: None
"""
if isinstance(id_ind_tab, int):
id_ind_tab += -1 if id_ind_tab else 1
id_ind_tab = self.tab_ids[id_ind_tab]
@ -393,7 +292,6 @@ class Chromium(object):
self._run_cdp('Target.activateTarget', targetId=id_ind_tab)
def reconnect(self):
"""断开重连"""
self._driver.stop()
BrowserDriver.BROWSERS.pop(self.id)
self._driver = BrowserDriver(self.id, 'browser', self.address, self)
@ -402,12 +300,6 @@ class Chromium(object):
self._driver.set_callback('Target.targetCreated', self._onTargetCreated)
def quit(self, timeout=5, force=False, del_data=False):
"""关闭浏览器
:param timeout: 等待浏览器关闭超时时间
:param force: 是否立刻强制终止进程
:param del_data: 是否删除用户文件夹
:return: None
"""
try:
self._run_cdp('Browser.close')
except PageDisconnectedError:
@ -457,12 +349,12 @@ class Chromium(object):
path = Path(self._chromium_options.user_data_path)
rmtree(path, True)
def _run_cdp(self, cmd, **cmd_args):
ignore = cmd_args.pop('_ignore', None)
r = self._driver.run(cmd, **cmd_args)
return r if __ERROR__ not in r else raise_error(r, ignore)
def _get_driver(self, tab_id, owner=None):
"""新建并返回指定tab id的Driver
:param tab_id: 标签页id
:param owner: 使用该驱动的对象
:return: Driver对象
"""
d = self._drivers.pop(tab_id, None)
if not d:
d = Driver(tab_id, 'page', self.address)
@ -471,7 +363,6 @@ class Chromium(object):
return d
def _onTargetCreated(self, **kwargs):
"""标签页创建时执行"""
if (kwargs['targetInfo']['type'] in ('page', 'webview')
and kwargs['targetInfo']['targetId'] not in self._all_drivers
and not kwargs['targetInfo']['url'].startswith('devtools://')):
@ -484,7 +375,6 @@ class Chromium(object):
pass
def _onTargetDestroyed(self, **kwargs):
"""标签页关闭时执行"""
tab_id = kwargs['targetId']
self._dl_mgr.clear_tab_info(tab_id)
for key in [k for k, i in self._frames.items() if i == tab_id]:
@ -494,16 +384,6 @@ class Chromium(object):
self._drivers.pop(tab_id, None)
self._all_drivers.pop(tab_id, None)
def _run_cdp(self, cmd, **cmd_args):
"""执行Chrome DevTools Protocol语句
:param cmd: 协议项目
:param cmd_args: 参数
:return: 执行的结果
"""
ignore = cmd_args.pop('_ignore', None)
r = self._driver.run(cmd, **cmd_args)
return r if __ERROR__ not in r else raise_error(r, ignore)
def _on_disconnect(self):
Chromium._BROWSERS.pop(self.id, None)
if self._chromium_options.is_auto_port and self._chromium_options.user_data_path:

View File

@ -25,7 +25,6 @@ class Chromium(object):
version: str = ...
retry_times: int = ...
retry_interval: float = ...
is_headless: bool = ...
_BROWSERS: dict = ...
_chromium_options: ChromiumOptions = ...
@ -44,81 +43,191 @@ class Chromium(object):
_load_mode: str = ...
_download_path: str = ...
_is_exists: bool = ...
_is_headless: bool = ...
def __new__(cls,
addr_or_opts: Union[str, int, ChromiumOptions] = None,
session_options: Optional[SessionOptions] = None): ...
session_options: Optional[SessionOptions] = None):
"""
:param addr_or_opts: 浏览器地址:端口ChromiumOptions对象或端口数字int
:param session_options: 使用双模Tab时使用的默认Session配置为True使用ini文件配置
"""
...
def __init__(self, addr_or_opts: Union[str, int, ChromiumOptions] = None,
session_options: Optional[SessionOptions] = None): ...
def _get_driver(self, tab_id: str, owner=None) -> Driver: ...
def _run_cdp(self, cmd, **cmd_args) -> dict: ...
session_options: Optional[SessionOptions] = None):
"""
:param addr_or_opts: 浏览器地址:端口ChromiumOptions对象或端口数字int
:param session_options: 使用双模Tab时使用的默认Session配置为True使用ini文件配置
"""
...
@property
def user_data_path(self) -> str: ...
def user_data_path(self) -> str:
"""返回用户文件夹路径"""
...
@property
def process_id(self) -> Optional[int]: ...
def process_id(self) -> Optional[int]:
"""返回浏览器进程id"""
...
@property
def timeout(self) -> float: ...
def timeout(self) -> float:
"""返回基础超时设置"""
...
@property
def timeouts(self) -> Timeout: ...
def timeouts(self) -> Timeout:
"""返回所有超时设置"""
...
@property
def load_mode(self) -> str: ...
def load_mode(self) -> str:
"""返回加载模式"""
...
@property
def download_path(self) -> str: ...
def download_path(self) -> str:
"""返回默认下载路径"""
...
@property
def set(self) -> BrowserSetter: ...
def set(self) -> BrowserSetter:
"""返回用于设置的对象"""
...
@property
def wait(self) -> BrowserWaiter: ...
def wait(self) -> BrowserWaiter:
"""返回用于等待的对象"""
...
@property
def tabs_count(self) -> int: ...
def tabs_count(self) -> int:
"""返回标签页数量"""
...
@property
def tab_ids(self) -> List[str]: ...
def tab_ids(self) -> List[str]:
"""返回所有标签页id组成的列表"""
...
@property
def latest_tab(self) -> Union[ChromiumTab, str]: ...
def latest_tab(self) -> Union[ChromiumTab, str]:
"""返回最新的标签页,最新标签页指最后创建或最后被激活的
当Settings.singleton_tab_obj==True时返回Tab对象否则返回tab id"""
...
def cookies(self, all_info: bool = False) -> CookiesList: ...
def cookies(self, all_info: bool = False) -> CookiesList:
"""以list格式返回所有域名的cookies
:param all_info: 是否返回所有内容False则只返回name, value, domain
:return: cookies组成的列表
"""
...
def close_tabs(self,
tabs_or_ids: Union[str, ChromiumTab, List[Union[str, ChromiumTab]],
Tuple[Union[str, ChromiumTab]]] = None,
others: bool = False) -> None: ...
def new_tab(self,
url: str = None,
new_window: bool = False,
background: bool = False,
new_context: bool = False) -> ChromiumTab:
"""新建一个标签页
:param url: 新标签页跳转到的网址
:param new_window: 是否在新窗口打开标签页
:param background: 是否不激活新标签页如new_window为True则无效
:param new_context: 是否创建新的上下文
:return: 新标签页对象
"""
...
def new_mix_tab(self,
url: str = None,
new_window: bool = False,
background: bool = False,
new_context: bool = False) -> MixTab:
"""新建一个标签页
:param url: 新标签页跳转到的网址
:param new_window: 是否在新窗口打开标签页
:param background: 是否不激活新标签页如new_window为True则无效
:param new_context: 是否创建新的上下文
:return: 新标签页对象
"""
...
def _new_tab(self,
obj,
url: str = None,
new_window: bool = False,
background: bool = False,
new_context: bool = False) -> Union[ChromiumTab, MixTab]:
"""新建一个标签页
:param obj: 要创建的Tab类型
:param url: 新标签页跳转到的网址
:param new_window: 是否在新窗口打开标签页
:param background: 是否不激活新标签页如new_window为True则无效
:param new_context: 是否创建新的上下文
:return: 新标签页对象
"""
...
def get_tab(self,
id_or_num: Union[str, int] = None,
title: str = None,
url: str = None,
tab_type: str = 'page',
as_id: bool = False) -> Union[ChromiumTab, str]: ...
as_id: bool = False) -> Union[ChromiumTab, str]:
"""获取一个标签页对象id_or_num不为None时后面几个参数无效
:param id_or_num: 要获取的标签页id或序号序号从1开始可传入负数获取倒数第几个不是视觉排列顺序而是激活顺序
:param title: 要匹配title的文本模糊匹配为None则匹配所有
:param url: 要匹配url的文本模糊匹配为None则匹配所有
:param tab_type: tab类型可用列表输入多个 'page', 'iframe' 为None则匹配所有
:param as_id: 是否返回标签页id而不是标签页对象
:return: Tab对象
"""
...
def get_tabs(self,
title: str = None,
url: str = None,
tab_type: str = 'page',
as_id: bool = False) -> List[ChromiumTab, str]: ...
as_id: bool = False) -> List[ChromiumTab, str]:
"""查找符合条件的tab返回它们组成的列表title和url是与关系
:param title: 要匹配title的文本
:param url: 要匹配url的文本
:param tab_type: tab类型可用列表输入多个
:param as_id: 是否返回标签页id而不是标签页对象
:return: Tab对象列表
"""
...
def get_mix_tab(self,
id_or_num: Union[str, int] = None,
title: str = None,
url: str = None,
tab_type: str = 'page') -> Union[MixTab, str]: ...
tab_type: str = 'page',
as_id: bool = False) -> Union[MixTab, str]:
"""获取一个标签页对象id_or_num不为None时后面几个参数无效
:param id_or_num: 要获取的标签页id或序号序号从1开始可传入负数获取倒数第几个不是视觉排列顺序而是激活顺序
:param title: 要匹配title的文本模糊匹配为None则匹配所有
:param url: 要匹配url的文本模糊匹配为None则匹配所有
:param tab_type: tab类型可用列表输入多个 'page', 'iframe' 为None则匹配所有
:param as_id: 是否返回标签页id而不是标签页对象
:return: Tab对象
"""
...
def get_mix_tabs(self,
title: str = None,
url: str = None,
tab_type: str = 'page') -> List[MixTab, str]: ...
tab_type: str = 'page',
as_id: bool = False) -> List[MixTab, str]:
"""查找符合条件的tab返回它们组成的列表title和url是与关系
:param title: 要匹配title的文本
:param url: 要匹配url的文本
:param tab_type: tab类型可用列表输入多个
:param as_id: 是否返回标签页id而不是标签页对象
:return: Tab对象列表
"""
...
def _get_tab(self,
id_or_num: Union[str, int] = None,
@ -126,42 +235,83 @@ class Chromium(object):
url: str = None,
tab_type: str = 'page',
mix: bool = False,
as_id: bool = False) -> Union[ChromiumTab, str]: ...
as_id: bool = False) -> Union[ChromiumTab, str]:
"""获取一个标签页对象id_or_num不为None时后面几个参数无效
:param id_or_num: 要获取的标签页id或序号序号从1开始可传入负数获取倒数第几个不是视觉排列顺序而是激活顺序
:param title: 要匹配title的文本模糊匹配为None则匹配所有
:param url: 要匹配url的文本模糊匹配为None则匹配所有
:param tab_type: tab类型可用列表输入多个 'page', 'iframe' 为None则匹配所有
:param mix: 是否返回可切换模式的Tab对象
:param as_id: 是否返回标签页id而不是标签页对象mix=False时无效
:return: Tab对象
"""
...
def _get_tabs(self,
title: str = None,
url: str = None,
tab_type: str = 'page',
mix: bool = False,
as_id: bool = False) -> List[ChromiumTab, str]: ...
as_id: bool = False) -> List[ChromiumTab, str]:
"""查找符合条件的tab返回它们组成的列表title和url是与关系
:param title: 要匹配title的文本
:param url: 要匹配url的文本
:param tab_type: tab类型可用列表输入多个
:param mix: 是否返回可切换模式的Tab对象
:param as_id: 是否返回标签页id而不是标签页对象mix=False时无效
:return: Tab对象列表
"""
...
def activate_tab(self, id_ind_tab: Union[int, str, ChromiumTab]) -> None: ...
def close_tabs(self,
tabs_or_ids: Union[str, ChromiumTab, List[Union[str, ChromiumTab]],
Tuple[Union[str, ChromiumTab]]] = None,
others: bool = False) -> None:
"""关闭传入的标签页,默认关闭当前页。可传入多个
:param tabs_or_ids: 要关闭的标签页对象或id可传入列表或元组为None时关闭最后操作的
:param others: 是否关闭指定标签页之外的
:return: None
"""
...
def _new_tab(self,
obj,
url: str = None,
new_window: bool = False,
background: bool = False,
new_context: bool = False) -> Union[ChromiumTab, MixTab]: ...
def activate_tab(self, id_ind_tab: Union[int, str, ChromiumTab]) -> None:
"""使标签页变为活动状态
:param id_ind_tab: 标签页idstrTab对象或标签页序号int序号从1开始
:return: None
"""
...
def new_tab(self,
url: str = None,
new_window: bool = False,
background: bool = False,
new_context: bool = False) -> ChromiumTab: ...
def reconnect(self) -> None:
"""断开重连"""
...
def new_mix_tab(self,
url: str = None,
new_window: bool = False,
background: bool = False,
new_context: bool = False) -> MixTab: ...
def quit(self, timeout: float = 5, force: bool = False, del_data: bool = False) -> None:
"""关闭浏览器
:param timeout: 等待浏览器关闭超时时间
:param force: 是否立刻强制终止进程
:param del_data: 是否删除用户文件夹
:return: None
"""
...
def reconnect(self) -> None: ...
def _run_cdp(self, cmd, **cmd_args) -> dict:
"""执行Chrome DevTools Protocol语句
:param cmd: 协议项目
:param cmd_args: 参数
:return: 执行的结果
"""
...
def _onTargetCreated(self, **kwargs) -> None: ...
def _get_driver(self, tab_id: str, owner=None) -> Driver:
"""新建并返回指定tab id的Driver
:param tab_id: 标签页id
:param owner: 使用该驱动的对象
:return: Driver对象
"""
...
def _onTargetDestroyed(self, **kwargs) -> None: ...
def _onTargetCreated(self, **kwargs): ...
def quit(self, timeout: float = 5, force: bool = False, del_data: bool = False) -> None: ...
def _onTargetDestroyed(self, **kwargs): ...
def _on_disconnect(self) -> None: ...
def _on_disconnect(self): ...

View File

@ -23,12 +23,6 @@ adapters.DEFAULT_RETRIES = 5
class Driver(object):
def __init__(self, tab_id, tab_type, address, owner=None):
"""
:param tab_id: 标签页id
:param tab_type: 标签页类型
:param address: 浏览器连接地址
:param owner: 创建这个驱动的对象
"""
self.id = tab_id
self.address = address
self.type = tab_type
@ -58,11 +52,6 @@ class Driver(object):
self.start()
def _send(self, message, timeout=None):
"""发送信息到浏览器,并返回浏览器返回的信息
:param message: 发送给浏览器的数据
:param timeout: 超时时间为None表示无限
:return: 浏览器返回的数据
"""
self._cur_id += 1
ws_id = self._cur_id
message['id'] = ws_id
@ -110,7 +99,6 @@ class Driver(object):
return {'error': {'message': 'connection disconnected'}, 'type': 'connection_error'}
def _recv_loop(self):
"""接收浏览器信息的守护线程方法"""
while self.is_running:
try:
# self._ws.settimeout(1)
@ -148,7 +136,6 @@ class Driver(object):
# print(f'未知信息:{msg}')
def _handle_event_loop(self):
"""当接收到浏览器信息,执行已绑定的方法"""
while self.is_running:
try:
event = self.event_queue.get(timeout=1)
@ -170,11 +157,6 @@ class Driver(object):
pass
def _handle_immediate_event(self, function, kwargs):
"""处理立即执行的动作
:param function: 要运行下方法
:param kwargs: 方法参数
:return: None
"""
self.immediate_event_queue.put((function, kwargs))
if self._handle_immediate_event_th is None or not self._handle_immediate_event_th.is_alive():
self._handle_immediate_event_th = Thread(target=self._handle_immediate_event_loop)
@ -200,7 +182,6 @@ class Driver(object):
return result['result']
def start(self):
"""启动连接"""
self.is_running = True
try:
self._ws = create_connection(self._websocket_url, enable_multithread=True, suppress_origin=True)
@ -216,14 +197,12 @@ class Driver(object):
return True
def stop(self):
"""中断连接"""
self._stop()
while self._handle_event_th.is_alive() or self._recv_th.is_alive():
sleep(.1)
return True
def _stop(self):
"""中断连接"""
if not self.is_running:
return False
@ -259,12 +238,6 @@ class Driver(object):
self.owner._on_disconnect()
def set_callback(self, event, callback, immediate=False):
"""绑定cdp event和回调方法
:param event: cdp event
:param callback: 绑定到cdp event的回调方法
:param immediate: 是否要立即处理的动作
:return: None
"""
handler = self.immediate_event_handlers if immediate else self.event_handlers
if callback:
handler[event] = callback

View File

@ -15,14 +15,6 @@ from websocket import WebSocket
from .browser import Chromium
class GenericAttr(object):
def __init__(self, name: str, tab: Driver): ...
def __getattr__(self, item: str) -> Callable: ...
def __setattr__(self, key: str, value: Callable) -> None: ...
class Driver(object):
id: str
address: str
@ -35,7 +27,6 @@ class Driver(object):
_recv_th: Thread
_handle_event_th: Thread
_handle_immediate_event_th: Optional[Thread]
# _stopped: Event
is_running: bool
event_handlers: dict
immediate_event_handlers: dict
@ -43,27 +34,69 @@ class Driver(object):
event_queue: Queue
immediate_event_queue: Queue
def __init__(self, tab_id: str, tab_type: str, address: str, owner=None): ...
def __init__(self, tab_id: str, tab_type: str, address: str, owner=None):
"""
:param tab_id: 标签页id
:param tab_type: 标签页类型
:param address: 浏览器连接地址
:param owner: 创建这个驱动的对象
"""
...
def _send(self, message: dict, timeout: float = None) -> dict: ...
def _send(self, message: dict, timeout: float = None) -> dict:
"""发送信息到浏览器,并返回浏览器返回的信息
:param message: 发送给浏览器的数据
:param timeout: 超时时间为None表示无限
:return: 浏览器返回的数据
"""
...
def _recv_loop(self) -> None: ...
def _recv_loop(self) -> None:
"""接收浏览器信息的守护线程方法"""
...
def _handle_event_loop(self) -> None: ...
def _handle_event_loop(self) -> None:
"""当接收到浏览器信息,执行已绑定的方法"""
...
def _handle_immediate_event_loop(self): ...
def _handle_immediate_event(self, function: Callable, kwargs: dict): ...
def _handle_immediate_event(self, function: Callable, kwargs: dict):
"""处理立即执行的动作
:param function: 要运行下方法
:param kwargs: 方法参数
:return: None
"""
...
def run(self, _method: str, **kwargs) -> dict: ...
def run(self, _method: str, **kwargs) -> dict:
"""执行cdp方法
:param _method: cdp方法名
:param kwargs: cdp参数
:return: 执行结果
"""
...
def start(self) -> bool: ...
def start(self) -> bool:
"""启动连接"""
...
def stop(self) -> bool: ...
def stop(self) -> bool:
"""中断连接"""
...
def _stop(self) -> None: ...
def _stop(self) -> None:
"""中断连接"""
...
def set_callback(self, event: str, callback: Union[Callable, None], immediate: bool = False) -> None: ...
def set_callback(self, event: str, callback: Union[Callable, None], immediate: bool = False) -> None:
"""绑定cdp event和回调方法
:param event: cdp event
:param callback: 绑定到cdp event的回调方法
:param immediate: 是否要立即处理的动作
:return: None
"""
...
class BrowserDriver(Driver):
@ -74,4 +107,9 @@ class BrowserDriver(Driver):
def __init__(self, tab_id: str, tab_type: str, address: str, owner: Chromium): ...
def get(self, url) -> Response: ...
def get(self, url) -> Response:
"""
:param url: 要访问的链接
:return: Response对象
"""
...

View File

@ -37,12 +37,6 @@ class ChromiumElement(DrissionElement):
"""控制浏览器元素的对象"""
def __init__(self, owner, node_id=None, obj_id=None, backend_id=None):
"""node_id、obj_id和backend_id必须至少传入一个
:param owner: 元素所在页面对象
:param node_id: cdp中的node id
:param obj_id: js中的object id
:param backend_id: backend id
"""
super().__init__(owner)
self.tab = self.owner._tab
self._select = None
@ -81,11 +75,6 @@ class ChromiumElement(DrissionElement):
return f'<ChromiumElement {self.tag} {" ".join(attrs)}>'
def __call__(self, locator, index=1, timeout=None):
"""在内部查找元素
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 超时时间
:return: ChromiumElement对象或属性文本
"""
return self.ele(locator, index=index, timeout=timeout)
def __eq__(self, other):
@ -93,7 +82,6 @@ class ChromiumElement(DrissionElement):
@property
def tag(self):
"""返回元素tag"""
if self._tag is None:
self._tag = self.owner._run_cdp('DOM.describeNode',
backendNodeId=self._backend_id)['node']['localName'].lower()
@ -101,17 +89,14 @@ class ChromiumElement(DrissionElement):
@property
def html(self):
"""返回元素outerHTML文本"""
return self.owner._run_cdp('DOM.getOuterHTML', backendNodeId=self._backend_id)['outerHTML']
@property
def inner_html(self):
"""返回元素innerHTML文本"""
return self._run_js('return this.innerHTML;')
@property
def attrs(self):
"""返回元素所有attribute属性"""
try:
attrs = self.owner._run_cdp('DOM.getAttributes', nodeId=self._node_id)['attributes']
return {attrs[i]: attrs[i + 1] for i in range(0, len(attrs), 2)}
@ -124,46 +109,39 @@ class ChromiumElement(DrissionElement):
@property
def text(self):
"""返回元素内所有文本,文本已格式化"""
return get_ele_txt(make_session_ele(self.html))
@property
def raw_text(self):
"""返回未格式化处理的元素内文本"""
return self.property('innerText')
# -----------------d模式独有属性-------------------
@property
def set(self):
"""返回用于设置元素属性的对象"""
if self._set is None:
self._set = ChromiumElementSetter(self)
return self._set
@property
def states(self):
"""返回用于获取元素状态的对象"""
if self._states is None:
self._states = ElementStates(self)
return self._states
@property
def pseudo(self):
"""返回用于获取伪元素内容的对象"""
if self._pseudo is None:
self._pseudo = Pseudo(self)
return self._pseudo
@property
def rect(self):
"""返回用于获取元素位置的对象"""
if self._rect is None:
self._rect = ElementRect(self)
return self._rect
@property
def sr(self):
"""返回当前元素的shadow_root元素对象"""
end_time = perf_counter() + self.owner.timeout
while perf_counter() < end_time:
info = self.owner._run_cdp('DOM.describeNode', backendNodeId=self._backend_id)['node']
@ -173,39 +151,33 @@ class ChromiumElement(DrissionElement):
@property
def shadow_root(self):
"""返回当前元素的shadow_root元素对象"""
return self.sr
@property
def scroll(self):
"""用于滚动滚动条的对象"""
if self._scroll is None:
self._scroll = ElementScroller(self)
return self._scroll
@property
def click(self):
"""返回用于点击的对象"""
if self._clicker is None:
self._clicker = Clicker(self)
return self._clicker
@property
def wait(self):
"""返回用于等待的对象"""
if self._wait is None:
self._wait = ElementWaiter(self)
return self._wait
@property
def select(self):
"""返回专门处理下拉列表的Select类非下拉列表元素返回False"""
if self._select is None:
if self.tag != 'select':
self._select = False
else:
self._select = SelectElement(self)
return self._select
@property
@ -234,118 +206,39 @@ class ChromiumElement(DrissionElement):
self.click()
def parent(self, level_or_loc=1, index=1, timeout=0):
"""返回上面某一级父元素,可指定层数或用查询语法定位
:param level_or_loc: 第几级父元素1开始或定位符
:param index: 当level_or_loc传入定位符使用此参数选择第几个结果1开始
:param timeout: 查找超时时间
:return: 上级元素对象
"""
return super().parent(level_or_loc, index, timeout=timeout)
def child(self, locator='', index=1, timeout=None, ele_only=True):
"""返回当前元素的一个符合条件的直接子元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param locator: 用于筛选的查询语法
:param index: 第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 直接子元素或节点文本
"""
return super().child(locator, index, timeout, ele_only=ele_only)
def prev(self, locator='', index=1, timeout=None, ele_only=True):
"""返回当前元素前面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param locator: 用于筛选的查询语法
:param index: 前面第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 兄弟元素或节点文本
"""
return super().prev(locator, index, timeout, ele_only=ele_only)
def next(self, locator='', index=1, timeout=None, ele_only=True):
"""返回当前元素后面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param locator: 用于筛选的查询语法
:param index: 第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 兄弟元素或节点文本
"""
return super().next(locator, index, timeout, ele_only=ele_only)
def before(self, locator='', index=1, timeout=None, ele_only=True):
"""返回文档中当前元素前面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个
查找范围不限同级元素而是整个DOM文档
:param locator: 用于筛选的查询语法
:param index: 前面第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素前面的某个元素或节点
"""
return super().before(locator, index, timeout, ele_only=ele_only)
def after(self, locator='', index=1, timeout=None, ele_only=True):
"""返回文档中此当前元素后面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个
查找范围不限同级元素而是整个DOM文档
:param locator: 用于筛选的查询语法
:param index: 第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素后面的某个元素或节点
"""
return super().after(locator, index, timeout, ele_only=ele_only)
def children(self, locator='', timeout=None, ele_only=True):
"""返回当前元素符合条件的直接子元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 直接子元素或节点文本组成的列表
"""
return ChromiumElementsList(self.owner, super().children(locator, timeout, ele_only=ele_only))
def prevs(self, locator='', timeout=None, ele_only=True):
"""返回当前元素前面符合条件的同级元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 兄弟元素或节点文本组成的列表
"""
return ChromiumElementsList(self.owner, super().prevs(locator, timeout, ele_only=ele_only))
def nexts(self, locator='', timeout=None, ele_only=True):
"""返回当前元素后面符合条件的同级元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 兄弟元素或节点文本组成的列表
"""
return ChromiumElementsList(self.owner, super().nexts(locator, timeout, ele_only=ele_only))
def befores(self, locator='', timeout=None, ele_only=True):
"""返回文档中当前元素前面符合条件的元素或节点组成的列表,可用查询语法筛选
查找范围不限同级元素而是整个DOM文档
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素前面的元素或节点组成的列表
"""
return ChromiumElementsList(self.owner, super().befores(locator, timeout, ele_only=ele_only))
def afters(self, locator='', timeout=None, ele_only=True):
"""返回文档中当前元素后面符合条件的元素或节点组成的列表,可用查询语法筛选
查找范围不限同级元素而是整个DOM文档
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素后面的元素或节点组成的列表
"""
return ChromiumElementsList(self.owner, super().afters(locator, timeout, ele_only=ele_only))
def over(self, timeout=None):
"""获取覆盖在本元素上最上层的元素
:param timeout: 等待元素出现的超时时间
:return: 元素对象
"""
timeout = timeout if timeout is None else self.owner.timeout
bid = self.wait.covered(timeout=timeout)
if bid:
@ -354,13 +247,6 @@ class ChromiumElement(DrissionElement):
return NoneElement(page=self.owner, method='on()', args={'timeout': timeout})
def offset(self, locator=None, x=None, y=None, timeout=None):
"""获取相对本元素左上角左边指定偏移量位置的元素如果offset_x和offset_y都是None定位到元素中间点
:param locator: 定位符只支持str且不支持xpath和css方式
:param x: 横坐标偏移量向右为正
:param y: 纵坐标偏移量向下为正
:param timeout: 超时时间为None使用所在页面设置
:return: 元素对象
"""
if locator and not (isinstance(locator, str) and not locator.startswith(
('x:', 'xpath:', 'x=', 'xpath=', 'c:', 'css:', 'c=', 'css='))):
raise ValueError('locator参数只能是str格式且不支持xpath和css形式。')
@ -405,43 +291,18 @@ class ChromiumElement(DrissionElement):
args={'locator': locator, 'offset_x': x, 'offset_y': y, 'timeout': timeout})
def east(self, loc_or_pixel=None, index=1):
"""获取元素右边某个指定元素
:param loc_or_pixel: 定位符只支持str或int且不支持xpath和css方式传入int按像素距离获取
:param index: 第几个从1开始
:return: 获取到的元素对象
"""
return self._get_relative_eles(mode='east', locator=loc_or_pixel, index=index)
def south(self, loc_or_pixel=None, index=1):
"""获取元素下方某个指定元素
:param loc_or_pixel: 定位符只支持str或int且不支持xpath和css方式传入int按像素距离获取
:param index: 第几个从1开始
:return: 获取到的元素对象
"""
return self._get_relative_eles(mode='south', locator=loc_or_pixel, index=index)
def west(self, loc_or_pixel=None, index=1):
"""获取元素左边某个指定元素
:param loc_or_pixel: 定位符只支持str或int且不支持xpath和css方式传入int按像素距离获取
:param index: 第几个从1开始
:return: 获取到的元素对象
"""
return self._get_relative_eles(mode='west', locator=loc_or_pixel, index=index)
def north(self, loc_or_pixel=None, index=1):
"""获取元素上方某个指定元素
:param loc_or_pixel: 定位符只支持str或int且不支持xpath和css方式传入int按像素距离获取
:param index: 第几个从1开始
:return: 获取到的元素对象
"""
return self._get_relative_eles(mode='north', locator=loc_or_pixel, index=index)
def _get_relative_eles(self, mode='north', locator=None, index=1):
"""获取元素下方某个指定元素
:param locator: 定位符只支持str或int且不支持xpath和css方式
:param index: 第几个从1开始
:return: 获取到的元素对象
"""
if locator and not (isinstance(locator, str) and not locator.startswith(
('x:', 'xpath:', 'x=', 'xpath=', 'c:', 'css:', 'c=', 'css=')) or isinstance(locator, int)):
raise ValueError('locator参数只能是str格式且不支持xpath和css形式。')
@ -508,10 +369,6 @@ class ChromiumElement(DrissionElement):
return NoneElement(page=self.owner, method=f'{mode}()', args={'locator': locator})
def attr(self, attr):
"""返回一个attribute属性值
:param attr: 属性名
:return: 属性值文本没有该属性返回None
"""
attrs = self.attrs
if attr == 'href': # 获取href属性时返回绝对url
link = attrs.get('href')
@ -539,17 +396,9 @@ class ChromiumElement(DrissionElement):
return attrs.get(attr, None)
def remove_attr(self, name):
"""删除元素一个attribute属性
:param name: 属性名
:return: None
"""
self._run_js(f'this.removeAttribute("{name}");')
def property(self, name):
"""获取一个property属性值
:param name: 属性名
:return: 属性值文本
"""
try:
value = self._run_js(f'return this.{name};')
return format_html(value) if isinstance(value, str) else value
@ -557,98 +406,38 @@ class ChromiumElement(DrissionElement):
return None
def run_js(self, script, *args, as_expr=False, timeout=None):
"""对本元素执行javascript代码
:param script: js文本文本中用this表示本元素
:param args: 参数按顺序在js文本中对应arguments[0]arguments[1]...
:param as_expr: 是否作为表达式运行为True时args无效
:param timeout: js超时时间为None则使用页面timeouts.script设置
:return: 运行的结果
"""
return self._run_js(script, *args, as_expr=as_expr, timeout=timeout)
def _run_js(self, script, *args, as_expr=False, timeout=None):
"""对本元素执行javascript代码
:param script: js文本文本中用this表示本元素
:param args: 参数按顺序在js文本中对应arguments[0]arguments[1]...
:param as_expr: 是否作为表达式运行为True时args无效
:param timeout: js超时时间为None则使用页面timeouts.script设置
:return: 运行的结果
"""
return run_js(self, script, as_expr, self.owner.timeouts.script if timeout is None else timeout, args)
def run_async_js(self, script, *args, as_expr=False):
"""以异步方式对本元素执行javascript代码
:param script: js文本文本中用this表示本元素
:param args: 参数按顺序在js文本中对应arguments[0]arguments[1]...
:param as_expr: 是否作为表达式运行为True时args无效
:return: None
"""
run_js(self, script, as_expr, 0, args)
def ele(self, locator, index=1, timeout=None):
"""返回当前元素下级符合条件的一个元素、属性或节点文本
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 获取第几个元素从1开始可传入负数获取倒数第几个
:param timeout: 查找元素超时时间默认与元素所在页面等待时间一致
:return: ChromiumElement对象或属性文本
"""
return self._ele(locator, timeout, index=index, method='ele()')
def eles(self, locator, timeout=None):
"""返回当前元素下级所有符合条件的子元素、属性或节点文本
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 查找元素超时时间默认与元素所在页面等待时间一致
:return: ChromiumElement对象或属性文本组成的列表
"""
return self._ele(locator, timeout=timeout, index=None)
def s_ele(self, locator=None, index=1, timeout=None):
"""查找一个符合条件的元素以SessionElement形式返回
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:param timeout: 查找元素超时时间默认与元素所在页面等待时间一致
:return: SessionElement对象或属性文本
"""
return (make_session_ele(self, locator, index=index, method='s_ele()')
if self.ele(locator, index=index, timeout=timeout)
else NoneElement(self, method='s_ele()', args={'locator': locator, 'index': index}))
def s_eles(self, locator=None, timeout=None):
"""查找所有符合条件的元素以SessionElement列表形式返回
:param locator: 定位符
:param timeout: 查找元素超时时间默认与元素所在页面等待时间一致
:return: SessionElement或属性文本组成的列表
"""
return (make_session_ele(self, locator, index=None)
if self.ele(locator, timeout=timeout) else SessionElementsList())
def _find_elements(self, locator, timeout=None, index=1, relative=False, raise_err=None):
"""返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 查找元素超时时间
:param index: 第几个结果从1开始可传入负数获取倒数第几个为None返回所有
:param relative: MixTab用的表示是否相对定位的参数
:param raise_err: 找不到元素是是否抛出异常为None时根据全局设置
:return: ChromiumElement对象或文本属性或其组成的列表
"""
return find_in_chromium_ele(self, locator, index, timeout, relative=relative)
def style(self, style, pseudo_ele=''):
"""返回元素样式属性值,可获取伪元素属性值
:param style: 样式属性名称
:param pseudo_ele: 伪元素名称如有
:return: 样式属性的值
"""
if pseudo_ele:
pseudo_ele = f', "{pseudo_ele}"' if pseudo_ele.startswith(':') else f', "::{pseudo_ele}"'
return self._run_js(f'return window.getComputedStyle(this{pseudo_ele}).getPropertyValue("{style}");')
def src(self, timeout=None, base64_to_bytes=True):
"""返回元素src资源base64的可转为bytes返回其它返回str
:param timeout: 等待资源加载的超时时间
:param base64_to_bytes: 为True时如果是base64数据转换为bytes格式
:return: 资源内容
"""
timeout = self.owner.timeout if timeout is None else timeout
if self.tag == 'img': # 等待图片加载完成
js = ('return this.complete && typeof this.naturalWidth != "undefined" '
@ -658,7 +447,7 @@ class ChromiumElement(DrissionElement):
while not self._run_js(js) and perf_counter() < end_time:
sleep(.1)
src = self.attr('src')
src = self.attr('href') if self.tag == 'link' else self.attr('src')
if not src:
raise RuntimeError('元素没有src值或该值为空。')
if src.lower().startswith('data:image'):
@ -680,8 +469,9 @@ class ChromiumElement(DrissionElement):
else:
while perf_counter() < end_time:
src = self.property('currentSrc')
src = self.attr('href') if self.tag == 'link' else self.property('currentSrc') or self.property('src')
if not src:
sleep(.01)
continue
node = self.owner._run_cdp('DOM.describeNode', backendNodeId=self._backend_id)['node']
@ -707,13 +497,6 @@ class ChromiumElement(DrissionElement):
return result['content']
def save(self, path=None, name=None, timeout=None, rename=True):
"""保存图片或其它有src属性的元素的资源
:param path: 文件保存路径为None时保存到当前文件夹
:param name: 文件名称为None时从资源url获取
:param timeout: 等待资源加载的超时时间
:param rename: 遇到重名文件时是否自动重命名
:return: 返回保存路径
"""
data = self.src(timeout=timeout)
if not data:
raise NoResourceError
@ -739,14 +522,6 @@ class ChromiumElement(DrissionElement):
return str(path)
def get_screenshot(self, path=None, name=None, as_bytes=None, as_base64=None, scroll_to_center=True):
"""对当前元素截图,可保存到文件,或以字节方式返回
:param path: 文件保存路径
:param name: 完整文件名后缀可选 'jpg','jpeg','png','webp'
:param as_bytes: 是否以字节形式返回图片可选 'jpg','jpeg','png','webp'生效时path参数和as_base64参数无效
:param as_base64: 是否以base64字符串形式返回图片可选 'jpg','jpeg','png','webp'生效时path参数无效
:param scroll_to_center: 截图前是否滚动到视口中央
:return: 图片完整路径或字节文本
"""
if self.tag == 'img': # 等待图片加载完成
js = ('return this.complete && typeof this.naturalWidth != "undefined" && this.naturalWidth > 0 '
'&& typeof this.naturalHeight != "undefined" && this.naturalHeight > 0')
@ -767,12 +542,6 @@ class ChromiumElement(DrissionElement):
left_top=left_top, right_bottom=right_bottom, ele=self)
def input(self, vals, clear=False, by_js=False):
"""输入文本或组合键也可用于输入文件路径到input元素路径间用\n间隔)
:param vals: 文本值或按键组合
:param clear: 输入前是否清空文本框
:param by_js: 是否用js方式输入不能输入组合键
:return: None
"""
if self.tag == 'input' and self.attr('type') == 'file':
return self._set_file_input(vals)
@ -798,10 +567,6 @@ class ChromiumElement(DrissionElement):
self.owner.actions.type(vals)
def clear(self, by_js=False):
"""清空元素文本
:param by_js: 是否用js方式清空为False则用全选+del模拟输入删除
:return: None
"""
if by_js:
self._run_js("this.value='';")
self._run_js('this.dispatchEvent(new Event("change", {bubbles: true}));')
@ -811,45 +576,27 @@ class ChromiumElement(DrissionElement):
self.input(('\ue009', 'a', '\ue017'), clear=False)
def _input_focus(self):
"""输入前使元素获取焦点"""
try:
self.owner._run_cdp('DOM.focus', backendNodeId=self._backend_id)
except Exception:
self.click(by_js=None)
def focus(self):
"""使元素获取焦点"""
try:
self.owner._run_cdp('DOM.focus', backendNodeId=self._backend_id)
except Exception:
self._run_js('this.focus();')
def hover(self, offset_x=None, offset_y=None):
"""鼠标悬停可接受偏移量偏移量相对于元素左上角坐标。不传入offset_x和offset_y值时悬停在元素中点
:param offset_x: 相对元素左上角坐标的x轴偏移量
:param offset_y: 相对元素左上角坐标的y轴偏移量
:return: None
"""
self.owner.actions.move_to(self, offset_x=offset_x, offset_y=offset_y, duration=.1)
def drag(self, offset_x=0, offset_y=0, duration=.5):
"""拖拽当前元素到相对位置
:param offset_x: x变化值
:param offset_y: y变化值
:param duration: 拖动用时传入0即瞬间到达
:return: None
"""
curr_x, curr_y = self.rect.midpoint
offset_x += curr_x
offset_y += curr_y
self.drag_to((offset_x, offset_y), duration)
def drag_to(self, ele_or_loc, duration=.5):
"""拖拽当前元素,目标为另一个元素或坐标元组(x, y)
:param ele_or_loc: 另一个元素或坐标元组坐标为元素中点的坐标
:param duration: 拖动用时传入0即瞬间到达
:return: None
"""
if isinstance(ele_or_loc, ChromiumElement):
ele_or_loc = ele_or_loc.rect.midpoint
elif not isinstance(ele_or_loc, (list, tuple)):
@ -857,22 +604,12 @@ class ChromiumElement(DrissionElement):
self.owner.actions.hold(self).move_to(ele_or_loc, duration=duration).release()
def _get_obj_id(self, node_id=None, backend_id=None):
"""根据传入node id或backend id获取js中的object id
:param node_id: cdp中的node id
:param backend_id: backend id
:return: js中的object id
"""
if node_id:
return self.owner._run_cdp('DOM.resolveNode', nodeId=node_id)['object']['objectId']
else:
return self.owner._run_cdp('DOM.resolveNode', backendNodeId=backend_id)['object']['objectId']
def _get_node_id(self, obj_id=None, backend_id=None):
"""根据传入object id或backend id获取cdp中的node id
:param obj_id: js中的object id
:param backend_id: backend id
:return: cdp中的node id
"""
if obj_id:
return self.owner._run_cdp('DOM.requestNode', objectId=obj_id)['nodeId']
else:
@ -881,21 +618,15 @@ class ChromiumElement(DrissionElement):
return n['nodeId']
def _get_backend_id(self, node_id):
"""根据传入node id获取backend id
:param node_id:
:return: backend id
"""
n = self.owner._run_cdp('DOM.describeNode', nodeId=node_id)['node']
self._tag = n['localName']
return n['backendNodeId']
def _refresh_id(self):
"""根据backend id刷新其它id"""
self._obj_id = self._get_obj_id(backend_id=self._backend_id)
self._node_id = self._get_node_id(obj_id=self._obj_id)
def _get_ele_path(self, mode):
"""返获取绝对的css路径或xpath路径"""
if mode == 'xpath':
txt1 = 'let tag = el.nodeName.toLowerCase();'
txt3 = ''' && sib.nodeName.toLowerCase()==tag'''
@ -940,10 +671,6 @@ class ChromiumElement(DrissionElement):
return f'{t}' if mode == 'css' else t
def _set_file_input(self, files):
"""对上传控件写入路径
:param files: 文件路径列表或字符串字符串时多个文件用回车分隔
:return: None
"""
if isinstance(files, str):
files = files.split('\n')
files = [str(Path(i).absolute()) for i in files]
@ -954,11 +681,6 @@ class ShadowRoot(BaseElement):
"""ShadowRoot是用于处理ShadowRoot的类使用方法和ChromiumElement基本一致"""
def __init__(self, parent_ele, obj_id=None, backend_id=None):
"""
:param parent_ele: shadow root 所在父元素
:param obj_id: js中的object id
:param backend_id: cdp中的backend id
"""
super().__init__(parent_ele.owner)
self.tab = self.owner._tab
self.parent_ele = parent_ele
@ -977,13 +699,6 @@ class ShadowRoot(BaseElement):
return f'<ShadowRoot in {self.parent_ele}>'
def __call__(self, locator, index=1, timeout=None):
"""在内部查找元素
ele2 = ele1('@id=ele_id')
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:param timeout: 超时时间
:return: 元素对象或属性文本
"""
return self.ele(locator, index=index, timeout=timeout)
def __eq__(self, other):
@ -991,65 +706,34 @@ class ShadowRoot(BaseElement):
@property
def tag(self):
"""返回元素标签名"""
return 'shadow-root'
@property
def html(self):
"""返回outerHTML文本"""
return f'<shadow_root>{self.inner_html}</shadow_root>'
@property
def inner_html(self):
"""返回内部的html文本"""
return self._run_js('return this.innerHTML;')
@property
def states(self):
"""返回用于获取元素状态的对象"""
if self._states is None:
self._states = ShadowRootStates(self)
return self._states
def run_js(self, script, *args, as_expr=False, timeout=None):
"""运行javascript代码
:param script: js文本
:param args: 参数按顺序在js文本中对应arguments[0]arguments[1]...
:param as_expr: 是否作为表达式运行为True时args无效
:param timeout: js超时时间为None则使用页面timeouts.script设置
:return: 运行的结果
"""
return self._run_js(script, *args, as_expr=as_expr, timeout=timeout)
def _run_js(self, script, *args, as_expr=False, timeout=None):
"""运行javascript代码
:param script: js文本
:param args: 参数按顺序在js文本中对应arguments[0]arguments[1]...
:param as_expr: 是否作为表达式运行为True时args无效
:param timeout: js超时时间为None则使用页面timeouts.script设置
:return: 运行的结果
"""
return run_js(self, script, as_expr, self.owner.timeouts.script if timeout is None else timeout, args)
def run_async_js(self, script, *args, as_expr=False, timeout=None):
"""以异步方式执行js代码
:param script: js文本
:param args: 参数按顺序在js文本中对应arguments[0]arguments[1]...
:param as_expr: 是否作为表达式运行为True时args无效
:param timeout: js超时时间为None则使用页面timeouts.script设置
:return: None
"""
from threading import Thread
Thread(target=run_js, args=(self, script, as_expr,
self.owner.timeouts.script if timeout is None else timeout, args)).start()
def parent(self, level_or_loc=1, index=1, timeout=0):
"""返回上面某一级父元素,可指定层数或用查询语法定位
:param level_or_loc: 第几级父元素或定位符
:param index: 当level_or_loc传入定位符使用此参数选择第几个结果
:param timeout: 查找超时时间
:return: ChromiumElement对象
"""
if isinstance(level_or_loc, int):
loc = f'xpath:./ancestor-or-self::*[{level_or_loc}]'
@ -1067,12 +751,6 @@ class ShadowRoot(BaseElement):
return self.parent_ele._ele(loc, timeout=timeout, relative=True, raise_err=False, method='parent()')
def child(self, locator='', index=1, timeout=None):
"""返回直接子元素元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param index: 第几个查询结果1开始
:param timeout: 查找超时时间
:return: 直接子元素或节点文本组成的列表
"""
if not locator:
loc = '*'
else:
@ -1088,12 +766,6 @@ class ShadowRoot(BaseElement):
{'locator': locator, 'index': index, 'timeout': timeout})
def next(self, locator='', index=1, timeout=None):
"""返回当前元素后面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param locator: 用于筛选的查询语法
:param index: 第几个查询结果1开始
:param timeout: 查找超时时间
:return: ChromiumElement对象
"""
loc = get_loc(locator, True)
if loc[0] == 'css selector':
raise ValueError('此css selector语法不受支持请换成xpath。')
@ -1106,13 +778,6 @@ class ShadowRoot(BaseElement):
{'locator': locator, 'index': index, 'timeout': timeout})
def before(self, locator='', index=1, timeout=None):
"""返回文档中当前元素前面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个
查找范围不限同级元素而是整个DOM文档
:param locator: 用于筛选的查询语法
:param index: 前面第几个查询结果1开始
:param timeout: 查找超时时间
:return: 本元素前面的某个元素或节点
"""
loc = get_loc(locator, True)
if loc[0] == 'css selector':
raise ValueError('此css selector语法不受支持请换成xpath。')
@ -1125,23 +790,11 @@ class ShadowRoot(BaseElement):
{'locator': locator, 'index': index, 'timeout': timeout})
def after(self, locator='', index=1, timeout=None):
"""返回文档中此当前元素后面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个
查找范围不限同级元素而是整个DOM文档
:param locator: 用于筛选的查询语法
:param index: 后面第几个查询结果1开始
:param timeout: 查找超时时间
:return: 本元素后面的某个元素或节点
"""
nodes = self.afters(locator=locator, timeout=timeout)
return nodes[index - 1] if nodes else NoneElement(self.owner, 'after()',
{'locator': locator, 'index': index, 'timeout': timeout})
def children(self, locator='', timeout=None):
"""返回当前元素符合条件的直接子元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param timeout: 查找超时时间
:return: 直接子元素或节点文本组成的列表
"""
if not locator:
loc = '*'
else:
@ -1154,11 +807,6 @@ class ShadowRoot(BaseElement):
return self._ele(loc, index=None, relative=True, timeout=timeout)
def nexts(self, locator='', timeout=None):
"""返回当前元素后面符合条件的同级元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param timeout: 查找超时时间
:return: ChromiumElement对象组成的列表
"""
loc = get_loc(locator, True)
if loc[0] == 'css selector':
raise ValueError('此css selector语法不受支持请换成xpath。')
@ -1168,12 +816,6 @@ class ShadowRoot(BaseElement):
return self.parent_ele._ele(xpath, index=None, relative=True, timeout=timeout)
def befores(self, locator='', timeout=None):
"""返回文档中当前元素前面符合条件的元素或节点组成的列表,可用查询语法筛选
查找范围不限同级元素而是整个DOM文档
:param locator: 用于筛选的查询语法
:param timeout: 查找超时时间
:return: 本元素前面的元素或节点组成的列表
"""
loc = get_loc(locator, True)
if loc[0] == 'css selector':
raise ValueError('此css selector语法不受支持请换成xpath。')
@ -1183,63 +825,27 @@ class ShadowRoot(BaseElement):
return self.parent_ele._ele(xpath, index=None, relative=True, timeout=timeout)
def afters(self, locator='', timeout=None):
"""返回文档中当前元素后面符合条件的元素或节点组成的列表,可用查询语法筛选
查找范围不限同级元素而是整个DOM文档
:param locator: 用于筛选的查询语法
:param timeout: 查找超时时间
:return: 本元素后面的元素或节点组成的列表
"""
eles1 = self.nexts(locator)
loc = get_loc(locator, True)[1].lstrip('./')
xpath = f'xpath:./following::{loc}'
return eles1 + self.parent_ele._ele(xpath, index=None, relative=True, timeout=timeout)
def ele(self, locator, index=1, timeout=None):
"""返回当前元素下级符合条件的一个元素
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 获取第几个元素从1开始可传入负数获取倒数第几个
:param timeout: 查找元素超时时间默认与元素所在页面等待时间一致
:return: ChromiumElement对象
"""
return self._ele(locator, timeout, index=index, method='ele()')
def eles(self, locator, timeout=None):
"""返回当前元素下级所有符合条件的子元素
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 查找元素超时时间默认与元素所在页面等待时间一致
:return: ChromiumElement对象组成的列表
"""
return self._ele(locator, timeout=timeout, index=None)
def s_ele(self, locator=None, index=1, timeout=None):
"""查找一个符合条件的元素以SessionElement形式返回处理复杂页面时效率很高
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:param timeout: 查找元素超时时间默认与元素所在页面等待时间一致
:return: SessionElement对象或属性文本
"""
return (make_session_ele(self, locator, index=index, method='s_ele()')
if self.ele(locator, index=index, timeout=timeout)
else NoneElement(self, method='s_ele()', args={'locator': locator, 'index': index}))
def s_eles(self, locator, timeout=None):
"""查找所有符合条件的元素以SessionElement列表形式返回处理复杂页面时效率很高
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 查找元素超时时间默认与元素所在页面等待时间一致
:return: SessionElement对象
"""
return (make_session_ele(self, locator, index=None)
if self.ele(locator, timeout=timeout) else SessionElementsList())
def _find_elements(self, locator, timeout=None, index=1, relative=False, raise_err=None):
"""返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 查找元素超时时间
:param index: 第几个结果从1开始可传入负数获取倒数第几个为None返回所有
:param relative: MixTab用的表示是否相对定位的参数
:param raise_err: 找不到元素是是否抛出异常为None时根据全局设置
:return: ChromiumElement对象或其组成的列表
"""
loc = get_loc(locator, css_mode=False)
if loc[0] == 'css selector' and str(loc[1]).startswith(':root'):
loc = loc[0], loc[1][5:]
@ -1301,29 +907,18 @@ class ShadowRoot(BaseElement):
return NoneElement(self.owner) if index is not None else ChromiumElementsList()
def _get_node_id(self, obj_id):
"""返回元素node id"""
return self.owner._run_cdp('DOM.requestNode', objectId=obj_id)['nodeId']
def _get_obj_id(self, back_id):
"""返回元素object id"""
return self.owner._run_cdp('DOM.resolveNode', backendNodeId=back_id)['object']['objectId']
def _get_backend_id(self, node_id):
"""返回元素object id"""
r = self.owner._run_cdp('DOM.describeNode', nodeId=node_id)['node']
self._tag = r['localName'].lower()
return r['backendNodeId']
def find_in_chromium_ele(ele, locator, index=1, timeout=None, relative=True):
"""在chromium元素中查找
:param ele: ChromiumElement对象
:param locator: 元素定位元组
:param index: 第几个结果从1开始可传入负数获取倒数第几个为None返回所有
:param timeout: 查找元素超时时间
:param relative: MixTab用于标记是否相对定位使用
:return: 返回ChromiumElement元素或它们组成的列表
"""
# ---------------处理定位符---------------
if isinstance(locator, (str, tuple)):
loc = get_loc(locator)
@ -1348,14 +943,6 @@ def find_in_chromium_ele(ele, locator, index=1, timeout=None, relative=True):
def find_by_xpath(ele, xpath, index, timeout, relative=True):
"""执行用xpath在元素中查找元素
:param ele: 在此元素中查找
:param xpath: 查找语句
:param index: 第几个结果从1开始可传入负数获取倒数第几个为None返回所有
:param timeout: 超时时间
:param relative: 是否相对定位
:return: ChromiumElement或其组成的列表
"""
type_txt = '9' if index == 1 else '7'
node_txt = 'this.contentDocument' if ele.tag in __FRAME_ELEMENT__ and not relative else 'this'
js = make_js_for_find_ele_by_xpath(xpath, type_txt, node_txt)
@ -1419,13 +1006,6 @@ def find_by_xpath(ele, xpath, index, timeout, relative=True):
def find_by_css(ele, selector, index, timeout):
"""执行用css selector在元素中查找元素
:param ele: 在此元素中查找
:param selector: 查找语句
:param index: 第几个结果从1开始可传入负数获取倒数第几个为None返回所有
:param timeout: 超时时间
:return: ChromiumElement或其组成的列表
"""
selector = selector.replace('"', r'\"')
find_all = '' if index == 1 else 'All'
node_txt = 'this.contentDocument' if ele.tag in ('iframe', 'frame', 'shadow-root') else 'this'
@ -1465,14 +1045,6 @@ def find_by_css(ele, selector, index, timeout):
def make_chromium_eles(page, _ids, index=1, is_obj_id=True, ele_only=False):
"""根据node id或object id生成相应元素对象
:param page: ChromiumPage对象
:param _ids: 元素的id列表
:param index: 获取第几个为None返回全部
:param is_obj_id: 传入的id是obj id还是node id
:param ele_only: 是否只返回ele在页面查找元素时生效
:return: 浏览器元素对象或它们组成的列表生成失败返回False
"""
if is_obj_id:
get_node_func = _get_node_by_obj_id
else:
@ -1551,12 +1123,6 @@ def _make_ele(page, obj_id, node):
def make_js_for_find_ele_by_xpath(xpath, type_txt, node_txt):
"""生成用xpath在元素中查找元素的js文本
:param xpath: xpath文本
:param type_txt: 查找类型
:param node_txt: 节点类型
:return: js文本
"""
for_txt = ''
# 获取第一个元素、节点或属性
@ -1593,14 +1159,6 @@ else{a.push(e.snapshotItem(i));}}"""
def run_js(page_or_ele, script, as_expr, timeout, args=None):
"""运行javascript代码
:param page_or_ele: 页面对象或元素对象
:param script: js文本
:param as_expr: 是否作为表达式运行为True时args无效
:param timeout: 超时时间
:param args: 参数按顺序在js文本中对应arguments[0]arguments[1]...
:return: js执行结果
"""
if isinstance(page_or_ele, (ChromiumElement, ShadowRoot)):
is_page = False
page = page_or_ele.owner
@ -1663,7 +1221,6 @@ def run_js(page_or_ele, script, as_expr, timeout, args=None):
def parse_js_result(page, ele, result, end_time):
"""解析js返回的结果"""
if 'unserializableValue' in result:
return result['unserializableValue']
@ -1713,7 +1270,6 @@ def parse_js_result(page, ele, result, end_time):
def convert_argument(arg):
"""把参数转换成js能够接收的形式"""
if isinstance(arg, ChromiumElement):
return {'objectId': arg._obj_id}
@ -1731,19 +1287,14 @@ def convert_argument(arg):
class Pseudo(object):
def __init__(self, ele):
"""
:param ele: ChromiumElement
"""
self._ele = ele
@property
def before(self):
"""返回当前元素的::before伪元素内容"""
return self._ele.style('content', 'before')
@property
def after(self):
"""返回当前元素的::after伪元素内容"""
return self._ele.style('content', 'after')

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,6 @@ from .._functions.web import get_ele_txt, make_absolute_link
class SessionElement(DrissionElement):
"""session模式的元素对象包装了一个lxml的Element对象并封装了常用功能"""
def __init__(self, ele, owner=None):
"""初始化对象
@ -39,13 +38,6 @@ class SessionElement(DrissionElement):
return f'<SessionElement {self.tag} {" ".join(attrs)}>'
def __call__(self, locator, index=1, timeout=None):
"""在内部查找元素
ele2 = ele1('@id=ele_id')
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 第几个元素从1开始可传入负数获取倒数第几个
:param timeout: 不起实际作用
:return: SessionElement对象或属性文本
"""
return self.ele(locator, index=index)
def __eq__(self, other):
@ -53,150 +45,65 @@ class SessionElement(DrissionElement):
@property
def tag(self):
"""返回元素类型"""
return self._inner_ele.tag
@property
def html(self):
"""返回outerHTML文本"""
html = tostring(self._inner_ele, method="html").decode()
return unescape(html[:html.rfind('>') + 1]) # tostring()会把跟紧元素的文本节点也带上,因此要去掉
@property
def inner_html(self):
"""返回元素innerHTML文本"""
r = match(r'<.*?>(.*)</.*?>', self.html, flags=DOTALL)
return '' if not r else r.group(1)
@property
def attrs(self):
"""返回元素所有属性及值"""
return {attr: self.attr(attr) for attr, val in self.inner_ele.items()}
@property
def text(self):
"""返回元素内所有文本"""
return get_ele_txt(self)
@property
def raw_text(self):
"""返回未格式化处理的元素内文本"""
return str(self._inner_ele.text_content())
def parent(self, level_or_loc=1, index=1):
"""返回上面某一级父元素,可指定层数或用查询语法定位
:param level_or_loc: 第几级父元素或定位符
:param index: 当level_or_loc传入定位符使用此参数选择第几个结果
:return: 上级元素对象
"""
return super().parent(level_or_loc, index)
def child(self, locator='', index=1, timeout=None, ele_only=True):
"""返回当前元素的一个符合条件的直接子元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param locator: 用于筛选的查询语法
:param index: 第几个查询结果1开始
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 直接子元素或节点文本
"""
return super().child(locator, index, timeout, ele_only=ele_only)
def prev(self, locator='', index=1, timeout=None, ele_only=True):
"""返回当前元素前面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param locator: 用于筛选的查询语法
:param index: 前面第几个查询结果1开始
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 同级元素
"""
return super().prev(locator, index, timeout, ele_only=ele_only)
def next(self, locator='', index=1, timeout=None, ele_only=True):
"""返回当前元素后面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param locator: 用于筛选的查询语法
:param index: 第几个查询结果1开始
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 同级元素
"""
return super().next(locator, index, timeout, ele_only=ele_only)
def before(self, locator='', index=1, timeout=None, ele_only=True):
"""返回文档中当前元素前面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个
查找范围不限同级元素而是整个DOM文档
:param locator: 用于筛选的查询语法
:param index: 前面第几个查询结果1开始
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素前面的某个元素或节点
"""
return super().before(locator, index, timeout, ele_only=ele_only)
def after(self, locator='', index=1, timeout=None, ele_only=True):
"""返回文档中此当前元素后面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个
查找范围不限同级元素而是整个DOM文档
:param locator: 用于筛选的查询语法
:param index: 第几个查询结果1开始
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素后面的某个元素或节点
"""
return super().after(locator, index, timeout, ele_only=ele_only)
def children(self, locator='', timeout=0, ele_only=True):
"""返回当前元素符合条件的直接子元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 直接子元素或节点文本组成的列表
"""
return SessionElementsList(self.owner, super().children(locator, timeout, ele_only=ele_only))
def prevs(self, locator='', timeout=None, ele_only=True):
"""返回当前元素前面符合条件的同级元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 同级元素或节点文本组成的列表
"""
return SessionElementsList(self.owner, super().prevs(locator, timeout, ele_only=ele_only))
def nexts(self, locator='', timeout=None, ele_only=True):
"""返回当前元素后面符合条件的同级元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 同级元素或节点文本组成的列表
"""
return SessionElementsList(self.owner, super().nexts(locator, timeout, ele_only=ele_only))
def befores(self, locator='', timeout=None, ele_only=True):
"""返回文档中当前元素前面符合条件的元素或节点组成的列表,可用查询语法筛选
查找范围不限同级元素而是整个DOM文档
:param locator: 用于筛选的查询语法
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素前面的元素或节点组成的列表
"""
return SessionElementsList(self.owner, super().befores(locator, timeout, ele_only=ele_only))
def afters(self, locator='', timeout=None, ele_only=True):
"""返回文档中当前元素后面符合条件的元素或节点组成的列表,可用查询语法筛选
查找范围不限同级元素而是整个DOM文档
:param locator: 用于筛选的查询语法
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素后面的元素或节点组成的列表
"""
return SessionElementsList(self.owner, super().afters(locator, timeout, ele_only=ele_only))
def attr(self, name):
"""返回attribute属性值
:param name: 属性名
:return: 属性值文本没有该属性返回None
"""
# 获取href属性时返回绝对url
if name == 'href':
if name == 'href': # 获取href属性时返回绝对url
link = self.inner_ele.get('href')
# 若为链接为None、js或邮件直接返回
if not link or link.lower().startswith(('javascript:', 'mailto:')):
@ -224,53 +131,21 @@ class SessionElement(DrissionElement):
return self.inner_ele.get(name)
def ele(self, locator, index=1, timeout=None):
"""返回当前元素下级符合条件的一个元素、属性或节点文本
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 第几个元素从1开始可传入负数获取倒数第几个
:param timeout: 不起实际作用
:return: SessionElement对象或属性文本
"""
return self._ele(locator, index=index, method='ele()')
def eles(self, locator, timeout=None):
"""返回当前元素下级所有符合条件的子元素、属性或节点文本
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 不起实际作用
:return: SessionElement对象或属性文本组成的列表
"""
return self._ele(locator, index=None)
def s_ele(self, locator=None, index=1):
"""返回当前元素下级符合条件的一个元素、属性或节点文本
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:return: SessionElement对象或属性文本
"""
return self._ele(locator, index=index, method='s_ele()')
def s_eles(self, locator):
"""返回当前元素下级所有符合条件的子元素、属性或节点文本
:param locator: 元素的定位信息可以是loc元组或查询字符串
:return: SessionElement对象或属性文本组成的列表
"""
return self._ele(locator, index=None)
def _find_elements(self, locator, timeout=None, index=1, relative=False, raise_err=None):
"""返回当前元素下级符合条件的子元素、属性或节点文本
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 不起实际作用用于和父类对应
:param index: 第几个结果从1开始可传入负数获取倒数第几个为None返回所有
:param relative: MixTab用的表示是否相对定位的参数
:param raise_err: 找不到元素是是否抛出异常为None时根据全局设置
:return: SessionElement对象
"""
return make_session_ele(self, locator, index=index)
def _get_ele_path(self, mode):
"""获取css路径或xpath路径
:param mode: 'css' 'xpath'
:return: css路径或xpath路径
"""
path_str = ''
ele = self
@ -292,14 +167,6 @@ class SessionElement(DrissionElement):
def make_session_ele(html_or_ele, loc=None, index=1, method=None):
"""从接收到的对象或html文本中查找元素返回SessionElement对象
如要直接从html生成SessionElement而不在下级查找loc输入None即可
:param html_or_ele: html文本BaseParser对象
:param loc: 定位元组或字符串为None时不在下级查找返回根元素
:param index: 获取第几个元素从1开始可传入负数获取倒数第几个None获取所有
:param method: 调用此方法的方法
:return: 返回SessionElement元素或列表或属性文本
"""
# ---------------处理定位符---------------
if not loc:
if isinstance(html_or_ele, SessionElement):

View File

@ -18,6 +18,7 @@ from .._pages.session_page import SessionPage
class SessionElement(DrissionElement):
"""静态元素对象"""
def __init__(self, ele: HtmlElement, owner: Union[SessionPage, None] = None):
self._inner_ele: HtmlElement = ...
@ -32,116 +33,273 @@ class SessionElement(DrissionElement):
def __call__(self,
locator: Union[Tuple[str, str], str],
index: int = 1,
timeout: float = None) -> SessionElement: ...
timeout: float = None) -> SessionElement:
"""在内部查找元素
ele2 = ele1('@id=ele_id')
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 第几个元素从1开始可传入负数获取倒数第几个
:param timeout: 不起实际作用
:return: SessionElement对象或属性文本
"""
...
def __eq__(self, other: SessionElement) -> bool: ...
@property
def tag(self) -> str: ...
def tag(self) -> str:
"""返回元素类型"""
...
@property
def html(self) -> str: ...
def html(self) -> str:
"""返回outerHTML文本"""
...
@property
def inner_html(self) -> str: ...
def inner_html(self) -> str:
"""返回元素innerHTML文本"""
...
@property
def attrs(self) -> dict: ...
def attrs(self) -> dict:
"""返回元素所有属性及值"""
...
@property
def text(self) -> str: ...
def text(self) -> str:
"""返回元素内文本"""
...
@property
def raw_text(self) -> str: ...
def raw_text(self) -> str:
"""返回未格式化处理的元素内文本"""
...
def parent(self,
level_or_loc: Union[tuple, str, int] = 1,
index: int = 1) -> SessionElement: ...
index: int = 1) -> SessionElement:
"""返回上面某一级父元素,可指定层数或用查询语法定位
:param level_or_loc: 第几级父元素或定位符
:param index: 当level_or_loc传入定位符使用此参数选择第几个结果
:return: 上级元素对象
"""
...
def child(self,
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[SessionElement, str]: ...
ele_only: bool = True) -> Union[SessionElement, str]:
"""返回当前元素的一个符合条件的直接子元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param locator: 用于筛选的查询语法
:param index: 第几个查询结果1开始
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 直接子元素或节点文本
"""
...
def prev(self,
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[SessionElement, str]: ...
ele_only: bool = True) -> Union[SessionElement, str]:
"""返回当前元素前面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param locator: 用于筛选的查询语法
:param index: 前面第几个查询结果1开始
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 同级元素
"""
...
def next(self,
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[SessionElement, str]: ...
ele_only: bool = True) -> Union[SessionElement, str]:
"""返回当前元素后面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param locator: 用于筛选的查询语法
:param index: 第几个查询结果1开始
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 同级元素
"""
...
def before(self,
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[SessionElement, str]: ...
ele_only: bool = True) -> Union[SessionElement, str]:
"""返回文档中当前元素前面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个
查找范围不限同级元素而是整个DOM文档
:param locator: 用于筛选的查询语法
:param index: 前面第几个查询结果1开始
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素前面的某个元素或节点
"""
...
def after(self,
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[SessionElement, str]: ...
ele_only: bool = True) -> Union[SessionElement, str]:
"""返回文档中此当前元素后面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个
查找范围不限同级元素而是整个DOM文档
:param locator: 用于筛选的查询语法
:param index: 第几个查询结果1开始
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素后面的某个元素或节点
"""
...
def children(self,
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> Union[SessionElementsList, List[Union[SessionElement, str]]]: ...
ele_only: bool = True) -> Union[SessionElementsList, List[Union[SessionElement, str]]]:
"""返回当前元素符合条件的直接子元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 直接子元素或节点文本组成的列表
"""
...
def prevs(self,
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> Union[SessionElementsList, List[Union[SessionElement, str]]]: ...
ele_only: bool = True) -> Union[SessionElementsList, List[Union[SessionElement, str]]]:
"""返回当前元素前面符合条件的同级元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 同级元素或节点文本组成的列表
"""
...
def nexts(self,
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> Union[SessionElementsList, List[Union[SessionElement, str]]]: ...
ele_only: bool = True) -> Union[SessionElementsList, List[Union[SessionElement, str]]]:
"""返回当前元素后面符合条件的同级元素或节点组成的列表,可用查询语法筛选
:param locator: 用于筛选的查询语法
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 同级元素或节点文本组成的列表
"""
...
def befores(self,
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> Union[SessionElementsList, List[Union[SessionElement, str]]]: ...
ele_only: bool = True) -> Union[SessionElementsList, List[Union[SessionElement, str]]]:
"""返回文档中当前元素前面符合条件的元素或节点组成的列表,可用查询语法筛选
查找范围不限同级元素而是整个DOM文档
:param locator: 用于筛选的查询语法
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素前面的元素或节点组成的列表
"""
...
def afters(self,
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> Union[SessionElementsList, List[Union[SessionElement, str]]]: ...
ele_only: bool = True) -> Union[SessionElementsList, List[Union[SessionElement, str]]]:
"""返回文档中当前元素后面符合条件的元素或节点组成的列表,可用查询语法筛选
查找范围不限同级元素而是整个DOM文档
:param locator: 用于筛选的查询语法
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素后面的元素或节点组成的列表
"""
...
def attr(self, name: str) -> Optional[str]: ...
def attr(self, name: str) -> Optional[str]:
"""返回attribute属性值
:param name: 属性名
:return: 属性值文本没有该属性返回None
"""
...
def ele(self,
locator: Union[Tuple[str, str], str],
index: int = 1,
timeout: float = None) -> SessionElement: ...
timeout: float = None) -> SessionElement:
"""返回当前元素下级符合条件的一个元素、属性或节点文本
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 第几个元素从1开始可传入负数获取倒数第几个
:param timeout: 不起实际作用
:return: SessionElement对象或属性文本
"""
...
def eles(self,
locator: Union[Tuple[str, str], str],
timeout: float = None) -> SessionElementsList: ...
timeout: float = None) -> SessionElementsList:
"""返回当前元素下级所有符合条件的子元素、属性或节点文本
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 不起实际作用
:return: SessionElement对象或属性文本组成的列表
"""
...
def s_ele(self,
locator: Union[Tuple[str, str], str] = None,
index: int = 1) -> SessionElement: ...
index: int = 1) -> SessionElement:
"""返回当前元素下级符合条件的一个元素、属性或节点文本
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:return: SessionElement对象或属性文本
"""
...
def s_eles(self, locator: Union[Tuple[str, str], str]) -> SessionElementsList: ...
def s_eles(self, locator: Union[Tuple[str, str], str]) -> SessionElementsList:
"""返回当前元素下级所有符合条件的子元素、属性或节点文本
:param locator: 元素的定位信息可以是loc元组或查询字符串
:return: SessionElement对象或属性文本组成的列表
"""
...
def _find_elements(self,
locator: Union[Tuple[str, str], str],
timeout: float = None,
index: Optional[int] = 1,
relative: bool = False,
raise_err: bool = None) -> Union[SessionElement, SessionElementsList]: ...
raise_err: bool = None) -> Union[SessionElement, SessionElementsList]:
"""返回当前元素下级符合条件的子元素、属性或节点文本
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 不起实际作用用于和父类对应
:param index: 第几个结果从1开始可传入负数获取倒数第几个为None返回所有
:param relative: MixTab用的表示是否相对定位的参数
:param raise_err: 找不到元素是是否抛出异常为None时根据全局设置
:return: SessionElement对象
"""
...
def _get_ele_path(self, mode: str) -> str: ...
def _get_ele_path(self, mode: str) -> str:
"""获取css路径或xpath路径
:param mode: 'css' 'xpath'
:return: css路径或xpath路径
"""
...
def make_session_ele(html_or_ele: Union[str, SessionElement, SessionPage, ChromiumElement, BaseElement, ChromiumFrame,
ChromiumBase],
loc: Union[str, Tuple[str, str]] = None,
index: Optional[int] = 1,
method: Optional[str] = None) -> Union[SessionElement, SessionElementsList]: ...
method: Optional[str] = None) -> Union[SessionElement, SessionElementsList]:
"""从接收到的对象或html文本中查找元素返回SessionElement对象
如要直接从html生成SessionElement而不在下级查找loc输入None即可
:param html_or_ele: html文本BaseParser对象
:param loc: 定位元组或字符串为None时不在下级查找返回根元素
:param index: 获取第几个元素从1开始可传入负数获取倒数第几个None获取所有
:param method: 调用此方法的方法
:return: 返回SessionElement元素或列表或属性文本
"""
...

View File

@ -209,6 +209,27 @@ def format_cookie(cookie):
elif cookie['name'].startswith('__Secure-'):
cookie['secure'] = True
if 'sameSite' in cookie:
sameSite = cookie['sameSite']
if sameSite in (None, False):
cookie.pop('sameSite')
elif sameSite not in ('None', 'Lax', 'Strict'):
raise ValueError(f'{cookie}\nsameSite字段必须为"None""Lax""Strict"之一。')
if 'priority' in cookie:
priority = cookie['priority']
if priority in (None, False):
cookie.pop('priority')
elif priority not in ('Low', 'Medium', 'High'):
raise ValueError(f'{cookie}\npriority字段必须为"Low""Medium""High"之一。')
if 'sourceScheme' in cookie:
sourceScheme = cookie['sourceScheme']
if sourceScheme in (None, False):
cookie.pop('sourceScheme')
elif sourceScheme not in ('Unset', 'NonSecure', 'Secure'):
raise ValueError(f'{cookie}\nsourceScheme字段必须为"Unset""NonSecure""Secure"之一。')
return cookie
@ -221,10 +242,15 @@ class CookiesList(list):
"""以str格式返回只包含name和value字段"""
return '; '.join([f'{c["name"]}={c["value"]}' for c in self])
def as_json(self):
"""以json格式返回"""
from json import dumps
return dumps(self)
def _dict_cookies_to_tuple(cookies: dict):
"""把dict形式的cookies转换为tuple形式
:param cookies: 单个或多个cookies单个时包含'name''value'
:param cookies: 单个或多个cookies单个时包含 'name' 'value'
:return: 多个dict格式cookies组成的列表
"""
if 'name' in cookies and 'value' in cookies: # 单个cookie

View File

@ -41,4 +41,6 @@ class CookiesList(list):
def as_str(self) -> str: ...
def as_json(self) -> str: ...
def __next__(self) -> dict: ...

View File

@ -11,36 +11,28 @@ from ..errors import CDPError, NoRectError, PageDisconnectedError, ElementLostEr
class ElementStates(object):
def __init__(self, ele):
"""
:param ele: ChromiumElement
"""
self._ele = ele
@property
def is_selected(self):
"""返回列表元素是否被选择"""
return self._ele._run_js('return this.selected;')
@property
def is_checked(self):
"""返回元素是否被选择"""
return self._ele._run_js('return this.checked;')
@property
def is_displayed(self):
"""返回元素是否显示"""
return not (self._ele.style('visibility') == 'hidden' or
self._ele._run_js('return this.offsetParent === null;')
or self._ele.style('display') == 'none' or self._ele.property('hidden'))
@property
def is_enabled(self):
"""返回元素是否可用"""
return not self._ele._run_js('return this.disabled;')
@property
def is_alive(self):
"""返回元素是否仍在DOM中"""
try:
return self._ele.owner._run_cdp('DOM.describeNode',
backendNodeId=self._ele._backend_id)['node']['nodeId'] != 0
@ -49,13 +41,11 @@ class ElementStates(object):
@property
def is_in_viewport(self):
"""返回元素是否出现在视口中以元素click_point为判断"""
x, y = self._ele.rect.click_point
return location_in_viewport(self._ele.owner, x, y) if x else False
@property
def is_whole_in_viewport(self):
"""返回元素是否整个都在视口内"""
x1, y1 = self._ele.rect.location
w, h = self._ele.rect.size
x2, y2 = x1 + w, y1 + h
@ -63,7 +53,6 @@ class ElementStates(object):
@property
def is_covered(self):
"""返回元素是否被覆盖与是否在视口中无关如被覆盖返回覆盖元素的backend id否则返回False"""
lx, ly = self._ele.rect.click_point
try:
bid = self._ele.owner._run_cdp('DOM.getNodeForLocation', x=int(lx), y=int(ly)).get('backendNodeId')
@ -73,12 +62,10 @@ class ElementStates(object):
@property
def is_clickable(self):
"""返回元素是否可被模拟点击,从是否有大小、是否可用、是否显示、是否响应点击判断,不判断是否被遮挡"""
return self.has_rect and self.is_enabled and self.is_displayed and self._ele.style('pointer-events') != 'none'
@property
def has_rect(self):
"""返回元素是否拥有位置和大小没有返回False有返回四个角在页面中坐标组成的列表"""
try:
return self._ele.rect.corners
except NoRectError:
@ -87,19 +74,14 @@ class ElementStates(object):
class ShadowRootStates(object):
def __init__(self, ele):
"""
:param ele: ChromiumElement
"""
self._ele = ele
@property
def is_enabled(self):
"""返回元素是否可用"""
return not self._ele._run_js('return this.disabled;')
@property
def is_alive(self):
"""返回元素是否仍在DOM中"""
try:
return self._ele.owner._run_cdp('DOM.describeNode',
backendNodeId=self._ele._backend_id)['node']['nodeId'] != 0
@ -107,23 +89,32 @@ class ShadowRootStates(object):
return False
class BrowserStates(object):
def __init__(self, browser):
self._browser = browser
def is_alive(self):
return self._browser._driver.is_running
def is_headless(self):
return self._browser._is_headless
def is_existed(self):
return self._browser._is_exists
class PageStates(object):
"""Page对象、Tab对象使用"""
def __init__(self, owner):
"""
:param owner: ChromiumBase对象
"""
self._owner = owner
@property
def is_loading(self):
"""返回页面是否在加载状态"""
return self._owner._is_loading
@property
def is_alive(self):
"""返回页面对象是否仍然可用"""
try:
self._owner._run_cdp('Page.getLayoutMetrics')
return True
@ -132,30 +123,23 @@ class PageStates(object):
@property
def ready_state(self):
"""返回当前页面加载状态,'connecting' 'loading' 'interactive' 'complete'"""
return self._owner._ready_state
@property
def has_alert(self):
"""返回当前页面是否存在弹窗"""
return self._owner._has_alert
class FrameStates(object):
def __init__(self, frame):
"""
:param frame: ChromiumFrame对象
"""
self._frame = frame
@property
def is_loading(self):
"""返回页面是否在加载状态"""
return self._frame._is_loading
@property
def is_alive(self):
"""返回frame元素是否可用且里面仍挂载有frame"""
try:
node = self._frame._target_page._run_cdp('DOM.describeNode',
backendNodeId=self._frame._frame_ele._backend_id)['node']
@ -165,17 +149,14 @@ class FrameStates(object):
@property
def ready_state(self):
"""返回加载状态"""
return self._frame._ready_state
@property
def is_displayed(self):
"""返回iframe是否显示"""
return not (self._frame.frame_ele.style('visibility') == 'hidden'
or self._frame.frame_ele._run_js('return this.offsetParent === null;')
or self._frame.frame_ele.style('display') == 'none')
@property
def has_alert(self):
"""返回当前页面是否存在弹窗"""
return self._frame._has_alert

View File

@ -7,92 +7,174 @@
"""
from typing import Union, Tuple, List, Optional, Literal
from .._base.browser import Chromium
from .._elements.chromium_element import ShadowRoot, ChromiumElement
from .._pages.chromium_base import ChromiumBase
from .._pages.chromium_frame import ChromiumFrame
class ElementStates(object):
_ele: ChromiumElement = ...
def __init__(self, ele: ChromiumElement):
self._ele: ChromiumElement = ...
"""
:param ele: ChromiumElement
"""
...
@property
def is_selected(self) -> bool: ...
def is_selected(self) -> bool:
"""返回列表元素是否被选择"""
...
@property
def is_checked(self) -> bool: ...
def is_checked(self) -> bool:
"""返回元素是否被选择"""
...
@property
def is_displayed(self) -> bool: ...
def is_displayed(self) -> bool:
"""返回元素是否显示"""
...
@property
def is_enabled(self) -> bool: ...
def is_enabled(self) -> bool:
"""返回元素是否可用"""
...
@property
def is_alive(self) -> bool: ...
def is_alive(self) -> bool:
"""返回元素是否仍在DOM中"""
...
@property
def is_in_viewport(self) -> bool: ...
def is_in_viewport(self) -> bool:
"""返回元素是否出现在视口中以元素click_point为判断"""
...
@property
def is_whole_in_viewport(self) -> bool: ...
def is_whole_in_viewport(self) -> bool:
"""返回元素是否整个都在视口内"""
...
@property
def is_covered(self) -> Union[Literal[False], int]: ...
def is_covered(self) -> Union[Literal[False], int]:
"""返回元素是否被覆盖与是否在视口中无关如被覆盖返回覆盖元素的backend id否则返回False"""
...
@property
def is_clickable(self) -> bool: ...
def is_clickable(self) -> bool:
"""返回元素是否可被模拟点击,从是否有大小、是否可用、是否显示、是否响应点击判断,不判断是否被遮挡"""
...
@property
def has_rect(self) -> Union[Literal[False], List[Tuple[float, float]]]: ...
def has_rect(self) -> Union[Literal[False], List[Tuple[float, float]]]:
"""返回元素是否拥有位置和大小没有返回False有返回四个角在页面中坐标组成的列表"""
...
class ShadowRootStates(object):
_ele: ShadowRoot = ...
def __init__(self, ele: ShadowRoot):
"""
:param ele: ChromiumElement
"""
self._ele: ShadowRoot = ...
...
@property
def is_enabled(self) -> bool: ...
def is_enabled(self) -> bool:
"""返回元素是否可用"""
...
@property
def is_alive(self) -> bool: ...
def is_alive(self) -> bool:
"""返回元素是否仍在DOM中"""
...
class BrowserStates(object):
_browser: Chromium = ...
def __init__(self, browser: Chromium):
"""
:param browser: Chromium对象
"""
...
def is_alive(self) -> bool:
"""返回浏览器是否仍可用"""
...
def is_headless(self) -> bool:
"""返回浏览器是否无头模式"""
...
def is_existed(self) -> bool:
"""返回浏览器是否接管的"""
...
class PageStates(object):
_owner: ChromiumBase = ...
def __init__(self, owner: ChromiumBase):
self._owner: ChromiumBase = ...
"""
:param owner: ChromiumBase对象
"""
...
@property
def is_loading(self) -> bool: ...
def is_loading(self) -> bool:
"""返回页面是否在加载状态"""
...
@property
def is_alive(self) -> bool: ...
def is_alive(self) -> bool:
"""返回页面对象是否仍然可用"""
...
@property
def ready_state(self) -> Optional[str]: ...
def ready_state(self) -> Optional[str]:
"""返回当前页面加载状态,'connecting' 'loading' 'interactive' 'complete'"""
...
@property
def has_alert(self) -> bool: ...
def has_alert(self) -> bool:
"""返回当前页面是否存在弹窗"""
...
class FrameStates(object):
_frame: ChromiumFrame = ...
def __init__(self, frame: ChromiumFrame):
self._frame: ChromiumFrame = ...
"""
:param frame: ChromiumFrame对象
"""
...
@property
def is_loading(self) -> bool: ...
def is_loading(self) -> bool:
"""返回页面是否在加载状态"""
...
@property
def is_alive(self) -> bool: ...
def is_alive(self) -> bool:
"""返回frame元素是否可用且里面仍挂载有frame"""
...
@property
def ready_state(self) -> str: ...
def ready_state(self) -> str:
"""返回加载状态"""
...
@property
def is_displayed(self) -> bool: ...
def is_displayed(self) -> bool:
"""返回iframe是否显示"""
...
@property
def has_alert(self) -> bool: ...
def has_alert(self) -> bool:
"""返回当前页面是否存在弹窗"""
...

View File

@ -10,6 +10,7 @@ from ._elements.none_element import NoneElement
from ._elements.session_element import SessionElement
from ._pages.chromium_frame import ChromiumFrame
from ._pages.tabs import ChromiumTab, MixTab
from ._pages.tabs import MixTab as WebPageTab
__all__ = ['ChromiumElement', 'ShadowRoot', 'NoneElement', 'SessionElement', 'ChromiumFrame', 'ChromiumTab',
'MixTab']
'MixTab', 'WebPageTab']