Merge pull request !38 from g1879/dev
This commit is contained in:
g1879 2024-01-27 06:43:45 +00:00 committed by Gitee
commit ff016d6095
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
52 changed files with 1107 additions and 1028 deletions

View File

@ -14,4 +14,4 @@ from ._configs.chromium_options import ChromiumOptions
from ._configs.session_options import SessionOptions
__all__ = ['ChromiumPage', 'ChromiumOptions', 'SessionOptions', 'SessionPage', 'WebPage', '__version__']
__version__ = '4.0.2'
__version__ = '4.0.4'

View File

@ -20,31 +20,30 @@ from ..errors import ElementNotFoundError
class BaseParser(object):
"""所有页面、元素类的基类"""
def __call__(self, loc_or_str):
return self.ele(loc_or_str)
def __call__(self, locator):
return self.ele(locator)
def ele(self, loc_or_ele, index=1, timeout=None):
return self._ele(loc_or_ele, timeout, index=index, method='ele()')
def ele(self, locator, index=1, timeout=None):
return self._ele(locator, timeout, index=index, method='ele()')
def eles(self, loc_or_str, timeout=None):
return self._ele(loc_or_str, timeout, index=None)
def eles(self, locator, timeout=None):
return self._ele(locator, timeout, index=None)
# ----------------以下属性或方法待后代实现----------------
@property
def html(self):
return ''
def s_ele(self, loc_or_ele):
def s_ele(self, locator):
pass
def s_eles(self, loc_or_str):
def s_eles(self, locator):
pass
def _ele(self, loc_or_ele, timeout=None, index=1, raise_err=None, method=None):
def _ele(self, locator, timeout=None, index=1, raise_err=None, method=None):
pass
@abstractmethod
def _find_elements(self, loc_or_ele, timeout=None, index=1, raise_err=None):
def _find_elements(self, locator, timeout=None, index=1, relative=False, raise_err=None):
pass
@ -53,6 +52,7 @@ class BaseElement(BaseParser):
def __init__(self, page=None):
self.page = page
self._type = 'BaseElement'
# ----------------以下属性或方法由后代实现----------------
@property
@ -68,9 +68,9 @@ class BaseElement(BaseParser):
def nexts(self):
pass
def _ele(self, loc_or_str, timeout=None, index=1, relative=False, raise_err=None, method=None):
def _ele(self, locator, timeout=None, index=1, relative=False, raise_err=None, method=None):
"""调用获取元素的方法
:param loc_or_str: 定位符
:param locator: 定位符
:param timeout: 超时时间
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:param relative: 是否相对定位
@ -78,24 +78,19 @@ class BaseElement(BaseParser):
:param method: 调用的方法名
:return: 元素对象或它们组成的列表
"""
r = self._find_elements(loc_or_str, timeout=timeout, index=index, relative=relative, raise_err=raise_err)
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, {'loc_or_str': loc_or_str, 'index': index})
raise ElementNotFoundError(None, method, {'locator': locator, 'index': index})
r.method = method
r.args = {'loc_or_str': loc_or_str, 'index': index}
r.args = {'locator': locator, 'index': index}
return r
@abstractmethod
def _find_elements(self, loc_or_str, timeout=None, index=1, relative=False, raise_err=None):
pass
class DrissionElement(BaseElement):
"""ChromiumElement 和 SessionElement的基类
但不是ShadowRoot的基类"""
"""ChromiumElement 和 SessionElement的基类但不是ShadowRoot的基类"""
@property
def link(self):
@ -151,21 +146,21 @@ class DrissionElement(BaseElement):
return self._ele(loc, timeout=0, relative=True, raise_err=False, method='parent()')
def child(self, filter_loc='', index=1, timeout=None, ele_only=True):
def child(self, locator='', index=1, timeout=None, ele_only=True):
"""返回直接子元素元素或节点组成的列表,可用查询语法筛选
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 直接子元素或节点文本组成的列表
"""
if isinstance(filter_loc, int):
index = filter_loc
filter_loc = ''
if not filter_loc:
if isinstance(locator, int):
index = locator
locator = ''
if not locator:
loc = '*' if ele_only else 'node()'
else:
loc = get_loc(filter_loc, True) # 把定位符转换为xpath
loc = get_loc(locator, True) # 把定位符转换为xpath
if loc[0] == 'css selector':
raise ValueError('此css selector语法不受支持请换成xpath。')
loc = loc[1].lstrip('./')
@ -175,62 +170,62 @@ class DrissionElement(BaseElement):
return node
if Settings.raise_when_ele_not_found:
raise ElementNotFoundError(None, 'child()', {'filter_loc': filter_loc, 'index': index,
raise ElementNotFoundError(None, 'child()', {'locator': locator, 'index': index,
'ele_only': ele_only})
else:
return NoneElement(self.page, 'child()', {'filter_loc': filter_loc, 'index': index, 'ele_only': ele_only})
return NoneElement(self.page, 'child()', {'locator': locator, 'index': index, 'ele_only': ele_only})
def prev(self, filter_loc='', index=1, timeout=None, ele_only=True):
def prev(self, locator='', index=1, timeout=None, ele_only=True):
"""返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 前面第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 兄弟元素
"""
return self._get_relative('prev()', 'preceding', True, filter_loc, index, timeout, ele_only)
return self._get_relative('prev()', 'preceding', True, locator, index, timeout, ele_only)
def next(self, filter_loc='', index=1, timeout=None, ele_only=True):
def next(self, locator='', index=1, timeout=None, ele_only=True):
"""返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 后面第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 兄弟元素
"""
return self._get_relative('next()', 'following', True, filter_loc, index, timeout, ele_only)
return self._get_relative('next()', 'following', True, locator, index, timeout, ele_only)
def before(self, filter_loc='', index=1, timeout=None, ele_only=True):
def before(self, locator='', index=1, timeout=None, ele_only=True):
"""返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 前面第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素前面的某个元素或节点
"""
return self._get_relative('before()', 'preceding', False, filter_loc, index, timeout, ele_only)
return self._get_relative('before()', 'preceding', False, locator, index, timeout, ele_only)
def after(self, filter_loc='', index=1, timeout=None, ele_only=True):
def after(self, locator='', index=1, timeout=None, ele_only=True):
"""返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 后面第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素后面的某个元素或节点
"""
return self._get_relative('after()', 'following', False, filter_loc, index, timeout, ele_only)
return self._get_relative('after()', 'following', False, locator, index, timeout, ele_only)
def children(self, filter_loc='', timeout=None, ele_only=True):
def children(self, locator='', timeout=None, ele_only=True):
"""返回直接子元素元素或节点组成的列表,可用查询语法筛选
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 直接子元素或节点文本组成的列表
"""
if not filter_loc:
if not locator:
loc = '*' if ele_only else 'node()'
else:
loc = get_loc(filter_loc, True) # 把定位符转换为xpath
loc = get_loc(locator, True) # 把定位符转换为xpath
if loc[0] == 'css selector':
raise ValueError('此css selector语法不受支持请换成xpath。')
loc = loc[1].lstrip('./')
@ -239,69 +234,69 @@ class DrissionElement(BaseElement):
nodes = self._ele(loc, timeout=timeout, index=None, relative=True)
return [e for e in nodes if not (isinstance(e, str) and sub('[ \n\t\r]', '', e) == '')]
def prevs(self, filter_loc='', timeout=None, ele_only=True):
def prevs(self, locator='', timeout=None, ele_only=True):
"""返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 兄弟元素或节点文本组成的列表
"""
return self._get_relatives(filter_loc=filter_loc, direction='preceding', timeout=timeout, ele_only=ele_only)
return self._get_relatives(locator=locator, direction='preceding', timeout=timeout, ele_only=ele_only)
def nexts(self, filter_loc='', timeout=None, ele_only=True):
def nexts(self, locator='', timeout=None, ele_only=True):
"""返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 兄弟元素或节点文本组成的列表
"""
return self._get_relatives(filter_loc=filter_loc, direction='following', timeout=timeout, ele_only=ele_only)
return self._get_relatives(locator=locator, direction='following', timeout=timeout, ele_only=ele_only)
def befores(self, filter_loc='', timeout=None, ele_only=True):
def befores(self, locator='', timeout=None, ele_only=True):
"""返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素前面的元素或节点组成的列表
"""
return self._get_relatives(filter_loc=filter_loc, direction='preceding',
return self._get_relatives(locator=locator, direction='preceding',
brother=False, timeout=timeout, ele_only=ele_only)
def afters(self, filter_loc='', timeout=None, ele_only=True):
def afters(self, locator='', timeout=None, ele_only=True):
"""返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素后面的元素或节点组成的列表
"""
return self._get_relatives(filter_loc=filter_loc, direction='following',
return self._get_relatives(locator=locator, direction='following',
brother=False, timeout=timeout, ele_only=ele_only)
def _get_relative(self, func, direction, brother, filter_loc='', index=1, timeout=None, ele_only=True):
def _get_relative(self, func, direction, brother, locator='', index=1, timeout=None, ele_only=True):
"""获取一个亲戚元素或节点,可用查询语法筛选,可指定返回筛选结果的第几个
:param func: 方法名称
:param direction: 方向'following' 'preceding'
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 前面第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素前面的某个元素或节点
"""
if isinstance(filter_loc, int):
index = filter_loc
filter_loc = ''
node = self._get_relatives(index, filter_loc, direction, brother, timeout, ele_only)
if isinstance(locator, int):
index = locator
locator = ''
node = self._get_relatives(index, locator, direction, brother, timeout, ele_only)
if node:
return node
if Settings.raise_when_ele_not_found:
raise ElementNotFoundError(None, func, {'filter_loc': filter_loc, 'index': index, 'ele_only': ele_only})
raise ElementNotFoundError(None, func, {'locator': locator, 'index': index, 'ele_only': ele_only})
else:
return NoneElement(self.page, func, {'filter_loc': filter_loc, 'index': index, 'ele_only': ele_only})
return NoneElement(self.page, func, {'locator': locator, 'index': index, 'ele_only': ele_only})
def _get_relatives(self, index=None, filter_loc='', direction='following', brother=True, timeout=.5, ele_only=True):
def _get_relatives(self, index=None, locator='', direction='following', brother=True, timeout=.5, ele_only=True):
"""按要求返回兄弟元素或节点组成的列表
:param index: 获取第几个该参数不为None时只获取该编号的元素
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param direction: 'following' 'preceding'查找的方向
:param brother: 查找范围在同级查找还是整个dom前后查找
:param timeout: 查找等待时间
@ -309,11 +304,11 @@ class DrissionElement(BaseElement):
"""
brother = '-sibling' if brother else ''
if not filter_loc:
if not locator:
loc = '*' if ele_only else 'node()'
else:
loc = get_loc(filter_loc, True) # 把定位符转换为xpath
loc = get_loc(locator, True) # 把定位符转换为xpath
if loc[0] == 'css selector':
raise ValueError('此css selector语法不受支持请换成xpath。')
loc = loc[1].lstrip('./')
@ -341,12 +336,15 @@ class DrissionElement(BaseElement):
return
@abstractmethod
def attr(self, attr: str):
def attr(self, name: str):
return ''
def _get_ele_path(self, mode):
return ''
def _find_elements(self, locator, timeout=None, index=1, relative=False, raise_err=None):
pass
class BasePage(BaseParser):
"""页面类的基类"""
@ -362,6 +360,7 @@ class BasePage(BaseParser):
self._download_path = None
self._none_ele_return_value = False
self._none_ele_value = None
self._type = 'BasePage'
@property
def title(self):
@ -379,11 +378,6 @@ class BasePage(BaseParser):
"""设置查找元素时等待的秒数"""
self._timeout = second
@property
def cookies(self):
"""返回cookies"""
return self.get_cookies(True)
@property
def url_available(self):
"""返回当前访问的url有效性"""
@ -415,36 +409,32 @@ class BasePage(BaseParser):
return
@abstractmethod
def get_cookies(self, as_dict=False, all_info=False):
def cookies(self, as_dict=False, all_info=False):
return {}
@abstractmethod
def get(self, url, show_errmsg=False, retry=None, interval=None):
pass
def _ele(self, loc_or_ele, timeout=None, index=1, raise_err=None, method=None):
def _ele(self, locator, timeout=None, index=1, raise_err=None, method=None):
"""调用获取元素的方法
:param loc_or_ele: 定位符
:param locator: 定位符
:param timeout: 超时时间
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:param raise_err: 找不到时是否抛出异常
:param method: 调用的方法名
:return: 元素对象或它们组成的列表
"""
if not loc_or_ele:
raise ElementNotFoundError(None, method, {'loc_or_str': loc_or_ele})
if not locator:
raise ElementNotFoundError(None, method, {'locator': locator})
r = self._find_elements(loc_or_ele, timeout=timeout, index=index, raise_err=raise_err)
r = self._find_elements(locator, timeout=timeout, index=index, 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, {'loc_or_str': loc_or_ele, 'index': index})
raise ElementNotFoundError(None, method, {'locator': locator, 'index': index})
r.method = method
r.args = {'loc_or_str': loc_or_ele, 'index': index}
r.args = {'locator': locator, 'index': index}
return r
@abstractmethod
def _find_elements(self, loc_or_ele, timeout=None, index=1, raise_err=None):
pass

View File

@ -14,36 +14,37 @@ from .._elements.none_element import NoneElement
class BaseParser(object):
_type: str
def __call__(self, loc_or_str: Union[Tuple[str, str], str], index: int = 1): ...
def __call__(self, locator: Union[Tuple[str, str], str], index: int = 1): ...
def ele(self,
loc_or_ele: Union[Tuple[str, str], str, BaseElement],
locator: Union[Tuple[str, str], str, BaseElement],
index: int = 1,
timeout: float = None): ...
def eles(self, loc_or_str: Union[Tuple[str, str], str], timeout=None): ...
def eles(self, locator: Union[Tuple[str, str], str], timeout=None): ...
# ----------------以下属性或方法待后代实现----------------
@property
def html(self) -> str: ...
def s_ele(self, loc_or_ele: Union[Tuple[str, str], str, BaseElement], index: int = 1): ...
def s_ele(self, locator: Union[Tuple[str, str], str, BaseElement], index: int = 1): ...
def s_eles(self, loc_or_str: Union[Tuple[str, str], str]): ...
def s_eles(self, locator: Union[Tuple[str, str], str]): ...
def _ele(self,
loc_or_ele,
locator: Union[Tuple[str, str], str],
timeout: float = None,
index: Optional[int] = 1,
raise_err: bool = None,
method: str = None): ...
@abstractmethod
def _find_elements(self,
loc_or_ele,
locator: Union[Tuple[str, str], str],
timeout: float = None,
index: Optional[int] = 1,
relative: bool = False,
raise_err: bool = None): ...
@ -57,20 +58,13 @@ class BaseElement(BaseParser):
def tag(self) -> str: ...
def _ele(self,
loc_or_str: Union[Tuple[str, str], str],
locator: Union[Tuple[str, str], str],
timeout: float = None,
index: Optional[int] = 1,
relative: bool = False,
raise_err: bool = None,
method: str = None): ...
@abstractmethod
def _find_elements(self, loc_or_str,
timeout: float = None,
index: Optional[int] = 1,
relative: bool = False,
raise_err: bool = None): ...
def parent(self, level_or_loc: Union[tuple, str, int] = 1): ...
def prev(self, index: int = 1) -> None: ...
@ -107,57 +101,57 @@ class DrissionElement(BaseElement):
index: int = 1) -> Union[DrissionElement, None]: ...
def child(self,
filter_loc: Union[tuple, str, int] = '',
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]: ...
def prev(self,
filter_loc: Union[tuple, str, int] = '',
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]: ...
def next(self,
filter_loc: Union[tuple, str, int] = '',
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]: ...
def before(self,
filter_loc: Union[tuple, str, int] = '',
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]: ...
def after(self,
filter_loc: Union[tuple, str, int] = '',
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]: ...
def children(self,
filter_loc: Union[tuple, str] = '',
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> List[Union[DrissionElement, str]]: ...
def prevs(self,
filter_loc: Union[tuple, str] = '',
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> List[Union[DrissionElement, str]]: ...
def nexts(self,
filter_loc: Union[tuple, str] = '',
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> List[Union[DrissionElement, str]]: ...
def befores(self,
filter_loc: Union[tuple, str] = '',
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> List[Union[DrissionElement, str]]: ...
def afters(self,
filter_loc: Union[tuple, str] = '',
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> List[Union[DrissionElement, str]]: ...
@ -165,14 +159,14 @@ class DrissionElement(BaseElement):
func: str,
direction: str,
brother: bool,
filter_loc: Union[tuple, str] = '',
locator: Union[Tuple[str, str], str] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> DrissionElement: ...
def _get_relatives(self,
index: int = None,
filter_loc: Union[tuple, str] = '',
locator: Union[Tuple[str, str], str] = '',
direction: str = 'following',
brother: bool = True,
timeout: float = 0.5,
@ -189,7 +183,7 @@ class DrissionElement(BaseElement):
def raw_text(self) -> str: ...
@abstractmethod
def attr(self, attr: str) -> str: ...
def attr(self, name: str) -> str: ...
def _get_ele_path(self, mode) -> str: ...
@ -215,9 +209,6 @@ class BasePage(BaseParser):
@timeout.setter
def timeout(self, second: float) -> None: ...
@property
def cookies(self) -> dict: ...
@property
def url_available(self) -> bool: ...
@ -238,21 +229,14 @@ class BasePage(BaseParser):
def user_agent(self) -> str: ...
@abstractmethod
def get_cookies(self, as_dict: bool = False, all_info: bool = False) -> Union[list, dict]: ...
def cookies(self, as_dict: bool = False, all_info: bool = False) -> Union[list, dict]: ...
@abstractmethod
def get(self, url: str, show_errmsg: bool = False, retry: int = None, interval: float = None): ...
def _ele(self,
loc_or_ele,
locator,
timeout: float = None,
index: Optional[int] = 1,
raise_err: bool = None,
method: str = None): ...
@abstractmethod
def _find_elements(self,
loc_or_ele,
timeout: float = None,
index: Optional[int] = 1,
raise_err: bool = None): ...

View File

@ -49,35 +49,44 @@ class Browser(object):
self.id = browser_id
self._frames = {}
self._drivers = {}
# self._drivers = {t: Driver(t, 'page', address) for t in self.tabs}
self._all_drivers = {}
self._connected = False
self._process_id = None
r = self.run_cdp('SystemInfo.getProcessInfo')
for i in r.get('processInfo', []):
if i['type'] == 'browser':
self._process_id = i['id']
break
try:
r = self.run_cdp('SystemInfo.getProcessInfo')
for i in r.get('processInfo', []):
if i['type'] == 'browser':
self._process_id = i['id']
break
except:
pass
self.run_cdp('Target.setDiscoverTargets', discover=True)
self._driver.set_callback('Target.targetDestroyed', self._onTargetDestroyed)
self._driver.set_callback('Target.targetCreated', self._onTargetCreated)
def _get_driver(self, tab_id, owner=None):
"""获取对应tab id的Driver
"""新建并返回指定tab id的Driver
:param tab_id: 标签页id
:param owner: 使用该驱动的对象
:return: Driver对象
"""
return self._drivers.pop(tab_id, Driver(tab_id, 'page', self.address, owner))
d = self._drivers.pop(tab_id, Driver(tab_id, 'page', self.address))
d.owner = owner
self._all_drivers.setdefault(tab_id, set()).add(d)
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://')):
try:
self._drivers[kwargs['targetInfo']['targetId']] = Driver(kwargs['targetInfo']['targetId'],
'page', self.address)
tab_id = kwargs['targetInfo']['targetId']
d = Driver(tab_id, 'page', self.address)
self._drivers[tab_id] = d
self._all_drivers.setdefault(tab_id, set()).add(d)
except WebSocketBadStatusException:
pass
@ -88,7 +97,10 @@ class Browser(object):
self._dl_mgr.clear_tab_info(tab_id)
for key in [k for k, i in self._frames.items() if i == tab_id]:
self._frames.pop(key, None)
for d in self._all_drivers.get(tab_id, tuple()):
d.stop()
self._drivers.pop(tab_id, None)
self._all_drivers.pop(tab_id, None)
def connect_to_page(self):
"""执行与page相关的逻辑"""
@ -153,7 +165,16 @@ class Browser(object):
:param tab_id: 标签页id
:return: None
"""
self.run_cdp('Target.closeTarget', targetId=tab_id, _ignore=PageDisconnectedError)
self._onTargetDestroyed(targetId=tab_id)
self.driver.run('Target.closeTarget', targetId=tab_id)
def stop_driver(self, driver):
"""停止一个Driver
:param driver: Driver对象
:return: None
"""
driver.stop()
self._all_drivers.get(driver.id, set()).discard(driver)
def activate_tab(self, tab_id):
"""使标签页变为活动状态
@ -175,10 +196,15 @@ class Browser(object):
:param force: 是否立刻强制终止进程
:return: None
"""
for tab in self._all_drivers.values():
for driver in tab:
driver.stop()
try:
self.run_cdp('Browser.close')
except PageDisconnectedError:
self.driver.stop()
return
self.driver.stop()
if force:
ip, port = self.address.split(':')

View File

@ -5,7 +5,7 @@
@Copyright: (c) 2024 by g1879, Inc. All Rights Reserved.
@License : BSD 3-Clause.
"""
from typing import List, Optional, Union
from typing import List, Optional, Union, Set, Dict
from .driver import BrowserDriver, Driver
from .._pages.chromium_page import ChromiumPage
@ -19,7 +19,8 @@ class Browser(object):
id: str = ...
address: str = ...
_frames: dict = ...
_drivers: dict = ...
_drivers: Dict[str, Driver] = ...
_all_drivers: Dict[str, Set[Driver]] = ...
_process_id: Optional[int] = ...
_dl_mgr: DownloadManager = ...
_connected: bool = ...
@ -49,6 +50,8 @@ class Browser(object):
def close_tab(self, tab_id: str) -> None: ...
def stop_driver(self, driver: Driver) -> None: ...
def activate_tab(self, tab_id: str) -> None: ...
def get_window_bounds(self, tab_id: str = None) -> dict: ...

View File

@ -189,8 +189,9 @@ class Driver(object):
timeout = kwargs.pop('_timeout', 30)
result = self._send({'method': _method, 'params': kwargs}, timeout=timeout)
if 'result' not in result and 'error' in result:
kwargs['_timeout'] = timeout
return {'error': result['error']['message'], 'type': result.get('type', 'call_method_error'),
'method': _method, 'args': kwargs, 'timeout': timeout}
'method': _method, 'args': kwargs}
else:
return result['result']

View File

@ -7,12 +7,8 @@
"""
from pathlib import Path
from re import search
from shutil import rmtree
from tempfile import gettempdir, TemporaryDirectory
from threading import Lock
from .options_manage import OptionsManager
from .._functions.tools import port_is_using, clean_folder
class ChromiumOptions(object):
@ -30,7 +26,7 @@ class ChromiumOptions(object):
if read_file is not False:
ini_path = str(ini_path) if ini_path else None
om = OptionsManager(ini_path)
self.ini_path = om.ini_path
self.ini_path = str(Path(om.ini_path).absolute())
options = om.chromium_options
self._download_path = om.paths.get('download_path', None) or None
@ -60,14 +56,10 @@ class ChromiumOptions(object):
timeouts = om.timeouts
self._timeouts = {'base': timeouts['base'],
'pageLoad': timeouts['page_load'],
'page_load': timeouts['page_load'],
'script': timeouts['script']}
self._auto_port = options.get('auto_port', False)
if self._auto_port:
port, path = PortFinder().get_port()
self._address = f'127.0.0.1:{port}'
self.set_argument('--user-data-dir', path)
others = om.others
self._retry_times = others.get('retry_times', 3)
@ -83,7 +75,7 @@ class ChromiumOptions(object):
self._extensions = []
self._prefs = {}
self._flags = {}
self._timeouts = {'base': 10, 'pageLoad': 30, 'script': 30}
self._timeouts = {'base': 10, 'page_load': 30, 'script': 30}
self._address = '127.0.0.1:9222'
self._load_mode = 'normal'
self._proxy = None
@ -170,7 +162,7 @@ class ChromiumOptions(object):
@property
def is_auto_port(self):
"""返回是否使用自动端口和用户文件"""
"""返回是否使用自动端口和用户文件如指定范围则返回范围tuple"""
return self._auto_port
@property
@ -286,18 +278,18 @@ class ChromiumOptions(object):
self.clear_file_flags = True
return self
def set_timeouts(self, base=None, pageLoad=None, script=None, implicit=None):
def set_timeouts(self, base=None, page_load=None, script=None, implicit=None):
"""设置超时时间,单位为秒
:param base: 默认超时时间
:param pageLoad: 页面加载超时时间
:param page_load: 页面加载超时时间
:param script: 脚本运行超时时间
:return: 当前对象
"""
base = base if base is not None else implicit
if base is not None:
self._timeouts['base'] = base
if pageLoad is not None:
self._timeouts['pageLoad'] = pageLoad
if page_load is not None:
self._timeouts['page_load'] = page_load
if script is not None:
self._timeouts['script'] = script
@ -448,7 +440,6 @@ class ChromiumOptions(object):
:return: 当前对象
"""
self._browser_path = str(path)
self._auto_port = False
return self
def set_download_path(self, path):
@ -494,14 +485,15 @@ class ChromiumOptions(object):
self._system_user_path = on_off
return self
def auto_port(self, on_off=True, tmp_path=None):
def auto_port(self, on_off=True, tmp_path=None, scope=None):
"""自动获取可用端口
:param on_off: 是否开启自动获取端口号
:param tmp_path: 临时文件保存路径为None时保存到系统临时文件夹on_off为False时此参数无效
:param scope: 指定端口范围不含最后的数字为None则使用[9600-19600)
:return: 当前对象
"""
if on_off:
self._auto_port = True
self._auto_port = scope if scope else True
if tmp_path:
self._tmp_path = str(tmp_path)
else:
@ -553,7 +545,7 @@ class ChromiumOptions(object):
om.set_item('paths', 'tmp_path', self._tmp_path or '')
# 设置timeout
om.set_item('timeouts', 'base', self._timeouts['base'])
om.set_item('timeouts', 'page_load', self._timeouts['pageLoad'])
om.set_item('timeouts', 'page_load', self._timeouts['page_load'])
om.set_item('timeouts', 'script', self._timeouts['script'])
# 设置重试
om.set_item('others', 'retry_times', self.retry_times)
@ -619,41 +611,3 @@ class ChromiumOptions(object):
"""
on_off = None if on_off else False
return self.set_argument('--mute-audio', on_off)
class PortFinder(object):
used_port = {}
lock = Lock()
def __init__(self, path=None):
"""
:param path: 临时文件保存路径为None时使用系统临时文件夹
"""
tmp = Path(path) if path else Path(gettempdir()) / 'DrissionPage'
self.tmp_dir = tmp / 'UserTempFolder'
self.tmp_dir.mkdir(parents=True, exist_ok=True)
if not PortFinder.used_port:
clean_folder(self.tmp_dir)
def get_port(self):
"""查找一个可用端口
:return: 可以使用的端口和用户文件夹路径组成的元组
"""
with PortFinder.lock:
for i in range(9600, 19600):
if i in PortFinder.used_port:
continue
elif port_is_using('127.0.0.1', i):
PortFinder.used_port[i] = None
continue
path = TemporaryDirectory(dir=self.tmp_dir).name
PortFinder.used_port[i] = path
return i, path
for i in range(9600, 19600):
if port_is_using('127.0.0.1', i):
continue
rmtree(PortFinder.used_port[i], ignore_errors=True)
return i, TemporaryDirectory(dir=self.tmp_dir).name
raise OSError('未找到可用端口。')

View File

@ -6,8 +6,7 @@
@License : BSD 3-Clause.
"""
from pathlib import Path
from threading import Lock
from typing import Union, Tuple, Any, Literal, Optional
from typing import Union, Any, Literal, Optional, Tuple
class ChromiumOptions(object):
@ -82,7 +81,7 @@ class ChromiumOptions(object):
def is_existing_only(self) -> bool: ...
@property
def is_auto_port(self) -> bool: ...
def is_auto_port(self) -> Union[bool, Tuple[int, int]]: ...
@property
def retry_times(self) -> int: ...
@ -110,7 +109,9 @@ class ChromiumOptions(object):
def clear_flags_in_file(self) -> ChromiumOptions: ...
def set_timeouts(self, base: float = None, pageLoad: float = None,
def set_timeouts(self,
base: float = None,
page_load: float = None,
script: float = None) -> ChromiumOptions: ...
def set_user(self, user: str = 'Default') -> ChromiumOptions: ...
@ -153,21 +154,13 @@ class ChromiumOptions(object):
def use_system_user_path(self, on_off: bool = True) -> ChromiumOptions: ...
def auto_port(self, on_off: bool = True, tmp_path: Union[str, Path] = None) -> ChromiumOptions: ...
def auto_port(self,
on_off: bool = True,
tmp_path: Union[str, Path] = None,
scope: Tuple[int, int] = None) -> ChromiumOptions: ...
def existing_only(self, on_off: bool = True) -> ChromiumOptions: ...
def save(self, path: Union[str, Path] = None) -> str: ...
def save_to_default(self) -> str: ...
class PortFinder(object):
used_port: dict = ...
lock: Lock = ...
tmp_dir: Path = ...
def __init__(self, path: Union[str, Path] = None): ...
@staticmethod
def get_port() -> Tuple[int, str]: ...

View File

@ -5,7 +5,7 @@ tmp_path =
[chromium_options]
address = 127.0.0.1:9222
browser_path = chrome
arguments = ['--no-default-browser-check', '--disable-suggestions-ui', '--no-first-run', '--disable-infobars', '--disable-popup-blocking', '--hide-crash-restore-bubble']
arguments = ['--no-default-browser-check', '--disable-suggestions-ui', '--no-first-run', '--disable-infobars', '--disable-popup-blocking', '--hide-crash-restore-bubble', '--disable-features=PrivacySandboxSettings4']
extensions = []
prefs = {'profile.default_content_settings.popups': 0, 'profile.default_content_setting_values': {'notifications': 2}}
flags = {}

View File

@ -8,6 +8,7 @@
from configparser import RawConfigParser, NoSectionError, NoOptionError
from pathlib import Path
from pprint import pprint
from time import sleep
class OptionsManager(object):
@ -28,8 +29,9 @@ class OptionsManager(object):
self.ini_path = str(path)
if not Path(self.ini_path).exists():
input('\nini文件不存在。\n如果是打包使用,请查看打包注意事项\nhttps://g1879.gitee.io/drission'
'pagedocs/advance/packaging/')
print('\nini文件不存在。\n如果是打包使用,请查看打包注意事项\n'
'https://g1879.gitee.io/drissionpagedocs/advance/packaging/')
sleep(10)
self._conf = RawConfigParser()
self._conf.read(self.ini_path, encoding='utf-8')

View File

@ -12,11 +12,6 @@ from typing import Any
class OptionsManager(object):
ini_path: str = ...
_conf: RawConfigParser = ...
paths: dict = ...
chrome_options: dict = ...
session_options: dict = ...
timeouts: dict = ...
proxies: dict = ...
def __init__(self, path: str = None): ...

View File

@ -171,28 +171,27 @@ class SessionOptions(object):
self._headers = {key.lower(): headers[key] for key in headers}
return self
def set_a_header(self, attr, value):
def set_a_header(self, name, value):
"""设置headers中一个项
:param attr: 设置名称
:param name: 设置名称
:param value: 设置值
:return: 返回当前对象
"""
if self._headers is None:
self._headers = {}
self._headers[attr.lower()] = value
self._headers[name.lower()] = value
return self
def remove_a_header(self, attr):
def remove_a_header(self, name):
"""从headers中删除一个设置
:param attr: 要删除的设置
:param name: 要删除的设置
:return: 返回当前对象
"""
if self._headers is None:
return self
attr = attr.lower()
self._headers.pop(attr, None)
self._headers.pop(name.lower(), None)
return self

View File

@ -51,9 +51,9 @@ class SessionOptions(object):
def set_headers(self, headers: Union[dict, None]) -> SessionOptions: ...
def set_a_header(self, attr: str, value: str) -> SessionOptions: ...
def set_a_header(self, name: str, value: str) -> SessionOptions: ...
def remove_a_header(self, attr: str) -> SessionOptions: ...
def remove_a_header(self, name: str) -> SessionOptions: ...
@property
def cookies(self) -> list: ...

View File

@ -19,7 +19,7 @@ from .._base.base import DrissionElement, BaseElement
from .._functions.keys import input_text_or_keys
from .._functions.locator import get_loc
from .._functions.settings import Settings
from .._functions.web import make_absolute_link, get_ele_txt, format_html, is_js_func, offset_scroll
from .._functions.web import make_absolute_link, get_ele_txt, format_html, is_js_func, offset_scroll, get_blob
from .._units.clicker import Clicker
from .._units.rect import ElementRect
from .._units.scroller import ElementScroller
@ -53,6 +53,7 @@ class ChromiumElement(DrissionElement):
self._clicker = None
self._tag = None
self._wait = None
self._type = 'ChromiumElement'
if node_id and obj_id and backend_id:
self._node_id = node_id
@ -77,17 +78,16 @@ class ChromiumElement(DrissionElement):
self._doc_id = doc['objectId'] if doc else None
def __repr__(self):
attrs = self.attrs
attrs = [f"{attr}='{attrs[attr]}'" for attr in attrs]
attrs = [f"{k}='{v}'" for k, v in self.attrs.items()]
return f'<ChromiumElement {self.tag} {" ".join(attrs)}>'
def __call__(self, loc_or_str, index=1, timeout=None):
def __call__(self, locator, index=1, timeout=None):
"""在内部查找元素
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 超时时间
:return: ChromiumElement对象或属性文本
"""
return self.ele(loc_or_str, index=index, timeout=timeout)
return self.ele(locator, index=index, timeout=timeout)
def __eq__(self, other):
return self._backend_id == getattr(other, '_backend_id', None)
@ -127,10 +127,9 @@ class ChromiumElement(DrissionElement):
@property
def raw_text(self):
"""返回未格式化处理的元素内文本"""
return self.prop('innerText')
return self.property('innerText')
# -----------------d模式独有属性-------------------
@property
def set(self):
"""返回用于设置元素属性的对象"""
@ -205,6 +204,29 @@ class ChromiumElement(DrissionElement):
return self._select
@property
def value(self):
return self.property('value')
# -----即将废弃开始--------
@property
def location(self):
"""返回元素左上角的绝对坐标"""
return self.rect.location
@property
def size(self):
"""返回元素宽和高组成的元组"""
return self.rect.size
def prop(self, prop):
return self.property(prop)
def get_src(self, timeout=None, base64_to_bytes=True):
return self.src(timeout=timeout, base64_to_bytes=base64_to_bytes)
# -----即将废弃结束--------
def check(self, uncheck=False, by_js=False):
"""选中或取消选中当前元素
:param uncheck: 是否取消选中
@ -234,104 +256,104 @@ class ChromiumElement(DrissionElement):
"""
return super().parent(level_or_loc, index)
def child(self, filter_loc='', index=1, timeout=None, ele_only=True):
def child(self, locator='', index=1, timeout=None, ele_only=True):
"""返回当前元素的一个符合条件的直接子元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 直接子元素或节点文本
"""
return super().child(filter_loc, index, timeout, ele_only=ele_only)
return super().child(locator, index, timeout, ele_only=ele_only)
def prev(self, filter_loc='', index=1, timeout=None, ele_only=True):
def prev(self, locator='', index=1, timeout=None, ele_only=True):
"""返回当前元素前面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 前面第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 兄弟元素或节点文本
"""
return super().prev(filter_loc, index, timeout, ele_only=ele_only)
return super().prev(locator, index, timeout, ele_only=ele_only)
def next(self, filter_loc='', index=1, timeout=None, ele_only=True):
def next(self, locator='', index=1, timeout=None, ele_only=True):
"""返回当前元素后面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 兄弟元素或节点文本
"""
return super().next(filter_loc, index, timeout, ele_only=ele_only)
return super().next(locator, index, timeout, ele_only=ele_only)
def before(self, filter_loc='', index=1, timeout=None, ele_only=True):
def before(self, locator='', index=1, timeout=None, ele_only=True):
"""返回文档中当前元素前面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个
查找范围不限同级元素而是整个DOM文档
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 前面第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素前面的某个元素或节点
"""
return super().before(filter_loc, index, timeout, ele_only=ele_only)
return super().before(locator, index, timeout, ele_only=ele_only)
def after(self, filter_loc='', index=1, timeout=None, ele_only=True):
def after(self, locator='', index=1, timeout=None, ele_only=True):
"""返回文档中此当前元素后面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个
查找范围不限同级元素而是整个DOM文档
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素后面的某个元素或节点
"""
return super().after(filter_loc, index, timeout, ele_only=ele_only)
return super().after(locator, index, timeout, ele_only=ele_only)
def children(self, filter_loc='', timeout=None, ele_only=True):
def children(self, locator='', timeout=None, ele_only=True):
"""返回当前元素符合条件的直接子元素或节点组成的列表,可用查询语法筛选
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 直接子元素或节点文本组成的列表
"""
return super().children(filter_loc, timeout, ele_only=ele_only)
return super().children(locator, timeout, ele_only=ele_only)
def prevs(self, filter_loc='', timeout=None, ele_only=True):
def prevs(self, locator='', timeout=None, ele_only=True):
"""返回当前元素前面符合条件的同级元素或节点组成的列表,可用查询语法筛选
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 兄弟元素或节点文本组成的列表
"""
return super().prevs(filter_loc, timeout, ele_only=ele_only)
return super().prevs(locator, timeout, ele_only=ele_only)
def nexts(self, filter_loc='', timeout=None, ele_only=True):
def nexts(self, locator='', timeout=None, ele_only=True):
"""返回当前元素后面符合条件的同级元素或节点组成的列表,可用查询语法筛选
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 兄弟元素或节点文本组成的列表
"""
return super().nexts(filter_loc, timeout, ele_only=ele_only)
return super().nexts(locator, timeout, ele_only=ele_only)
def befores(self, filter_loc='', timeout=None, ele_only=True):
def befores(self, locator='', timeout=None, ele_only=True):
"""返回文档中当前元素前面符合条件的元素或节点组成的列表,可用查询语法筛选
查找范围不限同级元素而是整个DOM文档
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素前面的元素或节点组成的列表
"""
return super().befores(filter_loc, timeout, ele_only=ele_only)
return super().befores(locator, timeout, ele_only=ele_only)
def afters(self, filter_loc='', timeout=None, ele_only=True):
def afters(self, locator='', timeout=None, ele_only=True):
"""返回文档中当前元素后面符合条件的元素或节点组成的列表,可用查询语法筛选
查找范围不限同级元素而是整个DOM文档
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素后面的元素或节点组成的列表
"""
return super().afters(filter_loc, timeout, ele_only=ele_only)
return super().afters(locator, timeout, ele_only=ele_only)
def attr(self, attr):
"""返回一个attribute属性值
@ -344,10 +366,10 @@ class ChromiumElement(DrissionElement):
if not link or link.lower().startswith(('javascript:', 'mailto:')):
return link
else:
return make_absolute_link(link, self.prop('baseURI'))
return make_absolute_link(link, self.property('baseURI'))
elif attr == 'src':
return make_absolute_link(attrs.get('src', None), self.prop('baseURI'))
return make_absolute_link(attrs.get('src', None), self.property('baseURI'))
elif attr == 'text':
return self.text
@ -364,20 +386,20 @@ class ChromiumElement(DrissionElement):
else:
return attrs.get(attr, None)
def remove_attr(self, attr):
def remove_attr(self, name):
"""删除元素一个attribute属性
:param attr: 属性名
:param name: 属性名
:return: None
"""
self.run_js(f'this.removeAttribute("{attr}");')
self.run_js(f'this.removeAttribute("{name}");')
def prop(self, prop):
def property(self, name):
"""获取一个property属性值
:param prop: 属性名
:param name: 属性名
:return: 属性值文本
"""
try:
value = self.run_js(f'return this.{prop};')
value = self.run_js(f'return this.{name};')
return format_html(value) if isinstance(value, str) else value
except:
return None
@ -401,60 +423,60 @@ class ChromiumElement(DrissionElement):
"""
run_js(self, script, as_expr, 0, args)
def ele(self, loc_or_str, index=1, timeout=None):
def ele(self, locator, index=1, timeout=None):
"""返回当前元素下级符合条件的一个元素、属性或节点文本
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 获取第几个元素从1开始可传入负数获取倒数第几个
:param timeout: 查找元素超时时间默认与元素所在页面等待时间一致
:return: ChromiumElement对象或属性文本
"""
return self._ele(loc_or_str, timeout, index=index, method='ele()')
return self._ele(locator, timeout, index=index, method='ele()')
def eles(self, loc_or_str, timeout=None):
def eles(self, locator, timeout=None):
"""返回当前元素下级所有符合条件的子元素、属性或节点文本
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 查找元素超时时间默认与元素所在页面等待时间一致
:return: ChromiumElement对象或属性文本组成的列表
"""
return self._ele(loc_or_str, timeout=timeout, index=None)
return self._ele(locator, timeout=timeout, index=None)
def s_ele(self, loc_or_str=None, index=1):
def s_ele(self, locator=None, index=1):
"""查找一个符合条件的元素以SessionElement形式返回
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:return: SessionElement对象或属性文本
"""
if self.tag in __FRAME_ELEMENT__:
r = make_session_ele(self.inner_html, loc_or_str, index=index)
r = make_session_ele(self.inner_html, locator, index=index)
else:
r = make_session_ele(self, loc_or_str, index=index)
r = make_session_ele(self, locator, index=index)
if isinstance(r, NoneElement):
if Settings.raise_when_ele_not_found:
raise ElementNotFoundError(None, 's_ele()', {'loc_or_str': loc_or_str})
raise ElementNotFoundError(None, 's_ele()', {'locator': locator})
else:
r.method = 's_ele()'
r.args = {'loc_or_str': loc_or_str}
r.args = {'locator': locator}
return r
def s_eles(self, loc_or_str=None):
def s_eles(self, locator=None):
"""查找所有符合条件的元素以SessionElement列表形式返回
:param loc_or_str: 定位符
:param locator: 定位符
:return: SessionElement或属性文本组成的列表
"""
if self.tag in __FRAME_ELEMENT__:
return make_session_ele(self.inner_html, loc_or_str, index=None)
return make_session_ele(self, loc_or_str, index=None)
return make_session_ele(self.inner_html, locator, index=None)
return make_session_ele(self, locator, index=None)
def _find_elements(self, loc_or_str, timeout=None, index=1, relative=False, raise_err=None):
def _find_elements(self, locator, timeout=None, index=1, relative=False, raise_err=None):
"""返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 查找元素超时时间
:param index: 第几个结果从1开始可传入负数获取倒数第几个为None返回所有
:param relative: WebPage用的表示是否相对定位的参数
:param raise_err: 找不到元素是是否抛出异常为None时根据全局设置
:return: ChromiumElement对象或文本属性或其组成的列表
"""
return find_in_chromium_ele(self, loc_or_str, index, timeout, relative=relative)
return find_in_chromium_ele(self, locator, index, timeout, relative=relative)
def style(self, style, pseudo_ele=''):
"""返回元素样式属性值,可获取伪元素属性值
@ -466,7 +488,7 @@ class ChromiumElement(DrissionElement):
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 get_src(self, timeout=None, base64_to_bytes=True):
def src(self, timeout=None, base64_to_bytes=True):
"""返回元素src资源base64的可转为bytes返回其它返回str
:param timeout: 等待资源加载的超时时间
:param base64_to_bytes: 为True时如果是base64数据转换为bytes格式
@ -495,29 +517,12 @@ class ChromiumElement(DrissionElement):
end_time = perf_counter() + timeout
while perf_counter() < end_time:
if is_blob:
js = """
function fetchData(url) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = function() {
var reader = new FileReader();
reader.onloadend = function() {resolve(reader.result);}
reader.readAsDataURL(xhr.response);
};
xhr.open('GET', url, true);
xhr.send();
});
}
"""
try:
result = self.page.run_js(js, src)
result = get_blob(self.page, src, base64_to_bytes)
if result:
break
except:
continue
else:
src = self.prop('currentSrc')
src = self.property('currentSrc')
if not src:
continue
@ -533,19 +538,14 @@ class ChromiumElement(DrissionElement):
if not result:
return None
if is_blob:
if base64_to_bytes:
from base64 import b64decode
return b64decode(result.split(',', 1)[-1])
else:
return result
elif is_blob:
return result
elif result['base64Encoded'] and base64_to_bytes:
from base64 import b64decode
return b64decode(result['content'])
else:
if result['base64Encoded'] and base64_to_bytes:
from base64 import b64decode
return b64decode(result['content'])
else:
return result['content']
return result['content']
def save(self, path=None, name=None, timeout=None):
"""保存图片或其它有src属性的元素的资源
@ -554,7 +554,7 @@ class ChromiumElement(DrissionElement):
:param timeout: 等待资源加载的超时时间
:return: 返回保存路径
"""
data = self.get_src(timeout=timeout)
data = self.src(timeout=timeout)
if not data:
raise NoResourceError
@ -564,7 +564,7 @@ class ChromiumElement(DrissionElement):
if src.lower().startswith('data:image'):
r = search(r'data:image/(.*?);base64,', src)
name = f'img.{r.group(1)}' if r else None
name = name or basename(self.prop('currentSrc'))
name = name or basename(self.property('currentSrc'))
path = get_usable_path(f'{path}{sep}{name}').absolute()
write_type = 'wb' if isinstance(data, bytes) else 'w'
@ -616,7 +616,7 @@ class ChromiumElement(DrissionElement):
self.clear(True)
if isinstance(vals, (list, tuple)):
vals = ''.join([str(i) for i in vals])
self.set.prop('value', str(vals))
self.set.property('value', str(vals))
self.run_js('this.dispatchEvent(new Event("change", {bubbles: true}));')
return
@ -772,18 +772,6 @@ class ChromiumElement(DrissionElement):
files = [str(Path(i).absolute()) for i in files]
self.page.run_cdp('DOM.setFileInputFiles', files=files, backendNodeId=self._backend_id)
# -------------即将废弃-------------
@property
def location(self):
"""返回元素左上角的绝对坐标"""
return self.rect.location
@property
def size(self):
"""返回元素宽和高组成的元组"""
return self.rect.size
class ShadowRoot(BaseElement):
"""ShadowRoot是用于处理ShadowRoot的类使用方法和ChromiumElement基本一致"""
@ -805,19 +793,20 @@ class ShadowRoot(BaseElement):
self._node_id = self._get_node_id(obj_id)
self._backend_id = self._get_backend_id(self._node_id)
self._states = None
self._type = 'ShadowRoot'
def __repr__(self):
return f'<ShadowRoot in {self.parent_ele}>'
def __call__(self, loc_or_str, index=1, timeout=None):
def __call__(self, locator, index=1, timeout=None):
"""在内部查找元素
ele2 = ele1('@id=ele_id')
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:param timeout: 超时时间
:return: 元素对象或属性文本
"""
return self.ele(loc_or_str, index=index, timeout=timeout)
return self.ele(locator, index=index, timeout=timeout)
def __eq__(self, other):
return self._backend_id == getattr(other, '_backend_id', None)
@ -888,16 +877,16 @@ class ShadowRoot(BaseElement):
return self.parent_ele._ele(loc, timeout=0, relative=True, raise_err=False, method='parent()')
def child(self, filter_loc='', index=1):
def child(self, locator='', index=1):
"""返回直接子元素元素或节点组成的列表,可用查询语法筛选
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 第几个查询结果1开始
:return: 直接子元素或节点文本组成的列表
"""
if not filter_loc:
if not locator:
loc = '*'
else:
loc = get_loc(filter_loc, True) # 把定位符转换为xpath
loc = get_loc(locator, True) # 把定位符转换为xpath
if loc[0] == 'css selector':
raise ValueError('此css selector语法不受支持请换成xpath。')
loc = loc[1].lstrip('./')
@ -908,17 +897,17 @@ class ShadowRoot(BaseElement):
return ele
if Settings.raise_when_ele_not_found:
raise ElementNotFoundError(None, 'child()', {'filter_loc': filter_loc, 'index': index})
raise ElementNotFoundError(None, 'child()', {'locator': locator, 'index': index})
else:
return NoneElement(self.page, 'child()', {'filter_loc': filter_loc, 'index': index})
return NoneElement(self.page, 'child()', {'locator': locator, 'index': index})
def next(self, filter_loc='', index=1):
def next(self, locator='', index=1):
"""返回当前元素后面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 第几个查询结果1开始
:return: ChromiumElement对象
"""
loc = get_loc(filter_loc, True)
loc = get_loc(locator, True)
if loc[0] == 'css selector':
raise ValueError('此css selector语法不受支持请换成xpath。')
@ -929,18 +918,18 @@ class ShadowRoot(BaseElement):
return ele
if Settings.raise_when_ele_not_found:
raise ElementNotFoundError(None, 'next()', {'filter_loc': filter_loc, 'index': index})
raise ElementNotFoundError(None, 'next()', {'locator': locator, 'index': index})
else:
return NoneElement(self.page, 'next()', {'filter_loc': filter_loc, 'index': index})
return NoneElement(self.page, 'next()', {'locator': locator, 'index': index})
def before(self, filter_loc='', index=1):
def before(self, locator='', index=1):
"""返回文档中当前元素前面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个
查找范围不限同级元素而是整个DOM文档
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 前面第几个查询结果1开始
:return: 本元素前面的某个元素或节点
"""
loc = get_loc(filter_loc, True)
loc = get_loc(locator, True)
if loc[0] == 'css selector':
raise ValueError('此css selector语法不受支持请换成xpath。')
@ -951,34 +940,34 @@ class ShadowRoot(BaseElement):
return ele
if Settings.raise_when_ele_not_found:
raise ElementNotFoundError(None, 'before()', {'filter_loc': filter_loc, 'index': index})
raise ElementNotFoundError(None, 'before()', {'locator': locator, 'index': index})
else:
return NoneElement(self.page, 'before()', {'filter_loc': filter_loc, 'index': index})
return NoneElement(self.page, 'before()', {'locator': locator, 'index': index})
def after(self, filter_loc='', index=1):
def after(self, locator='', index=1):
"""返回文档中此当前元素后面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个
查找范围不限同级元素而是整个DOM文档
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 后面第几个查询结果1开始
:return: 本元素后面的某个元素或节点
"""
nodes = self.afters(filter_loc=filter_loc)
nodes = self.afters(locator=locator)
if nodes:
return nodes[index - 1]
if Settings.raise_when_ele_not_found:
raise ElementNotFoundError(None, 'after()', {'filter_loc': filter_loc, 'index': index})
raise ElementNotFoundError(None, 'after()', {'locator': locator, 'index': index})
else:
return NoneElement(self.page, 'after()', {'filter_loc': filter_loc, 'index': index})
return NoneElement(self.page, 'after()', {'locator': locator, 'index': index})
def children(self, filter_loc=''):
def children(self, locator=''):
"""返回当前元素符合条件的直接子元素或节点组成的列表,可用查询语法筛选
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:return: 直接子元素或节点文本组成的列表
"""
if not filter_loc:
if not locator:
loc = '*'
else:
loc = get_loc(filter_loc, True) # 把定位符转换为xpath
loc = get_loc(locator, True) # 把定位符转换为xpath
if loc[0] == 'css selector':
raise ValueError('此css selector语法不受支持请换成xpath。')
loc = loc[1].lstrip('./')
@ -986,12 +975,12 @@ class ShadowRoot(BaseElement):
loc = f'xpath:./{loc}'
return self._ele(loc, index=None, relative=True)
def nexts(self, filter_loc=''):
def nexts(self, locator=''):
"""返回当前元素后面符合条件的同级元素或节点组成的列表,可用查询语法筛选
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:return: ChromiumElement对象组成的列表
"""
loc = get_loc(filter_loc, True)
loc = get_loc(locator, True)
if loc[0] == 'css selector':
raise ValueError('此css selector语法不受支持请换成xpath。')
@ -999,13 +988,13 @@ class ShadowRoot(BaseElement):
xpath = f'xpath:./{loc}'
return self.parent_ele._ele(xpath, index=None, relative=True)
def befores(self, filter_loc=''):
def befores(self, locator=''):
"""返回文档中当前元素前面符合条件的元素或节点组成的列表,可用查询语法筛选
查找范围不限同级元素而是整个DOM文档
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:return: 本元素前面的元素或节点组成的列表
"""
loc = get_loc(filter_loc, True)
loc = get_loc(locator, True)
if loc[0] == 'css selector':
raise ValueError('此css selector语法不受支持请换成xpath。')
@ -1013,63 +1002,63 @@ class ShadowRoot(BaseElement):
xpath = f'xpath:./preceding::{loc}'
return self.parent_ele._ele(xpath, index=None, relative=True)
def afters(self, filter_loc=''):
def afters(self, locator=''):
"""返回文档中当前元素后面符合条件的元素或节点组成的列表,可用查询语法筛选
查找范围不限同级元素而是整个DOM文档
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:return: 本元素后面的元素或节点组成的列表
"""
eles1 = self.nexts(filter_loc)
loc = get_loc(filter_loc, True)[1].lstrip('./')
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)
def ele(self, loc_or_str, index=1, timeout=None):
def ele(self, locator, index=1, timeout=None):
"""返回当前元素下级符合条件的一个元素
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 获取第几个元素从1开始可传入负数获取倒数第几个
:param timeout: 查找元素超时时间默认与元素所在页面等待时间一致
:return: ChromiumElement对象
"""
return self._ele(loc_or_str, timeout, index=index, method='ele()')
return self._ele(locator, timeout, index=index, method='ele()')
def eles(self, loc_or_str, timeout=None):
def eles(self, locator, timeout=None):
"""返回当前元素下级所有符合条件的子元素
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 查找元素超时时间默认与元素所在页面等待时间一致
:return: ChromiumElement对象组成的列表
"""
return self._ele(loc_or_str, timeout=timeout, index=None)
return self._ele(locator, timeout=timeout, index=None)
def s_ele(self, loc_or_str=None, index=1):
def s_ele(self, locator=None, index=1):
"""查找一个符合条件的元素以SessionElement形式返回处理复杂页面时效率很高
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:return: SessionElement对象或属性文本
"""
r = make_session_ele(self, loc_or_str, index=index)
r = make_session_ele(self, locator, index=index)
if isinstance(r, NoneElement):
r.method = 's_ele()'
r.args = {'loc_or_str': loc_or_str}
r.args = {'locator': locator}
return r
def s_eles(self, loc_or_str):
def s_eles(self, locator):
"""查找所有符合条件的元素以SessionElement列表形式返回处理复杂页面时效率很高
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:return: SessionElement对象
"""
return make_session_ele(self, loc_or_str, index=None)
return make_session_ele(self, locator, index=None)
def _find_elements(self, loc_or_str, timeout=None, index=1, relative=False, raise_err=None):
def _find_elements(self, locator, timeout=None, index=1, relative=False, raise_err=None):
"""返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 查找元素超时时间
:param index: 第几个结果从1开始可传入负数获取倒数第几个为None返回所有
:param relative: WebPage用的表示是否相对定位的参数
:param raise_err: 找不到元素是是否抛出异常为None时根据全局设置
:return: ChromiumElement对象或其组成的列表
"""
loc = get_loc(loc_or_str, css_mode=False)
loc = get_loc(locator, css_mode=False)
if loc[0] == 'css selector' and str(loc[1]).startswith(':root'):
loc = loc[0], loc[1][5:]
@ -1134,20 +1123,20 @@ class ShadowRoot(BaseElement):
return r['backendNodeId']
def find_in_chromium_ele(ele, loc, index=1, timeout=None, relative=True):
def find_in_chromium_ele(ele, locator, index=1, timeout=None, relative=True):
"""在chromium元素中查找
:param ele: ChromiumElement对象
:param loc: 元素定位元组
:param locator: 元素定位元组
:param index: 第几个结果从1开始可传入负数获取倒数第几个为None返回所有
:param timeout: 查找元素超时时间
:param relative: WebPage用于标记是否相对定位使用
:return: 返回ChromiumElement元素或它们组成的列表
"""
# ---------------处理定位符---------------
if isinstance(loc, (str, tuple)):
loc = get_loc(loc)
if isinstance(locator, (str, tuple)):
loc = get_loc(locator)
else:
raise ValueError(f"定位符必须为str或长度为2的tuple对象。现在是{loc}")
raise ValueError(f"定位符必须为str或长度为2的tuple对象。现在是{locator}")
loc_str = loc[1]
if loc[0] == 'xpath' and loc[1].lstrip().startswith('/'):
@ -1441,7 +1430,7 @@ def run_js(page_or_ele, script, as_expr, timeout, args=None):
else:
raise ElementLostError('原来获取到的元素对象已不在页面内。')
if res is None and page.states.has_alert: # 存在alert的情况
if res is None and page.states.has_alert:
return None
exceptionDetails = res.get('exceptionDetails')

View File

@ -8,7 +8,6 @@
from pathlib import Path
from typing import Union, Tuple, List, Any, Literal, Optional
from .none_element import NoneElement
from .._base.base import DrissionElement, BaseElement
from .._elements.session_element import SessionElement
from .._pages.chromium_base import ChromiumBase
@ -48,9 +47,9 @@ class ChromiumElement(DrissionElement):
def __repr__(self) -> str: ...
def __call__(self,
loc_or_str: Union[Tuple[str, str], str],
locator: Union[Tuple[str, str], str],
index: int = 1,
timeout: float = None) -> Union[ChromiumElement, NoneElement]: ...
timeout: float = None) -> ChromiumElement: ...
def __eq__(self, other: ChromiumElement) -> bool: ...
@ -73,7 +72,6 @@ class ChromiumElement(DrissionElement):
def raw_text(self) -> str: ...
# -----------------d模式独有属性-------------------
@property
def set(self) -> ChromiumElementSetter: ...
@ -100,60 +98,60 @@ class ChromiumElement(DrissionElement):
def parent(self,
level_or_loc: Union[tuple, str, int] = 1,
index: int = 1) -> Union[ChromiumElement, NoneElement]: ...
index: int = 1) -> ChromiumElement: ...
def child(self,
filter_loc: Union[tuple, str, int] = '',
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[ChromiumElement, str, NoneElement]: ...
ele_only: bool = True) -> Union[ChromiumElement, str]: ...
def prev(self,
filter_loc: Union[tuple, str, int] = '',
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[ChromiumElement, str, NoneElement]: ...
ele_only: bool = True) -> Union[ChromiumElement, str]: ...
def next(self,
filter_loc: Union[tuple, str, int] = '',
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[ChromiumElement, str, NoneElement]: ...
ele_only: bool = True) -> Union[ChromiumElement, str]: ...
def before(self,
filter_loc: Union[tuple, str, int] = '',
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[ChromiumElement, str, NoneElement]: ...
ele_only: bool = True) -> Union[ChromiumElement, str]: ...
def after(self,
filter_loc: Union[tuple, str, int] = '',
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[ChromiumElement, str, NoneElement]: ...
ele_only: bool = True) -> Union[ChromiumElement, str]: ...
def children(self,
filter_loc: Union[tuple, str] = '',
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> List[Union[ChromiumElement, str]]: ...
def prevs(self,
filter_loc: Union[tuple, str] = '',
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> List[Union[ChromiumElement, str]]: ...
def nexts(self,
filter_loc: Union[tuple, str] = '',
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> List[Union[ChromiumElement, str]]: ...
def befores(self,
filter_loc: Union[tuple, str] = '',
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> List[Union[ChromiumElement, str]]: ...
def afters(self,
filter_loc: Union[tuple, str] = '',
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> List[Union[ChromiumElement, str]]: ...
@ -163,44 +161,47 @@ class ChromiumElement(DrissionElement):
@property
def select(self) -> SelectElement: ...
@property
def value(self) -> None: ...
def check(self, uncheck: bool = False, by_js: bool = False) -> None: ...
def attr(self, attr: str) -> Union[str, None]: ...
def attr(self, name: str) -> Union[str, None]: ...
def remove_attr(self, attr: str) -> None: ...
def remove_attr(self, name: str) -> None: ...
def prop(self, prop: str) -> Union[str, int, None]: ...
def property(self, name: str) -> Union[str, int, None]: ...
def run_js(self, script: str, *args, as_expr: bool = False, timeout: float = None) -> Any: ...
def run_async_js(self, script: str, *args, as_expr: bool = False) -> None: ...
def ele(self,
loc_or_str: Union[Tuple[str, str], str],
locator: Union[Tuple[str, str], str],
index: int = 1,
timeout: float = None) -> Union[ChromiumElement, NoneElement]: ...
timeout: float = None) -> ChromiumElement: ...
def eles(self,
loc_or_str: Union[Tuple[str, str], str],
locator: Union[Tuple[str, str], str],
timeout: float = None) -> List[ChromiumElement]: ...
def s_ele(self,
loc_or_str: Union[Tuple[str, str], str] = None,
index: int = 1) -> Union[SessionElement, NoneElement]: ...
locator: Union[Tuple[str, str], str] = None,
index: int = 1) -> SessionElement: ...
def s_eles(self, loc_or_str: Union[Tuple[str, str], str] = None) -> List[SessionElement]: ...
def s_eles(self, locator: Union[Tuple[str, str], str] = None) -> List[SessionElement]: ...
def _find_elements(self,
loc_or_str: Union[Tuple[str, str], str],
locator: Union[Tuple[str, str], str],
timeout: float = None,
index: Optional[int] = 1,
relative: bool = False,
raise_err: bool = False) -> Union[ChromiumElement, ChromiumFrame, NoneElement,
raise_err: bool = False) -> Union[ChromiumElement, ChromiumFrame,
List[Union[ChromiumElement, ChromiumFrame]]]: ...
def style(self, style: str, pseudo_ele: str = '') -> str: ...
def get_src(self, timeout: float = None, base64_to_bytes: bool = True) -> Union[bytes, str, None]: ...
def src(self, timeout: float = None, base64_to_bytes: bool = True) -> Union[bytes, str, None]: ...
def save(self, path: [str, bool] = None, name: str = None, timeout: float = None) -> str: ...
@ -248,7 +249,8 @@ class ShadowRoot(BaseElement):
def __repr__(self) -> str: ...
def __call__(self, loc_or_str: Union[Tuple[str, str], str],
def __call__(self,
locator: Union[Tuple[str, str], str],
timeout: float = None) -> ChromiumElement: ...
def __eq__(self, other: ShadowRoot) -> bool: ...
@ -271,47 +273,51 @@ class ShadowRoot(BaseElement):
def parent(self, level_or_loc: Union[str, int] = 1, index: int = 1) -> ChromiumElement: ...
def child(self, filter_loc: Union[tuple, str] = '',
index: int = 1) -> Union[ChromiumElement, NoneElement]: ...
def child(self,
locator: Union[Tuple[str, str], str] = '',
index: int = 1) -> ChromiumElement: ...
def next(self, filter_loc: Union[tuple, str] = '',
index: int = 1) -> Union[ChromiumElement, NoneElement]: ...
def next(self,
locator: Union[Tuple[str, str], str] = '',
index: int = 1) -> ChromiumElement: ...
def before(self, filter_loc: Union[tuple, str] = '',
index: int = 1) -> Union[ChromiumElement, NoneElement]: ...
def before(self,
locator: Union[Tuple[str, str], str] = '',
index: int = 1) -> ChromiumElement: ...
def after(self, filter_loc: Union[tuple, str] = '',
index: int = 1) -> Union[ChromiumElement, NoneElement]: ...
def after(self,
locator: Union[Tuple[str, str], str] = '',
index: int = 1) -> ChromiumElement: ...
def children(self, filter_loc: Union[tuple, str] = '') -> List[ChromiumElement]: ...
def children(self, locator: Union[Tuple[str, str], str] = '') -> List[ChromiumElement]: ...
def nexts(self, filter_loc: Union[tuple, str] = '') -> List[ChromiumElement]: ...
def nexts(self, locator: Union[Tuple[str, str], str] = '') -> List[ChromiumElement]: ...
def befores(self, filter_loc: Union[tuple, str] = '') -> List[ChromiumElement]: ...
def befores(self, locator: Union[Tuple[str, str], str] = '') -> List[ChromiumElement]: ...
def afters(self, filter_loc: Union[tuple, str] = '') -> List[ChromiumElement]: ...
def afters(self, locator: Union[Tuple[str, str], str] = '') -> List[ChromiumElement]: ...
def ele(self,
loc_or_str: Union[Tuple[str, str], str],
locator: Union[Tuple[str, str], str],
index: int = 1,
timeout: float = None) -> Union[ChromiumElement, NoneElement]: ...
timeout: float = None) -> ChromiumElement: ...
def eles(self,
loc_or_str: Union[Tuple[str, str], str],
locator: Union[Tuple[str, str], str],
timeout: float = None) -> List[ChromiumElement]: ...
def s_ele(self,
loc_or_str: Union[Tuple[str, str], str] = None,
index: int = 1) -> Union[SessionElement, NoneElement]: ...
locator: Union[Tuple[str, str], str] = None,
index: int = 1) -> SessionElement: ...
def s_eles(self, loc_or_str: Union[Tuple[str, str], str]) -> List[SessionElement]: ...
def s_eles(self, locator: Union[Tuple[str, str], str]) -> List[SessionElement]: ...
def _find_elements(self,
loc_or_str: Union[Tuple[str, str], str],
locator: Union[Tuple[str, str], str],
timeout: float = None,
index: Optional[int] = 1,
relative: bool = False,
raise_err: bool = None) -> Union[ChromiumElement, ChromiumFrame, NoneElement, str,
raise_err: bool = None) -> Union[ChromiumElement, ChromiumFrame, str,
List[Union[ChromiumElement, ChromiumFrame, str]]]: ...
def _get_node_id(self, obj_id: str) -> int: ...
@ -325,28 +331,27 @@ def find_in_chromium_ele(ele: ChromiumElement,
loc: Union[str, Tuple[str, str]],
index: Optional[int] = 1,
timeout: float = None,
relative: bool = True) -> Union[ChromiumElement, NoneElement, List[ChromiumElement]]: ...
relative: bool = True) -> Union[ChromiumElement, List[ChromiumElement]]: ...
def find_by_xpath(ele: ChromiumElement,
xpath: str,
index: Optional[int],
timeout: float,
relative: bool = True) -> Union[ChromiumElement, List[ChromiumElement], NoneElement]: ...
relative: bool = True) -> Union[ChromiumElement, List[ChromiumElement]]: ...
def find_by_css(ele: ChromiumElement,
selector: str,
index: Optional[int],
timeout: float) -> Union[ChromiumElement, List[ChromiumElement], NoneElement]: ...
timeout: float) -> Union[ChromiumElement, List[ChromiumElement],]: ...
def make_chromium_eles(page: Union[ChromiumBase, ChromiumPage, WebPage, ChromiumTab, ChromiumFrame],
_ids: Union[tuple, list, str, int],
index: Optional[int] = 1,
is_obj_id: bool = True
) -> Union[ChromiumElement, ChromiumFrame, NoneElement,
List[Union[ChromiumElement, ChromiumFrame]]]: ...
) -> Union[ChromiumElement, ChromiumFrame, List[Union[ChromiumElement, ChromiumFrame]]]: ...
def make_js_for_find_ele_by_xpath(xpath: str, type_txt: str, node_txt: str) -> str: ...

View File

@ -18,6 +18,7 @@ class NoneElement(object):
self._none_ele_return_value = False
self.method = method
self.args = args
self._get = None
def __call__(self, *args, **kwargs):
if not self._none_ele_return_value:
@ -33,7 +34,7 @@ class NoneElement(object):
return self
else:
if item in ('size', 'link', 'css_path', 'xpath', 'comments', 'texts', 'tag', 'html', 'inner_html',
'attrs', 'text', 'raw_text'):
'attrs', 'text', 'raw_text', 'value', 'attr', 'style', 'src', 'property'):
return self._none_ele_value
else:
raise ElementNotFoundError(None, self.method, self.args)

View File

@ -27,23 +27,24 @@ class SessionElement(DrissionElement):
"""
super().__init__(page)
self._inner_ele = ele
self._type = 'SessionElement'
@property
def inner_ele(self):
return self._inner_ele
def __repr__(self):
attrs = [f"{attr}='{self.attrs[attr]}'" for attr in self.attrs]
attrs = [f"{k}='{v}'" for k, v in self.attrs.items()]
return f'<SessionElement {self.tag} {" ".join(attrs)}>'
def __call__(self, loc_or_str, timeout=None):
def __call__(self, locator, timeout=None):
"""在内部查找元素
ele2 = ele1('@id=ele_id')
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 不起实际作用用于和DriverElement对应便于无差别调用
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 不起实际作用
:return: SessionElement对象或属性文本
"""
return self.ele(loc_or_str)
return self.ele(locator)
def __eq__(self, other):
return self.xpath == getattr(other, 'xpath', None)
@ -80,119 +81,120 @@ class SessionElement(DrissionElement):
"""返回未格式化处理的元素内文本"""
return str(self._inner_ele.text_content())
def parent(self, level_or_loc=1):
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)
return super().parent(level_or_loc, index)
def child(self, filter_loc='', index=1, timeout=None, ele_only=True):
def child(self, locator='', index=1, timeout=None, ele_only=True):
"""返回当前元素的一个符合条件的直接子元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 第几个查询结果1开始
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 直接子元素或节点文本
"""
return super().child(index, filter_loc, timeout, ele_only=ele_only)
return super().child(locator, index, timeout, ele_only=ele_only)
def prev(self, filter_loc='', index=1, timeout=None, ele_only=True):
def prev(self, locator='', index=1, timeout=None, ele_only=True):
"""返回当前元素前面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 前面第几个查询结果1开始
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 同级元素
"""
return super().prev(index, filter_loc, timeout, ele_only=ele_only)
return super().prev(locator, index, timeout, ele_only=ele_only)
def next(self, filter_loc='', index=1, timeout=None, ele_only=True):
def next(self, locator='', index=1, timeout=None, ele_only=True):
"""返回当前元素后面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 第几个查询结果1开始
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 同级元素
"""
return super().next(index, filter_loc, timeout, ele_only=ele_only)
return super().next(locator, index, timeout, ele_only=ele_only)
def before(self, filter_loc='', index=1, timeout=None, ele_only=True):
def before(self, locator='', index=1, timeout=None, ele_only=True):
"""返回文档中当前元素前面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个
查找范围不限同级元素而是整个DOM文档
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 前面第几个查询结果1开始
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素前面的某个元素或节点
"""
return super().before(index, filter_loc, timeout, ele_only=ele_only)
return super().before(locator, index, timeout, ele_only=ele_only)
def after(self, filter_loc='', index=1, timeout=None, ele_only=True):
def after(self, locator='', index=1, timeout=None, ele_only=True):
"""返回文档中此当前元素后面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个
查找范围不限同级元素而是整个DOM文档
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 第几个查询结果1开始
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素后面的某个元素或节点
"""
return super().after(index, filter_loc, timeout, ele_only=ele_only)
return super().after(locator, index, timeout, ele_only=ele_only)
def children(self, filter_loc='', timeout=0, ele_only=True):
def children(self, locator='', timeout=0, ele_only=True):
"""返回当前元素符合条件的直接子元素或节点组成的列表,可用查询语法筛选
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 直接子元素或节点文本组成的列表
"""
return super().children(filter_loc, timeout, ele_only=ele_only)
return super().children(locator, timeout, ele_only=ele_only)
def prevs(self, filter_loc='', timeout=None, ele_only=True):
def prevs(self, locator='', timeout=None, ele_only=True):
"""返回当前元素前面符合条件的同级元素或节点组成的列表,可用查询语法筛选
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 同级元素或节点文本组成的列表
"""
return super().prevs(filter_loc, timeout, ele_only=ele_only)
return super().prevs(locator, timeout, ele_only=ele_only)
def nexts(self, filter_loc='', timeout=None, ele_only=True):
def nexts(self, locator='', timeout=None, ele_only=True):
"""返回当前元素后面符合条件的同级元素或节点组成的列表,可用查询语法筛选
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 同级元素或节点文本组成的列表
"""
return super().nexts(filter_loc, timeout, ele_only=ele_only)
return super().nexts(locator, timeout, ele_only=ele_only)
def befores(self, filter_loc='', timeout=None, ele_only=True):
def befores(self, locator='', timeout=None, ele_only=True):
"""返回文档中当前元素前面符合条件的元素或节点组成的列表,可用查询语法筛选
查找范围不限同级元素而是整个DOM文档
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素前面的元素或节点组成的列表
"""
return super().befores(filter_loc, timeout, ele_only=ele_only)
return super().befores(locator, timeout, ele_only=ele_only)
def afters(self, filter_loc='', timeout=None, ele_only=True):
def afters(self, locator='', timeout=None, ele_only=True):
"""返回文档中当前元素后面符合条件的元素或节点组成的列表,可用查询语法筛选
查找范围不限同级元素而是整个DOM文档
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param timeout: 此参数不起实际作用
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素后面的元素或节点组成的列表
"""
return super().afters(filter_loc, timeout, ele_only=ele_only)
return super().afters(locator, timeout, ele_only=ele_only)
def attr(self, attr):
def attr(self, name):
"""返回attribute属性值
:param attr: 属性名
:param name: 属性名
:return: 属性值文本没有该属性返回None
"""
# 获取href属性时返回绝对url
if attr == 'href':
if name == 'href':
link = self.inner_ele.get('href')
# 若为链接为None、js或邮件直接返回
if not link or link.lower().startswith(('javascript:', 'mailto:')):
@ -201,66 +203,66 @@ class SessionElement(DrissionElement):
else: # 其它情况直接返回绝对url
return make_absolute_link(link, self.page.url)
elif attr == 'src':
elif name == 'src':
return make_absolute_link(self.inner_ele.get('src'), self.page.url)
elif attr == 'text':
elif name == 'text':
return self.text
elif attr == 'innerText':
elif name == 'innerText':
return self.raw_text
elif attr in ('html', 'outerHTML'):
elif name in ('html', 'outerHTML'):
return self.html
elif attr == 'innerHTML':
elif name == 'innerHTML':
return self.inner_html
else:
return self.inner_ele.get(attr)
return self.inner_ele.get(name)
def ele(self, loc_or_str, index=1, timeout=None):
def ele(self, locator, index=1, timeout=None):
"""返回当前元素下级符合条件的一个元素、属性或节点文本
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 第几个元素从1开始可传入负数获取倒数第几个
:param timeout: 不起实际作用
:return: SessionElement对象或属性文本
"""
return self._ele(loc_or_str, index=index, method='ele()')
return self._ele(locator, index=index, method='ele()')
def eles(self, loc_or_str, timeout=None):
def eles(self, locator, timeout=None):
"""返回当前元素下级所有符合条件的子元素、属性或节点文本
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 不起实际作用用于和DriverElement对应便于无差别调用
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 不起实际作用
:return: SessionElement对象或属性文本组成的列表
"""
return self._ele(loc_or_str, index=None)
return self._ele(locator, index=None)
def s_ele(self, loc_or_str=None, index=1):
def s_ele(self, locator=None, index=1):
"""返回当前元素下级符合条件的一个元素、属性或节点文本
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:return: SessionElement对象或属性文本
"""
return self._ele(loc_or_str, index=index, method='s_ele()')
return self._ele(locator, index=index, method='s_ele()')
def s_eles(self, loc_or_str):
def s_eles(self, locator):
"""返回当前元素下级所有符合条件的子元素、属性或节点文本
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:return: SessionElement对象或属性文本组成的列表
"""
return self._ele(loc_or_str, index=None)
return self._ele(locator, index=None)
def _find_elements(self, loc_or_str, timeout=None, index=1, relative=False, raise_err=None):
def _find_elements(self, locator, timeout=None, index=1, relative=False, raise_err=None):
"""返回当前元素下级符合条件的子元素、属性或节点文本
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 不起实际作用用于和父类对应
:param index: 第几个结果从1开始可传入负数获取倒数第几个为None返回所有
:param relative: WebPage用的表示是否相对定位的参数
:param raise_err: 找不到元素是是否抛出异常为None时根据全局设置
:return: SessionElement对象
"""
return make_session_ele(self, loc_or_str, index=index)
return make_session_ele(self, locator, index=index)
def _get_ele_path(self, mode):
"""获取css路径或xpath路径
@ -304,9 +306,13 @@ def make_session_ele(html_or_ele, loc=None, index=1):
raise ValueError("定位符必须为str或长度为2的tuple。")
# ---------------根据传入对象类型获取页面对象和lxml元素对象---------------
the_type = str(type(html_or_ele))
# 直接传入html文本
if isinstance(html_or_ele, str):
page = None
html_or_ele = fromstring(html_or_ele)
# SessionElement
if the_type.endswith(".SessionElement'>"):
elif html_or_ele._type == 'SessionElement':
page = html_or_ele.page
loc_str = loc[1]
@ -327,8 +333,7 @@ def make_session_ele(html_or_ele, loc=None, index=1):
loc = loc[0], loc_str
# ChromiumElement, DriverElement
elif the_type.endswith((".ChromiumElement'>", ".DriverElement'>")):
elif html_or_ele._type == 'ChromiumElement':
loc_str = loc[1]
if loc[0] == 'xpath' and loc[1].lstrip().startswith('/'):
loc_str = f'.{loc[1]}'
@ -353,11 +358,6 @@ def make_session_ele(html_or_ele, loc=None, index=1):
html = sub(r'^<\?xml.*?>', '', html)
html_or_ele = fromstring(html)
# 直接传入html文本
elif isinstance(html_or_ele, str):
page = None
html_or_ele = fromstring(html_or_ele)
# ShadowRoot
elif isinstance(html_or_ele, BaseElement):
page = html_or_ele.page

View File

@ -9,7 +9,6 @@ from typing import Union, List, Tuple, Optional
from lxml.html import HtmlElement
from .none_element import NoneElement
from .._base.base import DrissionElement, BaseElement
from .._elements.chromium_element import ChromiumElement
from .._pages.chromium_base import ChromiumBase
@ -29,9 +28,9 @@ class SessionElement(DrissionElement):
def __repr__(self) -> str: ...
def __call__(self,
loc_or_str: Union[Tuple[str, str], str],
locator: Union[Tuple[str, str], str],
index: int = 1,
timeout: float = None) -> Union[SessionElement, NoneElement]: ...
timeout: float = None) -> SessionElement: ...
def __eq__(self, other: SessionElement) -> bool: ...
@ -55,86 +54,86 @@ class SessionElement(DrissionElement):
def parent(self,
level_or_loc: Union[tuple, str, int] = 1,
index: int = 1) -> Union[SessionElement, NoneElement]: ...
index: int = 1) -> SessionElement: ...
def child(self,
filter_loc: Union[tuple, str, int] = '',
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[SessionElement, str, NoneElement]: ...
ele_only: bool = True) -> Union[SessionElement, str]: ...
def prev(self,
filter_loc: Union[tuple, str, int] = '',
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[SessionElement, str, NoneElement]: ...
ele_only: bool = True) -> Union[SessionElement, str]: ...
def next(self,
filter_loc: Union[tuple, str, int] = '',
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[SessionElement, str, NoneElement]: ...
ele_only: bool = True) -> Union[SessionElement, str]: ...
def before(self,
filter_loc: Union[tuple, str, int] = '',
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[SessionElement, str, NoneElement]: ...
ele_only: bool = True) -> Union[SessionElement, str]: ...
def after(self,
filter_loc: Union[tuple, str, int] = '',
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[SessionElement, str, NoneElement]: ...
ele_only: bool = True) -> Union[SessionElement, str]: ...
def children(self,
filter_loc: Union[tuple, str] = '',
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> List[Union[SessionElement, str]]: ...
def prevs(self,
filter_loc: Union[tuple, str] = '',
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> List[Union[SessionElement, str]]: ...
def nexts(self,
filter_loc: Union[tuple, str] = '',
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> List[Union[SessionElement, str]]: ...
def befores(self,
filter_loc: Union[tuple, str] = '',
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> List[Union[SessionElement, str]]: ...
def afters(self,
filter_loc: Union[tuple, str] = '',
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> List[Union[SessionElement, str]]: ...
def attr(self, attr: str) -> Optional[str]: ...
def attr(self, name: str) -> Optional[str]: ...
def ele(self,
loc_or_str: Union[Tuple[str, str], str],
locator: Union[Tuple[str, str], str],
index: int = 1,
timeout: float = None) -> Union[SessionElement, NoneElement]: ...
timeout: float = None) -> SessionElement: ...
def eles(self,
loc_or_str: Union[Tuple[str, str], str],
locator: Union[Tuple[str, str], str],
timeout: float = None) -> List[SessionElement]: ...
def s_ele(self,
loc_or_str: Union[Tuple[str, str], str] = None,
index: int = 1) -> Union[SessionElement, NoneElement]: ...
locator: Union[Tuple[str, str], str] = None,
index: int = 1) -> SessionElement: ...
def s_eles(self, loc_or_str: Union[Tuple[str, str], str]) -> List[SessionElement]: ...
def s_eles(self, locator: Union[Tuple[str, str], str]) -> List[SessionElement]: ...
def _find_elements(self,
loc_or_str: Union[Tuple[str, str], str],
locator: Union[Tuple[str, str], str],
timeout: float = None,
index: Optional[int] = 1,
relative: bool = False,
raise_err: bool = None) -> Union[SessionElement, NoneElement, List[SessionElement]]: ...
raise_err: bool = None) -> Union[SessionElement, List[SessionElement]]: ...
def _get_ele_path(self, mode: str) -> str: ...
@ -142,4 +141,4 @@ class SessionElement(DrissionElement):
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) -> Union[SessionElement, NoneElement, List[SessionElement]]: ...
index: Optional[int] = 1) -> Union[SessionElement, List[SessionElement]]: ...

View File

@ -6,10 +6,9 @@
@License : BSD 3-Clause.
"""
from json import load, dump, JSONDecodeError
from os import popen
from os import environ
from pathlib import Path
from platform import system
from re import search
from subprocess import Popen, DEVNULL
from tempfile import gettempdir
from time import perf_counter, sleep
@ -27,7 +26,7 @@ def connect_browser(option):
:return: 返回是否接管的浏览器
"""
address = option.address.replace('localhost', '127.0.0.1').lstrip('http://').lstrip('https://')
chrome_path = option.browser_path
browser_path = option.browser_path
ip, port = address.split(':')
if ip != '127.0.0.1' or port_is_using(ip, port) or option.is_existing_only:
@ -44,16 +43,16 @@ def connect_browser(option):
set_prefs(option)
set_flags(option)
try:
_run_browser(port, chrome_path, args)
_run_browser(port, browser_path, args)
# 传入的路径找不到主动在ini文件、注册表、系统变量中找
except FileNotFoundError:
chrome_path = get_chrome_path()
browser_path = get_chrome_path(option.ini_path)
if not chrome_path:
if not browser_path:
raise FileNotFoundError('无法找到浏览器可执行文件路径,请手动配置。')
_run_browser(port, chrome_path, args)
_run_browser(port, browser_path, args)
test_connect(ip, port)
return False
@ -220,7 +219,7 @@ def test_connect(ip, port, timeout=30):
def _run_browser(port, path: str, args) -> Popen:
"""创建chrome进程
"""创建浏览器进程
:param port: 端口号
:param path: 浏览器路径
:param args: 启动参数
@ -283,12 +282,13 @@ def _remove_arg_from_dict(target_dict: dict, arg: str) -> None:
pass
def get_chrome_path():
def get_chrome_path(ini_path):
"""从ini文件或系统变量中获取chrome可执行文件的路径"""
# -----------从ini文件中获取--------------
path = OptionsManager().chromium_options.get('browser_path', None)
if path and Path(path).is_file():
return str(path)
if ini_path:
path = OptionsManager(ini_path).chromium_options.get('browser_path', None)
if path and Path(path).is_file():
return str(path)
# -----------使用which获取-----------
from shutil import which
@ -330,23 +330,8 @@ def get_chrome_path():
pass
# -----------从系统变量中获取--------------
try:
paths = popen('set path').read().lower()
except:
return None
r = search(r'[^;]*chrome[^;]*', paths)
if r:
path = Path(r.group(0)) if 'chrome.exe' in r.group(0) else Path(r.group(0)) / 'chrome.exe'
if path.exists():
return str(path)
paths = paths.split(';')
for path in paths:
for path in environ.get('PATH', '').split(';'):
path = Path(path) / 'chrome.exe'
try:
if path.exists():
return str(path)

View File

@ -25,4 +25,4 @@ def set_flags(opt: ChromiumOptions) -> None: ...
def test_connect(ip: str, port: Union[int, str], timeout: float = 30) -> None: ...
def get_chrome_path() -> Union[str, None]: ...
def get_chrome_path(ini_path: str) -> Union[str, None]: ...

View File

@ -8,7 +8,9 @@
from pathlib import Path
from platform import system
from shutil import rmtree
from time import perf_counter, sleep
from tempfile import gettempdir, TemporaryDirectory
from threading import Lock
from time import perf_counter
from psutil import process_iter, AccessDenied, NoSuchProcess, ZombieProcess
@ -17,6 +19,47 @@ from ..errors import (ContextLostError, ElementLostError, CDPError, PageDisconne
AlertExistsError, WrongURLError, StorageError, CookieFormatError, JavaScriptError)
class PortFinder(object):
used_port = {}
lock = Lock()
def __init__(self, path=None):
"""
:param path: 临时文件保存路径为None时使用系统临时文件夹
"""
tmp = Path(path) if path else Path(gettempdir()) / 'DrissionPage'
self.tmp_dir = tmp / 'UserTempFolder'
self.tmp_dir.mkdir(parents=True, exist_ok=True)
if not PortFinder.used_port:
clean_folder(self.tmp_dir)
def get_port(self, scope=None):
"""查找一个可用端口
:param scope: 指定端口范围不含最后的数字为None则使用[9600-19600)
:return: 可以使用的端口和用户文件夹路径组成的元组
"""
with PortFinder.lock:
if scope in (True, None):
scope = (9600, 19600)
for i in range(scope[0], scope[1]):
if i in PortFinder.used_port:
continue
elif port_is_using('127.0.0.1', i):
PortFinder.used_port[i] = None
continue
path = TemporaryDirectory(dir=self.tmp_dir).name
PortFinder.used_port[i] = path
return i, path
for i in range(scope[0], scope[1]):
if port_is_using('127.0.0.1', i):
continue
rmtree(PortFinder.used_port[i], ignore_errors=True)
return i, TemporaryDirectory(dir=self.tmp_dir).name
raise OSError('未找到可用端口。')
def port_is_using(ip, port):
"""检查端口是否被占用
:param ip: 浏览器地址
@ -69,7 +112,7 @@ def show_or_hide_browser(page, hide=True):
pid = page.process_id
if not pid:
return None
hds = get_chrome_hwnds_from_pid(pid, page.title)
hds = get_hwnds_from_pid(pid, page.title)
sw = SW_HIDE if hide else SW_SHOW
for hd in hds:
ShowWindow(hd, sw)
@ -98,7 +141,7 @@ def get_browser_progress_id(progress, address):
return txt.split(' ')[-1]
def get_chrome_hwnds_from_pid(pid, title):
def get_hwnds_from_pid(pid, title):
"""通过PID查询句柄ID
:param pid: 进程id
:param title: 窗口标题
@ -122,40 +165,21 @@ def get_chrome_hwnds_from_pid(pid, title):
return hwnds
def wait_until(page, condition, timeout=10, poll=0.1, raise_err=True):
"""等待返回值不为False或空直到超时
:param page: DrissionPage对象
:param condition: 等待条件返回值不为False则停止等待
def wait_until(function, kwargs=None, timeout=10):
"""等待传入的方法返回值不为假
:param function: 要执行的方法
:param kwargs: 方法参数
:param timeout: 超时时间
:param poll: 轮询间隔
:param raise_err: 是否抛出异常
:return: DP Element or bool
:return: 执行结果超时抛出TimeoutError
"""
if kwargs is None:
kwargs = {}
end_time = perf_counter() + timeout
if isinstance(condition, str) or isinstance(condition, tuple):
if not callable(getattr(page, 's_ele', None)):
raise AttributeError('page对象缺少s_ele方法')
condition_method = lambda page: page.s_ele(condition)
elif callable(condition):
condition_method = condition
else:
raise ValueError('condition必须是函数或者字符串或者元组')
while perf_counter() < end_time:
try:
value = condition_method(page)
if value:
return value
except Exception:
pass
sleep(poll)
if perf_counter() > end_time:
break
if raise_err:
raise TimeoutError(f'等待超时(等待{timeout}秒)。')
else:
return False
value = function(**kwargs)
if value:
return value
raise TimeoutError
def stop_process_on_port(port):
@ -215,10 +239,12 @@ def raise_error(result, ignore=None):
r = CookieFormatError(f'cookie格式不正确{result["args"]}')
elif error == 'Given expression does not evaluate to a function':
r = JavaScriptError(f'传入的js无法解析成函数\n{result["args"]["functionDeclaration"]}')
elif error.endswith("' wasn't found"):
r = RuntimeError(f'你的浏览器可能太旧。\n方法:{result["method"]}\n参数:{result["args"]}')
elif result['type'] in ('call_method_error', 'timeout'):
from DrissionPage import __version__
from time import process_time
txt = f'\n错误:{result["error"]}\nmethod{result["method"]}\nargs{result["args"]}\n' \
txt = f'\n错误:{result["error"]}\n方法{result["method"]}\n参数{result["args"]}\n' \
f'版本:{__version__}\n运行时间:{process_time()}\n出现这个错误可能意味着程序有bug请把错误信息和重现方法' \
'告知作者,谢谢。\n报告网站https://gitee.com/g1879/DrissionPage/issues'
r = TimeoutError(txt) if result['type'] == 'timeout' else CDPError(txt)

View File

@ -7,12 +7,23 @@
"""
from os import popen
from pathlib import Path
from typing import Union
from types import FunctionType
from threading import Lock
from typing import Union, Tuple
from .._pages.chromium_page import ChromiumPage
class PortFinder(object):
used_port: dict = ...
lock: Lock = ...
tmp_dir: Path = ...
def __init__(self, path: Union[str, Path] = None): ...
@staticmethod
def get_port(scope: Tuple[int, int] = None) -> Tuple[int, str]: ...
def port_is_using(ip: str, port: Union[str, int]) -> bool: ...
@ -25,10 +36,10 @@ def show_or_hide_browser(page: ChromiumPage, hide: bool = True) -> None: ...
def get_browser_progress_id(progress: Union[popen, None], address: str) -> Union[str, None]: ...
def get_chrome_hwnds_from_pid(pid: Union[str, int], title: str) -> list: ...
def get_hwnds_from_pid(pid: Union[str, int], title: str) -> list: ...
def wait_until(page, condition: Union[FunctionType, str, tuple], timeout: float, poll: float, raise_err: bool): ...
def wait_until(function: callable, kwargs: dict = None, timeout: float = 10): ...
def stop_process_on_port(port: Union[int, str]) -> None: ...

View File

@ -161,8 +161,8 @@ def is_js_func(func):
func = func.strip()
if (func.startswith('function') or func.startswith('async ')) and func.endswith('}'):
return True
elif '=>' in func:
return True
# elif '=>' in func:
# return True
return False
@ -328,3 +328,38 @@ def is_cookie_in_driver(page, cookie):
if cookie['name'] == c['name'] and cookie['value'] == c['value']:
return True
return False
def get_blob(page, url, as_bytes=True):
"""获取知道blob资源
:param page: 资源所在页面对象
:param url: 资源url
:param as_bytes: 是否以字节形式返回
:return: 资源内容
"""
if not url.startswith('blob'):
raise TypeError('该链接非blob类型。')
js = """
function fetchData(url) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = function() {
var reader = new FileReader();
reader.onloadend = function(){resolve(reader.result);}
reader.readAsDataURL(xhr.response);
};
xhr.open('GET', url, true);
xhr.send();
});
}
"""
try:
result = page.run_js(js, url)
except:
raise RuntimeError('无法获取该资源。')
if as_bytes:
from base64 import b64decode
return b64decode(result.split(',', 1)[-1])
else:
return result

View File

@ -47,3 +47,6 @@ def set_browser_cookies(page: ChromiumBase, cookies: Union[RequestsCookieJar, li
def is_cookie_in_driver(page: ChromiumBase, cookie: dict) -> bool: ...
def get_blob(page: ChromiumBase, url: str, as_bytes: bool = True) -> bytes: ...

View File

@ -16,7 +16,7 @@ from urllib.parse import quote
from DataRecorder.tools import make_valid_name
from .._base.base import BasePage
from .._elements.chromium_element import ChromiumElement, run_js, make_chromium_eles
from .._elements.chromium_element import run_js, make_chromium_eles
from .._elements.none_element import NoneElement
from .._elements.session_element import make_session_ele
from .._functions.locator import get_loc, is_loc
@ -62,6 +62,7 @@ class ChromiumBase(BasePage):
self._download_path = None
self._load_end_time = 0
self._init_jss = []
self._type = 'ChromiumBase'
if not hasattr(self, '_listener'):
self._listener = None
@ -244,15 +245,15 @@ class ChromiumBase(BasePage):
self.run_cdp('Page.setInterceptFileChooserDialog', enabled=False)
self._upload_list = None
def __call__(self, loc_or_str, index=1, timeout=None):
def __call__(self, locator, index=1, timeout=None):
"""在内部查找元素
ele = page('@id=ele_id')
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 获取第几个元素从1开始可传入负数获取倒数第几个
:param timeout: 超时时间
:return: ChromiumElement对象
"""
return self.ele(loc_or_str, index, timeout)
return self.ele(locator, index, timeout)
def _wait_to_stop(self):
"""eager策略超时时使页面停止加载"""
@ -473,8 +474,8 @@ class ChromiumBase(BasePage):
show_errmsg=show_errmsg, timeout=timeout)
return self._url_available
def get_cookies(self, as_dict=False, all_domains=False, all_info=False):
"""获取cookies信息
def cookies(self, as_dict=False, all_domains=False, all_info=False):
"""返回cookies信息
:param as_dict: 为True时返回由{name: value}键值对组成的dict为True时返回list且all_info无效
:param all_domains: 是否返回所有域的cookies
:param all_info: 是否返回所有信息为False时只返回namevaluedomain
@ -491,60 +492,60 @@ class ChromiumBase(BasePage):
return [{'name': cookie['name'], 'value': cookie['value'], 'domain': cookie['domain']}
for cookie in cookies]
def ele(self, loc_or_ele, index=1, timeout=None):
def ele(self, locator, index=1, timeout=None):
"""获取一个符合条件的元素对象
:param loc_or_ele: 定位符或元素对象
:param locator: 定位符或元素对象
:param index: 获取第几个元素从1开始可传入负数获取倒数第几个
:param timeout: 查找超时时间
:return: ChromiumElement对象
"""
return self._ele(loc_or_ele, timeout=timeout, index=index, method='ele()')
return self._ele(locator, timeout=timeout, index=index, method='ele()')
def eles(self, loc_or_str, timeout=None):
def eles(self, locator, timeout=None):
"""获取所有符合条件的元素对象
:param loc_or_str: 定位符或元素对象
:param locator: 定位符或元素对象
:param timeout: 查找超时时间
:return: ChromiumElement对象组成的列表
"""
return self._ele(loc_or_str, timeout=timeout, index=None)
return self._ele(locator, timeout=timeout, index=None)
def s_ele(self, loc_or_ele=None, index=1):
def s_ele(self, locator=None, index=1):
"""查找一个符合条件的元素以SessionElement形式返回处理复杂页面时效率很高
:param loc_or_ele: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:return: SessionElement对象或属性文本
"""
r = make_session_ele(self, loc_or_ele, index=index)
r = make_session_ele(self, locator, index=index)
if isinstance(r, NoneElement):
if Settings.raise_when_ele_not_found:
raise ElementNotFoundError(None, 's_ele()', {'loc_or_ele': loc_or_ele})
raise ElementNotFoundError(None, 's_ele()', {'locator': locator})
else:
r.method = 's_ele()'
r.args = {'loc_or_ele': loc_or_ele}
r.args = {'locator': locator}
return r
def s_eles(self, loc_or_str):
def s_eles(self, locator):
"""查找所有符合条件的元素以SessionElement列表形式返回
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:return: SessionElement对象组成的列表
"""
return make_session_ele(self, loc_or_str, index=None)
return make_session_ele(self, locator, index=None)
def _find_elements(self, loc_or_ele, timeout=None, index=1, relative=False, raise_err=None):
def _find_elements(self, locator, timeout=None, index=1, relative=False, raise_err=None):
"""执行元素查找
:param loc_or_ele: 定位符或元素对象
:param locator: 定位符或元素对象
:param timeout: 查找超时时间
:param index: 第几个结果从1开始可传入负数获取倒数第几个为None返回所有
:param relative: WebPage用的表示是否相对定位的参数
:param raise_err: 找不到元素是是否抛出异常为None时根据全局设置
:return: ChromiumElement对象或元素对象组成的列表
"""
if isinstance(loc_or_ele, (str, tuple)):
loc = get_loc(loc_or_ele)[1]
elif isinstance(loc_or_ele, ChromiumElement) or str(type(loc_or_ele)).endswith(".ChromiumFrame'>"):
return loc_or_ele
if isinstance(locator, (str, tuple)):
loc = get_loc(locator)[1]
elif locator._type in ('ChromiumElement', 'ChromiumFrame'):
return locator
else:
raise ValueError('loc_or_str参数只能是tuple、str、ChromiumElement类型。')
raise ValueError('locator参数只能是tuple、str、ChromiumElement类型。')
self.wait.doc_loaded()
timeout = timeout if timeout is not None else self.timeout
@ -650,11 +651,13 @@ class ChromiumBase(BasePage):
"""页面停止加载"""
try:
self.run_cdp('Page.stopLoading')
end_time = perf_counter() + 5
while self._ready_state != 'complete' and perf_counter() < end_time:
sleep(.1)
except (PageDisconnectedError, CDPError):
pass
end_time = perf_counter() + self.timeouts.page_load
while self._ready_state != 'complete' and perf_counter() < end_time:
sleep(.1)
finally:
self._ready_state = 'complete'
def remove_ele(self, loc_or_ele):
"""从页面上删除一个元素
@ -680,13 +683,13 @@ class ChromiumBase(BasePage):
else:
xpath = loc_ind_ele
ele = self._ele(xpath, timeout=timeout)
if ele and not str(type(ele)).endswith(".ChromiumFrame'>"):
if ele and ele._type != 'ChromiumFrame':
raise TypeError('该定位符不是指向frame元素。')
r = ele
elif isinstance(loc_ind_ele, tuple):
ele = self._ele(loc_ind_ele, timeout=timeout)
if ele and not str(type(ele)).endswith(".ChromiumFrame'>"):
if ele and ele._type != 'ChromiumFrame':
raise TypeError('该定位符不是指向frame元素。')
r = ele
@ -698,7 +701,7 @@ class ChromiumBase(BasePage):
xpath = f'xpath:(//*[name()="frame" or name()="iframe"])[{loc_ind_ele}]'
r = self._ele(xpath, timeout=timeout)
elif str(type(loc_ind_ele)).endswith(".ChromiumFrame'>"):
elif loc_ind_ele._type == 'ChromiumFrame':
r = loc_ind_ele
else:
@ -709,18 +712,18 @@ class ChromiumBase(BasePage):
r.args = {'loc_ind_ele': loc_ind_ele}
return r
def get_frames(self, loc=None, timeout=None):
def get_frames(self, locator=None, timeout=None):
"""获取所有符合条件的frame对象
:param loc: 定位符为None时返回所有
:param locator: 定位符为None时返回所有
:param timeout: 查找超时时间
:return: ChromiumFrame对象组成的列表
"""
loc = loc or 'xpath://*[name()="iframe" or name()="frame"]'
frames = self._ele(loc, timeout=timeout, index=None, raise_err=False)
return [i for i in frames if str(type(i)).endswith(".ChromiumFrame'>")]
locator = locator or 'xpath://*[name()="iframe" or name()="frame"]'
frames = self._ele(locator, timeout=timeout, index=None, raise_err=False)
return [i for i in frames if i._type == 'ChromiumFrame']
def get_session_storage(self, item=None):
"""获取sessionStorage信息不设置item则获取全部
def session_storage(self, item=None):
"""返回sessionStorage信息不设置item则获取全部
:param item: 要获取的项不设置则返回全部
:return: sessionStorage一个或所有项内容
"""
@ -740,8 +743,8 @@ class ChromiumBase(BasePage):
'''
return {i['key']: i['val'] for i in self.run_js_loaded(js)}
def get_local_storage(self, item=None):
"""获取localStorage信息不设置item则获取全部
def local_storage(self, item=None):
"""返回localStorage信息不设置item则获取全部
:param item: 要获取的项目不设置则返回全部
:return: localStorage一个或所有项内容
"""
@ -826,7 +829,17 @@ class ChromiumBase(BasePage):
def disconnect(self):
"""断开与页面的连接,不关闭页面"""
if self._driver:
self.driver.stop()
self.browser.stop_driver(self._driver)
def reconnect(self, wait=0):
"""断开与页面原来的页面,重新建立连接
:param wait: 断开后等待若干秒再连接
:return: None
"""
t_id = self._target_id
self.disconnect()
sleep(wait)
self._driver = self.browser._get_driver(t_id, self)
def handle_alert(self, accept=True, send=None, timeout=None, next_one=False):
r = self._handle_alert(accept=accept, send=send, timeout=timeout, next_one=next_one)
@ -917,7 +930,11 @@ class ChromiumBase(BasePage):
:param interval: 重试间隔
:return: 重试次数和间隔组成的tuple
"""
self._url = quote(url, safe='-_.~!*\'"();:@&=+$,/\\?#[]%') or 'chrome://newtab/'
p = Path(url)
if p.exists():
self._url = str(p.absolute())
else:
self._url = quote(url, safe='-_.~!*\'"();:@&=+$,/\\?#[]%') or 'chrome://newtab/'
retry = retry if retry is not None else self.retry_times
interval = interval if interval is not None else self.retry_interval
return retry, interval
@ -1020,11 +1037,12 @@ class ChromiumBase(BasePage):
pic_type = path.suffix.lower()
pic_type = 'jpeg' if pic_type == '.jpg' else pic_type[1:]
width, height = self.rect.size
if full_page:
width, height = self.rect.size
if width == 0 or height == 0:
raise RuntimeError('页面大小为0请尝试等待页面加载完成。')
vp = {'x': 0, 'y': 0, 'width': width, 'height': height, 'scale': 1}
png = self.run_cdp_loaded('Page.captureScreenshot', format=pic_type,
captureBeyondViewport=True, clip=vp)['data']
args = {'format': pic_type, 'captureBeyondViewport': True, 'clip': vp}
else:
if left_top and right_bottom:
x, y = left_top
@ -1038,11 +1056,14 @@ class ChromiumBase(BasePage):
x += 10
vp = {'x': x, 'y': y, 'width': w, 'height': h, 'scale': 1}
png = self.run_cdp_loaded('Page.captureScreenshot', format=pic_type,
captureBeyondViewport=v, clip=vp)['data']
args = {'format': pic_type, 'captureBeyondViewport': v, 'clip': vp}
else:
png = self.run_cdp_loaded('Page.captureScreenshot', format=pic_type)['data']
args = {'format': pic_type}
if pic_type == 'jpeg':
args['quality'] = 100
png = self.run_cdp_loaded('Page.captureScreenshot', **args)['data']
if as_base64:
return png
@ -1082,6 +1103,15 @@ class ChromiumBase(BasePage):
"""返回页面总宽高,格式:(宽, 高)"""
return self.rect.size
def get_session_storage(self, item=None):
return self.session_storage(item)
def get_local_storage(self, item=None):
return self.local_storage(item)
def get_cookies(self, as_dict=False, all_domains=False, all_info=False):
return self.cookies(as_dict=as_dict, all_domains=all_domains, all_info=all_info)
class Timeout(object):
"""用于保存d模式timeout信息的类"""

View File

@ -13,7 +13,6 @@ from .._base.base import BasePage
from .._base.browser import Browser
from .._base.driver import Driver
from .._elements.chromium_element import ChromiumElement
from .._elements.none_element import NoneElement
from .._elements.session_element import SessionElement
from .._pages.chromium_frame import ChromiumFrame
from .._pages.chromium_page import ChromiumPage
@ -62,6 +61,7 @@ class ChromiumBase(BasePage):
self._init_jss: list = ...
self._ready_state: Optional[str] = ...
self._rect: TabRect = ...
self._type: str = ...
def _connect_browser(self, tab_id: str = None) -> None: ...
@ -94,9 +94,9 @@ class ChromiumBase(BasePage):
def _d_set_runtime_settings(self) -> None: ...
def __call__(self,
loc_or_str: Union[Tuple[str, str], str, ChromiumElement],
locator: Union[Tuple[str, str], str, ChromiumElement],
index: int = 1,
timeout: float = None) -> Union[ChromiumElement, NoneElement]: ...
timeout: float = None) -> ChromiumElement: ...
@property
def _js_ready_state(self) -> str: ...
@ -176,31 +176,30 @@ class ChromiumBase(BasePage):
def get(self, url: str, show_errmsg: bool = False, retry: int = None,
interval: float = None, timeout: float = None) -> Union[None, bool]: ...
def get_cookies(self, as_dict: bool = False, all_domains: bool = False,
all_info: bool = False) -> Union[list, dict]: ...
def cookies(self, as_dict: bool = False, all_domains: bool = False, all_info: bool = False) -> Union[list, dict]: ...
def ele(self,
loc_or_ele: Union[Tuple[str, str], str, ChromiumElement, ChromiumFrame],
locator: Union[Tuple[str, str], str, ChromiumElement, ChromiumFrame],
index: int = 1,
timeout: float = None) -> Union[ChromiumElement, NoneElement]: ...
timeout: float = None) -> ChromiumElement: ...
def eles(self,
loc_or_str: Union[Tuple[str, str], str],
locator: Union[Tuple[str, str], str],
timeout: float = None) -> List[ChromiumElement]: ...
def s_ele(self,
loc_or_ele: Union[Tuple[str, str], str] = None,
index:int = 1) -> Union[SessionElement, NoneElement]: ...
locator: Union[Tuple[str, str], str] = None,
index: int = 1) -> SessionElement: ...
def s_eles(self, loc_or_str: Union[Tuple[str, str], str]) -> List[SessionElement]: ...
def s_eles(self, locator: Union[Tuple[str, str], str]) -> List[SessionElement]: ...
def _find_elements(self,
loc_or_ele: Union[Tuple[str, str], str, ChromiumElement, ChromiumFrame],
locator: Union[Tuple[str, str], str, ChromiumElement, ChromiumFrame],
timeout: float = None,
index: Optional[int] = 1,
relative: bool = False,
raise_err: bool = None) \
-> Union[ChromiumElement, ChromiumFrame, NoneElement, List[Union[ChromiumElement, ChromiumFrame]]]: ...
-> Union[ChromiumElement, ChromiumFrame, List[Union[ChromiumElement, ChromiumFrame]]]: ...
def refresh(self, ignore_cache: bool = False) -> None: ...
@ -216,15 +215,15 @@ class ChromiumBase(BasePage):
def get_frame(self, loc_ind_ele: Union[str, int, tuple, ChromiumFrame], timeout: float = None) -> ChromiumFrame: ...
def get_frames(self, loc: Union[str, tuple] = None, timeout: float = None) -> List[ChromiumFrame]: ...
def get_frames(self, locator: Union[str, tuple] = None, timeout: float = None) -> List[ChromiumFrame]: ...
def run_cdp(self, cmd: str, **cmd_args) -> dict: ...
def run_cdp_loaded(self, cmd: str, **cmd_args) -> dict: ...
def get_session_storage(self, item: str = None) -> Union[str, dict, None]: ...
def session_storage(self, item: str = None) -> Union[str, dict, None]: ...
def get_local_storage(self, item: str = None) -> Union[str, dict, None]: ...
def local_storage(self, item: str = None) -> Union[str, dict, None]: ...
def add_init_js(self, script: str) -> str: ...
@ -243,6 +242,8 @@ class ChromiumBase(BasePage):
def disconnect(self) -> None: ...
def reconnect(self, wait: float = 0) -> None: ...
def handle_alert(self, accept: bool = True, send: str = None, timeout: float = None,
next_one: bool = False) -> Union[str, False]: ...

View File

@ -27,15 +27,14 @@ class ChromiumFrame(ChromiumBase):
:param ele: frame所在元素
:param info: frame所在元素信息
"""
page_type = str(type(page))
if 'ChromiumPage' in page_type or 'WebPage' in page_type:
if page._type in ('ChromiumPage', 'WebPage'):
self._page = self._target_page = self.tab = page
self._browser = page.browser
else: # Tab、Frame
self._page = page.page
self._browser = self._page.browser
self._target_page = page
self.tab = page.tab if 'ChromiumFrame' in page_type else page
self.tab = page.tab if page._type == 'ChromiumFrame' else page
self.address = page.address
self._tab_id = page.tab_id
@ -58,28 +57,28 @@ class ChromiumFrame(ChromiumBase):
self.doc_ele = ChromiumElement(self, obj_id=obj_id)
self._rect = None
self._type = 'ChromiumFrame'
end_time = perf_counter() + 2
while perf_counter() < end_time:
if self.url not in (None, 'about:blank'):
break
sleep(.1)
def __call__(self, loc_or_str, index=1, timeout=None):
def __call__(self, locator, index=1, timeout=None):
"""在内部查找元素
ele2 = ele1('@id=ele_id')
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:param timeout: 超时时间
:return: ChromiumElement对象或属性文本
"""
return self.ele(loc_or_str, index=index, timeout=timeout)
return self.ele(locator, index=index, timeout=timeout)
def __eq__(self, other):
return self._frame_id == getattr(other, '_frame_id', None)
def __repr__(self):
attrs = self._frame_ele.attrs
attrs = [f"{attr}='{attrs[attr]}'" for attr in attrs]
attrs = [f"{k}='{v}'" for k, v in self._frame_ele.attrs.items()]
return f'<ChromiumFrame {self.frame_ele.tag} {" ".join(attrs)}>'
def _d_set_runtime_settings(self):
@ -146,19 +145,6 @@ class ChromiumFrame(ChromiumBase):
if timeout <= 0:
timeout = .5
self._wait_loaded(timeout)
# while perf_counter() < end_time:
# try:
# obj_id = super().run_js('document;', as_expr=True)['objectId']
# self.doc_ele = ChromiumElement(self, obj_id=obj_id)
# break
# except Exception as e:
# sleep(.1)
# if self._debug:
# print(f'获取doc失败重试 {e}')
# else:
# raise GetDocumentError
# self.driver._debug = d_debug
self._is_loading = False
self._reloading = False
@ -208,7 +194,6 @@ class ChromiumFrame(ChromiumBase):
self._reload()
# ----------挂件----------
@property
def scroll(self):
"""返回用于滚动的对象"""
@ -252,7 +237,7 @@ class ChromiumFrame(ChromiumBase):
self._listener = FrameListener(self)
return self._listener
# ----------挂件----------
# ----------挂件结束----------
@property
def _obj_id(self):
@ -305,11 +290,6 @@ class ChromiumFrame(ChromiumBase):
r = self._ele('t:title', raise_err=False)
return r.text if r else None
@property
def cookies(self):
"""以dict格式返回cookies"""
return super().cookies if self._is_diff_domain else self.doc_ele.run_js('return this.cookie;')
@property
def attrs(self):
"""返回frame元素所有attribute属性"""
@ -356,23 +336,57 @@ class ChromiumFrame(ChromiumBase):
except:
return None
# ----------------即将废弃-----------------
@property
def is_alive(self):
"""返回是否仍可用"""
return self.states.is_alive
@property
def page_size(self):
"""返回frame内页面尺寸格式(宽,, 高)"""
return self.rect.size
@property
def size(self):
"""返回frame元素大小"""
return self.frame_ele.rect.size
@property
def location(self):
"""返回frame元素左上角的绝对坐标"""
return self.frame_ele.rect.location
@property
def locations(self):
"""返回用于获取元素位置的对象"""
return self.frame_ele.rect
# ----------------即将废弃结束-----------------
def refresh(self):
"""刷新frame页面"""
self.doc_ele.run_js('this.location.reload();')
def attr(self, attr):
"""返回frame元素attribute属性值
:param attr: 属性名
def property(self, name):
"""返回frame元素一个property属性值
:param name: 属性名
:return: 属性值文本没有该属性返回None
"""
return self.frame_ele.attr(attr)
return self.frame_ele.property(name)
def remove_attr(self, attr):
def attr(self, name):
"""返回frame元素一个attribute属性值
:param name: 属性名
:return: 属性值文本没有该属性返回None
"""
return self.frame_ele.attr(name)
def remove_attr(self, name):
"""删除frame元素attribute属性
:param attr: 属性名
:param name: 属性名
:return: None
"""
self.frame_ele.remove_attr(attr)
self.frame_ele.remove_attr(name)
def run_js(self, script, *args, as_expr=False, timeout=None):
"""运行javascript代码
@ -395,85 +409,85 @@ class ChromiumFrame(ChromiumBase):
"""
return self.frame_ele.parent(level_or_loc, index)
def prev(self, filter_loc='', index=1, timeout=0, ele_only=True):
def prev(self, locator='', index=1, timeout=0, ele_only=True):
"""返回当前元素前面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 前面第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 同级元素或节点
"""
return self.frame_ele.prev(filter_loc, index, timeout, ele_only=ele_only)
return self.frame_ele.prev(locator, index, timeout, ele_only=ele_only)
def next(self, filter_loc='', index=1, timeout=0, ele_only=True):
def next(self, locator='', index=1, timeout=0, ele_only=True):
"""返回当前元素后面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 后面第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 同级元素或节点
"""
return self.frame_ele.next(filter_loc, index, timeout, ele_only=ele_only)
return self.frame_ele.next(locator, index, timeout, ele_only=ele_only)
def before(self, filter_loc='', index=1, timeout=None, ele_only=True):
def before(self, locator='', index=1, timeout=None, ele_only=True):
"""返回文档中当前元素前面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个
查找范围不限同级元素而是整个DOM文档
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 前面第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素前面的某个元素或节点
"""
return self.frame_ele.before(filter_loc, index, timeout, ele_only=ele_only)
return self.frame_ele.before(locator, index, timeout, ele_only=ele_only)
def after(self, filter_loc='', index=1, timeout=None, ele_only=True):
def after(self, locator='', index=1, timeout=None, ele_only=True):
"""返回文档中此当前元素后面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个
查找范围不限同级元素而是整个DOM文档
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param index: 后面第几个查询结果1开始
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素后面的某个元素或节点
"""
return self.frame_ele.after(filter_loc, index, timeout, ele_only=ele_only)
return self.frame_ele.after(locator, index, timeout, ele_only=ele_only)
def prevs(self, filter_loc='', timeout=0, ele_only=True):
def prevs(self, locator='', timeout=0, ele_only=True):
"""返回当前元素前面符合条件的同级元素或节点组成的列表,可用查询语法筛选
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 同级元素或节点文本组成的列表
"""
return self.frame_ele.prevs(filter_loc, timeout, ele_only=ele_only)
return self.frame_ele.prevs(locator, timeout, ele_only=ele_only)
def nexts(self, filter_loc='', timeout=0, ele_only=True):
def nexts(self, locator='', timeout=0, ele_only=True):
"""返回当前元素后面符合条件的同级元素或节点组成的列表,可用查询语法筛选
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 同级元素或节点文本组成的列表
"""
return self.frame_ele.nexts(filter_loc, timeout, ele_only=ele_only)
return self.frame_ele.nexts(locator, timeout, ele_only=ele_only)
def befores(self, filter_loc='', timeout=None, ele_only=True):
def befores(self, locator='', timeout=None, ele_only=True):
"""返回文档中当前元素前面符合条件的元素或节点组成的列表,可用查询语法筛选
查找范围不限同级元素而是整个DOM文档
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素前面的元素或节点组成的列表
"""
return self.frame_ele.befores(filter_loc, timeout, ele_only=ele_only)
return self.frame_ele.befores(locator, timeout, ele_only=ele_only)
def afters(self, filter_loc='', timeout=None, ele_only=True):
def afters(self, locator='', timeout=None, ele_only=True):
"""返回文档中当前元素后面符合条件的元素或节点组成的列表,可用查询语法筛选
查找范围不限同级元素而是整个DOM文档
:param filter_loc: 用于筛选的查询语法
:param locator: 用于筛选的查询语法
:param timeout: 查找节点的超时时间
:param ele_only: 是否只获取元素为False时把文本注释节点也纳入
:return: 本元素前面的元素或节点组成的列表
"""
return self.frame_ele.afters(filter_loc, timeout, ele_only=ele_only)
return self.frame_ele.afters(locator, timeout, ele_only=ele_only)
def get_screenshot(self, path=None, name=None, as_bytes=None, as_base64=None):
"""对页面进行截图可对整个网页、可见网页、指定范围截图。对可视范围外截图需要90以上版本浏览器支持
@ -562,48 +576,21 @@ class ChromiumFrame(ChromiumBase):
self.tab.remove_ele(new_ele)
return r
def _find_elements(self, loc_or_ele, timeout=None, index=1, relative=False, raise_err=None):
def _find_elements(self, locator, timeout=None, index=1, relative=False, raise_err=None):
"""在frame内查找单个元素
:param loc_or_ele: 定位符或元素对象
:param locator: 定位符或元素对象
:param timeout: 查找超时时间
:param index: 第几个结果从1开始可传入负数获取倒数第几个为None返回所有
:param relative: WebPage用的表示是否相对定位的参数
:param raise_err: 找不到元素是是否抛出异常为None时根据全局设置
:return: ChromiumElement对象
"""
if isinstance(loc_or_ele, ChromiumElement):
return loc_or_ele
if isinstance(locator, ChromiumElement):
return locator
self.wait.doc_loaded()
return self.doc_ele._ele(loc_or_ele, index=index, timeout=timeout,
raise_err=raise_err) if index is not None else self.doc_ele.eles(loc_or_ele, timeout)
return self.doc_ele._ele(locator, index=index, timeout=timeout,
raise_err=raise_err) if index is not None else self.doc_ele.eles(locator, timeout)
def _is_inner_frame(self):
"""返回当前frame是否同域"""
return self._frame_id in str(self._target_page.run_cdp('Page.getFrameTree')['frameTree'])
# ----------------即将废弃-----------------
@property
def is_alive(self):
"""返回是否仍可用"""
return self.states.is_alive
@property
def page_size(self):
"""返回frame内页面尺寸格式(宽,, 高)"""
return self.rect.size
@property
def size(self):
"""返回frame元素大小"""
return self.frame_ele.rect.size
@property
def location(self):
"""返回frame元素左上角的绝对坐标"""
return self.frame_ele.rect.location
@property
def locations(self):
"""返回用于获取元素位置的对象"""
return self.frame_ele.rect

View File

@ -13,7 +13,6 @@ from .chromium_page import ChromiumPage
from .chromium_tab import ChromiumTab
from .web_page import WebPage
from .._elements.chromium_element import ChromiumElement
from .._elements.none_element import NoneElement
from .._units.listener import FrameListener
from .._units.rect import FrameRect
from .._units.scroller import FrameScroller
@ -32,6 +31,7 @@ class ChromiumFrame(ChromiumBase):
self._target_page: ChromiumBase = ...
self.tab: ChromiumTab = ...
self._tab_id: str = ...
self._set: ChromiumFrameSetter = ...
self._frame_ele: ChromiumElement = ...
self._backend_id: int = ...
self._doc_ele: ChromiumElement = ...
@ -43,9 +43,9 @@ class ChromiumFrame(ChromiumBase):
self._listener: FrameListener = ...
def __call__(self,
loc_or_str: Union[Tuple[str, str], str],
locator: Union[Tuple[str, str], str],
index: int = 1,
timeout: float = None) -> Union[ChromiumElement, NoneElement]: ...
timeout: float = None) -> ChromiumElement: ...
def __eq__(self, other: ChromiumFrame) -> bool: ...
@ -86,9 +86,6 @@ class ChromiumFrame(ChromiumBase):
@property
def title(self) -> str: ...
@property
def cookies(self) -> dict: ...
@property
def attrs(self) -> dict: ...
@ -133,9 +130,11 @@ class ChromiumFrame(ChromiumBase):
def refresh(self) -> None: ...
def attr(self, attr: str) -> Union[str, None]: ...
def property(self, name: str) -> Union[str, None]: ...
def remove_attr(self, attr: str) -> None: ...
def attr(self, name: str) -> Union[str, None]: ...
def remove_attr(self, name: str) -> None: ...
def run_js(self,
script: str,
@ -144,50 +143,50 @@ class ChromiumFrame(ChromiumBase):
timeout: float = None) -> Any: ...
def parent(self,
level_or_loc: Union[tuple, str, int] = 1,
index: int = 1) -> Union[ChromiumElement, NoneElement]: ...
level_or_loc: Union[Tuple[str, str], str, int] = 1,
index: int = 1) -> ChromiumElement: ...
def prev(self,
filter_loc: Union[tuple, str, int] = '',
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = 0,
ele_only: bool = True) -> Union[ChromiumElement, NoneElement, str]: ...
ele_only: bool = True) -> Union[ChromiumElement, str]: ...
def next(self,
filter_loc: Union[tuple, str, int] = '',
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = 0,
ele_only: bool = True) -> Union[ChromiumElement, NoneElement, str]: ...
ele_only: bool = True) -> Union[ChromiumElement, str]: ...
def before(self,
filter_loc: Union[tuple, str, int] = '',
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[ChromiumElement, NoneElement, str]: ...
ele_only: bool = True) -> Union[ChromiumElement, str]: ...
def after(self,
filter_loc: Union[tuple, str, int] = '',
locator: Union[Tuple[str, str], str, int] = '',
index: int = 1,
timeout: float = None,
ele_only: bool = True) -> Union[ChromiumElement, NoneElement, str]: ...
ele_only: bool = True) -> Union[ChromiumElement, str]: ...
def prevs(self,
filter_loc: Union[tuple, str] = '',
locator: Union[Tuple[str, str], str] = '',
timeout: float = 0,
ele_only: bool = True) -> List[Union[ChromiumElement, str]]: ...
def nexts(self,
filter_loc: Union[tuple, str] = '',
locator: Union[Tuple[str, str], str] = '',
timeout: float = 0,
ele_only: bool = True) -> List[Union[ChromiumElement, str]]: ...
def befores(self,
filter_loc: Union[tuple, str] = '',
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> List[Union[ChromiumElement, str]]: ...
def afters(self,
filter_loc: Union[tuple, str] = '',
locator: Union[Tuple[str, str], str] = '',
timeout: float = None,
ele_only: bool = True) -> List[Union[ChromiumElement, str]]: ...
@ -208,7 +207,7 @@ class ChromiumFrame(ChromiumBase):
ele: ChromiumElement = None) -> Union[str, bytes]: ...
def _find_elements(self,
loc_or_ele: Union[Tuple[str, str], str, ChromiumElement, ChromiumFrame],
locator: Union[Tuple[str, str], str, ChromiumElement, ChromiumFrame],
timeout: float = None,
index: Optional[int] = 1,
relative: bool = False,

View File

@ -6,13 +6,15 @@
@License : BSD 3-Clause.
"""
from pathlib import Path
from threading import Lock
from time import sleep, perf_counter
from requests import get
from .._base.browser import Browser
from .._configs.chromium_options import ChromiumOptions
from .._functions.browser import connect_browser
from .._configs.chromium_options import ChromiumOptions, PortFinder
from .._functions.tools import PortFinder
from .._pages.chromium_base import ChromiumBase, get_mhtml, get_pdf, Timeout
from .._pages.chromium_tab import ChromiumTab
from .._units.setter import ChromiumPageSetter
@ -22,7 +24,7 @@ from ..errors import BrowserConnectError
class ChromiumPage(ChromiumBase):
"""用于管理浏览器的类"""
PAGES = {}
_PAGES = {}
def __new__(cls, addr_or_opts=None, tab_id=None, timeout=None, addr_driver_opts=None):
"""
@ -33,14 +35,17 @@ class ChromiumPage(ChromiumBase):
addr_or_opts = addr_or_opts or addr_driver_opts
opt = handle_options(addr_or_opts)
is_exist, browser_id = run_browser(opt)
if browser_id in cls.PAGES:
return cls.PAGES[browser_id]
if browser_id in cls._PAGES:
r = cls._PAGES[browser_id]
while not hasattr(r, '_frame_id'):
sleep(.1)
return r
r = object.__new__(cls)
r._chromium_options = opt
r._is_exist = is_exist
r._browser_id = browser_id
r.address = opt.address
cls.PAGES[browser_id] = r
cls._PAGES[browser_id] = r
return r
def __init__(self, addr_or_opts=None, tab_id=None, timeout=None, addr_driver_opts=None):
@ -56,6 +61,8 @@ class ChromiumPage(ChromiumBase):
self._page = self
self._run_browser()
super().__init__(self.address, tab_id)
self._type = 'ChromiumPage'
self._lock = Lock()
self.set.timeouts(base=timeout)
self._page_init()
@ -72,7 +79,7 @@ class ChromiumPage(ChromiumBase):
def _d_set_runtime_settings(self):
"""设置运行时用到的属性"""
self._timeouts = Timeout(self, page_load=self._chromium_options.timeouts['pageLoad'],
self._timeouts = Timeout(self, page_load=self._chromium_options.timeouts['page_load'],
script=self._chromium_options.timeouts['script'],
base=self._chromium_options.timeouts['base'])
if self._chromium_options.timeouts['base'] is not None:
@ -145,16 +152,17 @@ class ChromiumPage(ChromiumBase):
:param id_or_num: 要获取的标签页id或序号为None时获取当前tab序号从1开始可传入负数获取倒数第几个不是视觉排列顺序而是激活顺序
:return: 标签页对象
"""
if isinstance(id_or_num, str):
return ChromiumTab(self, id_or_num)
elif isinstance(id_or_num, int):
return ChromiumTab(self, self.tabs[id_or_num - 1 if id_or_num > 0 else id_or_num])
elif id_or_num is None:
return ChromiumTab(self, self.tab_id)
elif isinstance(id_or_num, ChromiumTab):
return id_or_num
else:
raise TypeError(f'id_or_num需传入tab id或序号{id_or_num}')
with self._lock:
if isinstance(id_or_num, str):
return ChromiumTab(self, id_or_num)
elif isinstance(id_or_num, int):
return ChromiumTab(self, self.tabs[id_or_num - 1 if id_or_num > 0 else id_or_num])
elif id_or_num is None:
return ChromiumTab(self, self.tab_id)
elif isinstance(id_or_num, ChromiumTab):
return id_or_num
else:
raise TypeError(f'id_or_num需传入tab id或序号{id_or_num}')
def find_tabs(self, title=None, url=None, tab_type=None, single=True):
"""查找符合条件的tab返回它们的id组成的列表
@ -202,7 +210,7 @@ class ChromiumPage(ChromiumBase):
def close(self):
"""关闭Page管理的标签页"""
self.browser.close_tab(self.tab_id)
self.close_tabs(self.tab_id)
def close_tabs(self, tabs_or_ids=None, others=False):
"""关闭传入的标签页,默认关闭当前页。可传入多个
@ -247,7 +255,7 @@ class ChromiumPage(ChromiumBase):
def _on_disconnect(self):
"""浏览器退出时执行"""
ChromiumPage.PAGES.pop(self._browser_id, None)
ChromiumPage._PAGES.pop(self._browser_id, None)
def __repr__(self):
return f'<ChromiumPage browser_id={self.browser.id} tab_id={self.tab_id}>'
@ -268,13 +276,18 @@ def handle_options(addr_or_opts):
"""
if not addr_or_opts:
_chromium_options = ChromiumOptions(addr_or_opts)
if _chromium_options.is_auto_port:
port, path = PortFinder(_chromium_options.tmp_path).get_port(_chromium_options.is_auto_port)
_chromium_options.set_address(f'127.0.0.1:{port}')
_chromium_options.set_user_data_path(path)
_chromium_options.auto_port(scope=_chromium_options.is_auto_port)
elif isinstance(addr_or_opts, ChromiumOptions):
if addr_or_opts.is_auto_port:
port, path = PortFinder(addr_or_opts.tmp_path).get_port()
port, path = PortFinder(addr_or_opts.tmp_path).get_port(addr_or_opts.is_auto_port)
addr_or_opts.set_address(f'127.0.0.1:{port}')
addr_or_opts.set_user_data_path(path)
addr_or_opts.auto_port()
addr_or_opts.auto_port(scope=addr_or_opts.is_auto_port)
_chromium_options = addr_or_opts
elif isinstance(addr_or_opts, str):

View File

@ -6,6 +6,7 @@
@License : BSD 3-Clause.
"""
from pathlib import Path
from threading import Lock
from typing import Union, Tuple, List, Optional
from .._base.browser import Browser
@ -18,7 +19,7 @@ from .._units.waiter import PageWaiter
class ChromiumPage(ChromiumBase):
PAGES: dict = ...
_PAGES: dict = ...
def __new__(cls,
addr_or_opts: Union[str, int, ChromiumOptions] = None,
@ -34,6 +35,7 @@ class ChromiumPage(ChromiumBase):
self._browser_id: str = ...
self._rect: Optional[TabRect] = ...
self._is_exist: bool = ...
self._lock: Lock = ...
def _handle_options(self, addr_or_opts: Union[str, ChromiumOptions]) -> str: ...
@ -53,9 +55,6 @@ class ChromiumPage(ChromiumBase):
@property
def wait(self) -> PageWaiter: ...
@property
def main_tab(self) -> str: ...
@property
def latest_tab(self) -> str: ...

View File

@ -6,6 +6,7 @@
@License : BSD 3-Clause.
"""
from copy import copy
from time import sleep
from .._base.base import BasePage
from .._configs.session_options import SessionOptions
@ -19,17 +20,20 @@ from .._units.waiter import TabWaiter
class ChromiumTab(ChromiumBase):
"""实现浏览器标签页的类"""
TABS = {}
_TABS = {}
def __new__(cls, page, tab_id):
"""
:param page: ChromiumPage对象
:param tab_id: 要控制的标签页id
"""
if Settings.singleton_tab_obj and tab_id in cls.TABS:
return cls.TABS[tab_id]
if Settings.singleton_tab_obj and tab_id in cls._TABS:
r = cls._TABS[tab_id]
while not hasattr(r, '_frame_id'):
sleep(.1)
return r
r = object.__new__(cls)
cls.TABS[tab_id] = r
cls._TABS[tab_id] = r
return r
def __init__(self, page, tab_id):
@ -45,6 +49,7 @@ class ChromiumTab(ChromiumBase):
self._browser = page.browser
super().__init__(page.address, tab_id, page.timeout)
self._rect = None
self._type = 'ChromiumTab'
def _d_set_runtime_settings(self):
"""重写设置浏览器运行参数方法"""
@ -91,7 +96,7 @@ class ChromiumTab(ChromiumBase):
return f'<ChromiumTab browser_id={self.browser.id} tab_id={self.tab_id}>'
def _on_disconnect(self):
ChromiumTab.TABS.pop(self.tab_id, None)
ChromiumTab._TABS.pop(self.tab_id, None)
class WebPageTab(SessionPage, ChromiumTab, BasePage):
@ -106,19 +111,20 @@ class WebPageTab(SessionPage, ChromiumTab, BasePage):
super().__init__(session_or_options=SessionOptions(read_file=False).from_session(copy(page.session),
page._headers))
super(SessionPage, self).__init__(page=page, tab_id=tab_id)
self._type = 'WebPageTab'
def __call__(self, loc_or_str, index=1, timeout=None):
def __call__(self, locator, index=1, timeout=None):
"""在内部查找元素
ele = page('@id=ele_id')
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:param timeout: 超时时间
:return: 子元素对象
"""
if self._mode == 'd':
return super(SessionPage, self).__call__(loc_or_str, index=index, timeout=timeout)
return super(SessionPage, self).__call__(locator, index=index, timeout=timeout)
elif self._mode == 's':
return super().__call__(loc_or_str, index=index)
return super().__call__(locator, index=index)
@property
def set(self):
@ -182,11 +188,6 @@ class WebPageTab(SessionPage, ChromiumTab, BasePage):
"""返回当前模式,'s''d' """
return self._mode
@property
def cookies(self):
"""以dict方式返回cookies"""
return super().cookies
@property
def user_agent(self):
"""返回user agent"""
@ -252,49 +253,49 @@ class WebPageTab(SessionPage, ChromiumTab, BasePage):
return self.response
return super().post(url, show_errmsg, retry, interval, **kwargs)
def ele(self, loc_or_ele, index=1, timeout=None):
def ele(self, locator, index=1, timeout=None):
"""返回第一个符合条件的元素、属性或节点文本
:param loc_or_ele: 元素的定位信息可以是元素对象loc元组或查询字符串
:param locator: 元素的定位信息可以是元素对象loc元组或查询字符串
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:param timeout: 查找元素超时时间默认与页面等待时间一致
:return: 元素对象或属性文本节点文本
"""
if self._mode == 's':
return super().ele(loc_or_ele, index=index)
return super().ele(locator, index=index)
elif self._mode == 'd':
return super(SessionPage, self).ele(loc_or_ele, index=index, timeout=timeout)
return super(SessionPage, self).ele(locator, index=index, timeout=timeout)
def eles(self, loc_or_str, timeout=None):
def eles(self, locator, timeout=None):
"""返回页面中所有符合条件的元素、属性或节点文本
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 查找元素超时时间默认与页面等待时间一致
:return: 元素对象或属性文本组成的列表
"""
if self._mode == 's':
return super().eles(loc_or_str)
return super().eles(locator)
elif self._mode == 'd':
return super(SessionPage, self).eles(loc_or_str, timeout=timeout)
return super(SessionPage, self).eles(locator, timeout=timeout)
def s_ele(self, loc_or_ele=None, index=1):
def s_ele(self, locator=None, index=1):
"""查找第一个符合条件的元素以SessionElement形式返回d模式处理复杂页面时效率很高
:param loc_or_ele: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:return: SessionElement对象或属性文本
"""
if self._mode == 's':
return super().s_ele(loc_or_ele, index=index)
return super().s_ele(locator, index=index)
elif self._mode == 'd':
return super(SessionPage, self).s_ele(loc_or_ele, index=index)
return super(SessionPage, self).s_ele(locator, index=index)
def s_eles(self, loc_or_str):
def s_eles(self, locator):
"""查找所有符合条件的元素以SessionElement形式返回d模式处理复杂页面时效率很高
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:return: SessionElement对象或属性文本组成的列表
"""
if self._mode == 's':
return super().s_eles(loc_or_str)
return super().s_eles(locator)
elif self._mode == 'd':
return super(SessionPage, self).s_eles(loc_or_str)
return super(SessionPage, self).s_eles(locator)
def change_mode(self, mode=None, go=True, copy_cookies=True):
"""切换模式,接收's''d',除此以外的字符串会切换为 d 模式
@ -351,15 +352,15 @@ class WebPageTab(SessionPage, ChromiumTab, BasePage):
user_agent = self.run_cdp('Runtime.evaluate', expression='navigator.userAgent;')['result']['value']
self._headers.update({"User-Agent": user_agent})
set_session_cookies(self.session, super(SessionPage, self).get_cookies())
set_session_cookies(self.session, super(SessionPage, self).cookies())
def cookies_to_browser(self):
"""把session对象的cookies复制到浏览器"""
if not self._has_driver:
return
set_browser_cookies(self, super().get_cookies())
set_browser_cookies(self, super().cookies())
def get_cookies(self, as_dict=False, all_domains=False, all_info=False):
def cookies(self, as_dict=False, all_domains=False, all_info=False):
"""返回cookies
:param as_dict: 是否以字典方式返回
:param all_domains: 是否返回所有域的cookies
@ -367,9 +368,9 @@ class WebPageTab(SessionPage, ChromiumTab, BasePage):
:return: cookies信息
"""
if self._mode == 's':
return super().get_cookies(as_dict, all_domains, all_info)
return super().cookies(as_dict, all_domains, all_info)
elif self._mode == 'd':
return super(SessionPage, self).get_cookies(as_dict, all_domains, all_info)
return super(SessionPage, self).cookies(as_dict, all_domains, all_info)
def close(self):
"""关闭当前标签页"""
@ -378,9 +379,9 @@ class WebPageTab(SessionPage, ChromiumTab, BasePage):
if self._response is not None:
self._response.close()
def _find_elements(self, loc_or_ele, timeout=None, index=1, relative=False, raise_err=None):
def _find_elements(self, locator, timeout=None, index=1, relative=False, raise_err=None):
"""返回页面中符合条件的元素、属性或节点文本,默认返回第一个
:param loc_or_ele: 元素的定位信息可以是元素对象loc元组或查询字符串
:param locator: 元素的定位信息可以是元素对象loc元组或查询字符串
:param timeout: 查找元素超时时间d模式专用
:param index: 第几个结果从1开始可传入负数获取倒数第几个为None返回所有
:param relative: WebPage用的表示是否相对定位的参数
@ -388,9 +389,13 @@ class WebPageTab(SessionPage, ChromiumTab, BasePage):
:return: 元素对象或属性文本节点文本
"""
if self._mode == 's':
return super()._find_elements(loc_or_ele, index=index)
return super()._find_elements(locator, index=index)
elif self._mode == 'd':
return super(SessionPage, self)._find_elements(loc_or_ele, timeout=timeout, index=index, relative=relative)
return super(SessionPage, self)._find_elements(locator, timeout=timeout, index=index, relative=relative)
def __repr__(self):
return f'<WebPageTab browser_id={self.browser.id} tab_id={self.tab_id}>'
# --------即将废弃-------
def get_cookies(self, as_dict=False, all_domains=False, all_info=False):
return self.cookies(as_dict=as_dict, all_domains=all_domains, all_info=all_info)

View File

@ -17,7 +17,6 @@ from .session_page import SessionPage
from .web_page import WebPage
from .._base.browser import Browser
from .._elements.chromium_element import ChromiumElement
from .._elements.none_element import NoneElement
from .._elements.session_element import SessionElement
from .._units.rect import TabRect
from .._units.setter import TabSetter, WebPageTabSetter
@ -25,7 +24,7 @@ from .._units.waiter import TabWaiter
class ChromiumTab(ChromiumBase):
TABS: dict = ...
_TABS: dict = ...
def __new__(cls, page: ChromiumPage, tab_id: str): ...
@ -78,9 +77,9 @@ class WebPageTab(SessionPage, ChromiumTab):
self._has_session = ...
def __call__(self,
loc_or_str: Union[Tuple[str, str], str, ChromiumElement, SessionElement],
locator: Union[Tuple[str, str], str, ChromiumElement, SessionElement],
index: int = 1,
timeout: float = None) -> Union[ChromiumElement, SessionElement, NoneElement]: ...
timeout: float = None) -> Union[ChromiumElement, SessionElement]: ...
@property
def page(self) -> WebPage: ...
@ -109,9 +108,6 @@ class WebPageTab(SessionPage, ChromiumTab):
@property
def mode(self) -> str: ...
@property
def cookies(self) -> dict: ...
@property
def user_agent(self) -> str: ...
@ -148,19 +144,19 @@ class WebPageTab(SessionPage, ChromiumTab):
cert: Any | None = ...) -> Union[bool, None]: ...
def ele(self,
loc_or_ele: Union[Tuple[str, str], str, ChromiumElement, SessionElement],
locator: Union[Tuple[str, str], str, ChromiumElement, SessionElement],
index: int = 1,
timeout: float = None) -> Union[ChromiumElement, SessionElement, NoneElement]: ...
timeout: float = None) -> Union[ChromiumElement, SessionElement]: ...
def eles(self,
loc_or_str: Union[Tuple[str, str], str],
locator: Union[Tuple[str, str], str],
timeout: float = None) -> List[Union[ChromiumElement, SessionElement]]: ...
def s_ele(self,
loc_or_ele: Union[Tuple[str, str], str] = None,
index: int = 1) -> Union[SessionElement, NoneElement]: ...
locator: Union[Tuple[str, str], str] = None,
index: int = 1) -> SessionElement: ...
def s_eles(self, loc_or_str: Union[Tuple[str, str], str]) -> List[SessionElement]: ...
def s_eles(self, locator: Union[Tuple[str, str], str]) -> List[SessionElement]: ...
def change_mode(self, mode: str = None, go: bool = True, copy_cookies: bool = True) -> None: ...
@ -168,8 +164,8 @@ class WebPageTab(SessionPage, ChromiumTab):
def cookies_to_browser(self) -> None: ...
def get_cookies(self, as_dict: bool = False, all_domains: bool = False,
all_info: bool = False) -> Union[dict, list]: ...
def cookies(self, as_dict: bool = False, all_domains: bool = False,
all_info: bool = False) -> Union[dict, list]: ...
def close(self) -> None: ...
@ -198,10 +194,10 @@ class WebPageTab(SessionPage, ChromiumTab):
def set(self) -> WebPageTabSetter: ...
def _find_elements(self,
loc_or_ele: Union[Tuple[str, str], str, ChromiumElement, SessionElement, ChromiumFrame],
locator: Union[Tuple[str, str], str, ChromiumElement, SessionElement, ChromiumFrame],
timeout: float = None,
index: Optional[int] = 1,
relative: bool = False,
raise_err: bool = None) \
-> Union[ChromiumElement, SessionElement, ChromiumFrame, NoneElement, List[SessionElement], List[
-> Union[ChromiumElement, SessionElement, ChromiumFrame, List[SessionElement], List[
Union[ChromiumElement, ChromiumFrame]]]: ...

View File

@ -35,6 +35,7 @@ class SessionPage(BasePage):
self._session = None
self._set = None
self._encoding = None
self._type = 'SessionPage'
self._s_set_start_options(session_or_options)
self._s_set_runtime_settings()
self._create_session()
@ -46,8 +47,11 @@ class SessionPage(BasePage):
:param session_or_options: SessionSessionOptions对象
:return: None
"""
if not session_or_options or isinstance(session_or_options, SessionOptions):
self._session_options = session_or_options or SessionOptions(session_or_options)
if not session_or_options:
self._session_options = SessionOptions(session_or_options)
elif isinstance(session_or_options, SessionOptions):
self._session_options = session_or_options
elif isinstance(session_or_options, Session):
self._session_options = SessionOptions()
@ -68,15 +72,15 @@ class SessionPage(BasePage):
if not self._session:
self._session, self._headers = self._session_options.make_session()
def __call__(self, loc_or_str, index=1, timeout=None):
def __call__(self, locator, index=1, timeout=None):
"""在内部查找元素
ele2 = ele1('@id=ele_id')
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:param timeout: 不起实际作用用于和ChromiumElement对应便于无差别调用
:return: SessionElement对象或属性文本
"""
return self.ele(loc_or_str, index=index)
return self.ele(locator, index=index)
# -----------------共有属性和方法-------------------
@property
@ -175,50 +179,49 @@ class SessionPage(BasePage):
"""
return self._s_connect(url, 'post', show_errmsg, retry, interval, **kwargs)
def ele(self, loc_or_ele, index=1, timeout=None):
def ele(self, locator, index=1, timeout=None):
"""返回页面中符合条件的一个元素、属性或节点文本
:param loc_or_ele: 元素的定位信息可以是元素对象loc元组或查询字符串
:param locator: 元素的定位信息可以是元素对象loc元组或查询字符串
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:param timeout: 不起实际作用用于和ChromiumElement对应便于无差别调用
:return: SessionElement对象或属性文本
"""
return self._ele(loc_or_ele, index=index, method='ele()')
return self._ele(locator, index=index, method='ele()')
def eles(self, loc_or_str, timeout=None):
def eles(self, locator, timeout=None):
"""返回页面中所有符合条件的元素、属性或节点文本
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 不起实际作用用于和ChromiumElement对应便于无差别调用
:return: SessionElement对象或属性文本组成的列表
"""
return self._ele(loc_or_str, index=None)
return self._ele(locator, index=None)
def s_ele(self, loc_or_ele=None, index=1):
def s_ele(self, locator=None, index=1):
"""返回页面中符合条件的一个元素、属性或节点文本
:param loc_or_ele: 元素的定位信息可以是元素对象loc元组或查询字符串
:param locator: 元素的定位信息可以是元素对象loc元组或查询字符串
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:return: SessionElement对象或属性文本
"""
return make_session_ele(self.html) if loc_or_ele is None else self._ele(loc_or_ele,
index=index, method='s_ele()')
return make_session_ele(self.html) if locator is None else self._ele(locator, index=index, method='s_ele()')
def s_eles(self, loc_or_str):
def s_eles(self, locator):
"""返回页面中符合条件的所有元素、属性或节点文本
:param loc_or_str: 元素的定位信息可以是元素对象loc元组或查询字符串
:param locator: 元素的定位信息可以是元素对象loc元组或查询字符串
:return: SessionElement对象或属性文本
"""
return self._ele(loc_or_str, index=None)
return self._ele(locator, index=None)
def _find_elements(self, loc_or_ele, timeout=None, index=1, raise_err=None):
def _find_elements(self, locator, timeout=None, index=1, relative=True, raise_err=None):
"""返回页面中符合条件的元素、属性或节点文本,默认返回第一个
:param loc_or_ele: 元素的定位信息可以是元素对象loc元组或查询字符串
:param locator: 元素的定位信息可以是元素对象loc元组或查询字符串
:param timeout: 不起实际作用用于和父类对应
:param index: 第几个结果从1开始可传入负数获取倒数第几个为None返回所有
:param raise_err: 找不到元素是是否抛出异常为None时根据全局设置
:return: SessionElement对象
"""
return loc_or_ele if isinstance(loc_or_ele, SessionElement) else make_session_ele(self, loc_or_ele, index=index)
return locator if isinstance(locator, SessionElement) else make_session_ele(self, locator, index=index)
def get_cookies(self, as_dict=False, all_domains=False, all_info=False):
def cookies(self, as_dict=False, all_domains=False, all_info=False):
"""返回cookies
:param as_dict: 是否以字典方式返回False则以list返回
:param all_domains: 是否返回所有域的cookies
@ -310,7 +313,7 @@ class SessionPage(BasePage):
parsed_url = urlparse(url)
hostname = parsed_url.hostname
scheme = parsed_url.scheme
if not check_headers(kwargs, self._headers, 'Referer'):
if not check_headers(kwargs['headers'], self._headers, 'Referer'):
kwargs['headers']['Referer'] = self.url if self.url else f'{scheme}://{hostname}'
if 'Host' not in kwargs['headers']:
kwargs['headers']['Host'] = hostname
@ -364,10 +367,14 @@ class SessionPage(BasePage):
def __repr__(self):
return f'<SessionPage url={self.url}>'
# ---------即将废弃---------
def get_cookies(self, as_dict=False, all_domains=False, all_info=False):
return self.cookies(as_dict=as_dict, all_domains=all_domains, all_info=all_info)
def check_headers(kwargs, headers, arg):
"""检查kwargs或headers中是否有arg所示属性"""
return arg in kwargs['headers'] or arg in headers
return arg in kwargs or arg in headers
def set_charset(response):

View File

@ -13,7 +13,6 @@ from requests.structures import CaseInsensitiveDict
from .._base.base import BasePage
from .._configs.session_options import SessionOptions
from .._elements.none_element import NoneElement
from .._elements.session_element import SessionElement
from .._units.setter import SessionPageSetter
@ -41,9 +40,9 @@ class SessionPage(BasePage):
def _create_session(self) -> None: ...
def __call__(self,
loc_or_str: Union[Tuple[str, str], str, SessionElement],
locator: Union[Tuple[str, str], str, SessionElement],
index: int = 1,
timeout: float = None) -> Union[SessionElement, NoneElement]: ...
timeout: float = None) -> SessionElement: ...
# -----------------共有属性和方法-------------------
@property
@ -91,28 +90,29 @@ class SessionPage(BasePage):
cert: Any | None = ...) -> bool: ...
def ele(self,
loc_or_ele: Union[Tuple[str, str], str, SessionElement],
locator: Union[Tuple[str, str], str, SessionElement],
index: int = 1,
timeout: float = None) -> Union[SessionElement, NoneElement]: ...
timeout: float = None) -> SessionElement: ...
def eles(self,
loc_or_str: Union[Tuple[str, str], str],
locator: Union[Tuple[str, str], str],
timeout: float = None) -> List[SessionElement]: ...
def s_ele(self,
loc_or_ele: Union[Tuple[str, str], str, SessionElement] = None,
index: int = 1) -> Union[SessionElement, NoneElement]: ...
locator: Union[Tuple[str, str], str, SessionElement] = None,
index: int = 1) -> SessionElement: ...
def s_eles(self, loc_or_str: Union[Tuple[str, str], str]) -> List[SessionElement]: ...
def s_eles(self, loc: Union[Tuple[str, str], str]) -> List[SessionElement]: ...
def _find_elements(self,
loc_or_ele: Union[Tuple[str, str], str, SessionElement],
locator: Union[Tuple[str, str], str, SessionElement],
timeout: float = None,
index: Optional[int] = 1,
relative: bool = True,
raise_err: bool = None) \
-> Union[SessionElement, NoneElement, List[SessionElement]]: ...
-> Union[SessionElement, List[SessionElement]]: ...
def get_cookies(self,
def cookies(self,
as_dict: bool = False,
all_domains: bool = False,
all_info: bool = False) -> Union[dict, list]: ...

View File

@ -48,20 +48,21 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
chromium_options = ChromiumOptions(read_file=chromium_options)
chromium_options.set_timeouts(base=self._timeout).set_paths(download_path=self.download_path)
super(SessionPage, self).__init__(addr_or_opts=chromium_options, timeout=timeout)
self._type = 'WebPage'
self.change_mode(self._mode, go=False, copy_cookies=False)
def __call__(self, loc_or_str, index=1, timeout=None):
def __call__(self, locator, index=1, timeout=None):
"""在内部查找元素
ele = page('@id=ele_id')
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:param timeout: 超时时间
:return: 子元素对象
"""
if self._mode == 'd':
return super(SessionPage, self).__call__(loc_or_str, index=index, timeout=timeout)
return super(SessionPage, self).__call__(locator, index=index, timeout=timeout)
elif self._mode == 's':
return super().__call__(loc_or_str, index=index)
return super().__call__(locator, index=index)
@property
def set(self):
@ -125,11 +126,6 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
"""返回当前模式,'s''d' """
return self._mode
@property
def cookies(self):
"""以dict方式返回cookies"""
return super().cookies
@property
def user_agent(self):
"""返回user agent"""
@ -195,49 +191,49 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
return self.response
return super().post(url, show_errmsg, retry, interval, **kwargs)
def ele(self, loc_or_ele, index=1, timeout=None):
def ele(self, locator, index=1, timeout=None):
"""返回第一个符合条件的元素、属性或节点文本
:param loc_or_ele: 元素的定位信息可以是元素对象loc元组或查询字符串
:param locator: 元素的定位信息可以是元素对象loc元组或查询字符串
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:param timeout: 查找元素超时时间默认与页面等待时间一致
:return: 元素对象或属性文本节点文本
"""
if self._mode == 's':
return super().ele(loc_or_ele, index=index)
return super().ele(locator, index=index)
elif self._mode == 'd':
return super(SessionPage, self).ele(loc_or_ele, index=index, timeout=timeout)
return super(SessionPage, self).ele(locator, index=index, timeout=timeout)
def eles(self, loc_or_str, timeout=None):
def eles(self, locator, timeout=None):
"""返回页面中所有符合条件的元素、属性或节点文本
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 查找元素超时时间默认与页面等待时间一致
:return: 元素对象或属性文本组成的列表
"""
if self._mode == 's':
return super().eles(loc_or_str)
return super().eles(locator)
elif self._mode == 'd':
return super(SessionPage, self).eles(loc_or_str, timeout=timeout)
return super(SessionPage, self).eles(locator, timeout=timeout)
def s_ele(self, loc_or_ele=None, index=1):
def s_ele(self, locator=None, index=1):
"""查找第一个符合条件的元素以SessionElement形式返回d模式处理复杂页面时效率很高
:param loc_or_ele: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:param index: 获取第几个从1开始可传入负数获取倒数第几个
:return: SessionElement对象或属性文本
"""
if self._mode == 's':
return super().s_ele(loc_or_ele, index=index)
return super().s_ele(locator, index=index)
elif self._mode == 'd':
return super(SessionPage, self).s_ele(loc_or_ele, index=index)
return super(SessionPage, self).s_ele(locator, index=index)
def s_eles(self, loc_or_str):
def s_eles(self, locator):
"""查找所有符合条件的元素以SessionElement形式返回d模式处理复杂页面时效率很高
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param locator: 元素的定位信息可以是loc元组或查询字符串
:return: SessionElement对象或属性文本组成的列表
"""
if self._mode == 's':
return super().s_eles(loc_or_str)
return super().s_eles(locator)
elif self._mode == 'd':
return super(SessionPage, self).s_eles(loc_or_str)
return super(SessionPage, self).s_eles(locator)
def change_mode(self, mode=None, go=True, copy_cookies=True):
"""切换模式,接收's''d',除此以外的字符串会切换为 d 模式
@ -280,7 +276,9 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
if go:
url = super(SessionPage, self).url
if url.startswith('http'):
self.get(url)
r = self.get(url)
if not r:
raise ConnectionError('s模式访问失败请设置go=False自行构造连接参数进行访问。')
def cookies_to_session(self, copy_user_agent=True):
"""把driver对象的cookies复制到session对象
@ -294,15 +292,15 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
user_agent = self.run_cdp('Runtime.evaluate', expression='navigator.userAgent;')['result']['value']
self._headers.update({"User-Agent": user_agent})
set_session_cookies(self.session, super(SessionPage, self).get_cookies())
set_session_cookies(self.session, super(SessionPage, self).cookies())
def cookies_to_browser(self):
"""把session对象的cookies复制到浏览器"""
if not self._has_driver:
return
set_browser_cookies(self, super().get_cookies())
set_browser_cookies(self, super().cookies())
def get_cookies(self, as_dict=False, all_domains=False, all_info=False):
def cookies(self, as_dict=False, all_domains=False, all_info=False):
"""返回cookies
:param as_dict: 是否以字典方式返回False以list形式返回
:param all_domains: 是否返回所有域的cookies
@ -310,9 +308,9 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
:return: cookies信息
"""
if self._mode == 's':
return super().get_cookies(as_dict, all_domains, all_info)
return super().cookies(as_dict, all_domains, all_info)
elif self._mode == 'd':
return super(SessionPage, self).get_cookies(as_dict, all_domains, all_info)
return super(SessionPage, self).cookies(as_dict, all_domains, all_info)
def get_tab(self, id_or_num=None):
"""获取一个标签页对象
@ -375,9 +373,9 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
if self._response is not None:
self._response.close()
def _find_elements(self, loc_or_ele, timeout=None, index=1, relative=False, raise_err=None):
def _find_elements(self, locator, timeout=None, index=1, relative=False, raise_err=None):
"""返回页面中符合条件的元素、属性或节点文本,默认返回第一个
:param loc_or_ele: 元素的定位信息可以是元素对象loc元组或查询字符串
:param locator: 元素的定位信息可以是元素对象loc元组或查询字符串
:param timeout: 查找元素超时时间d模式专用
:param index: 第几个结果从1开始可传入负数获取倒数第几个为None返回所有
:param relative: WebPage用的表示是否相对定位的参数
@ -385,9 +383,9 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
:return: 元素对象或属性文本节点文本
"""
if self._mode == 's':
return super()._find_elements(loc_or_ele, index=index)
return super()._find_elements(locator, index=index)
elif self._mode == 'd':
return super(SessionPage, self)._find_elements(loc_or_ele, timeout=timeout, index=index, relative=relative)
return super(SessionPage, self)._find_elements(locator, timeout=timeout, index=index, relative=relative)
def quit(self, timeout=5, force=True):
"""关闭浏览器和Session
@ -407,3 +405,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
def __repr__(self):
return f'<WebPage browser_id={self.browser.id} tab_id={self.tab_id}>'
# -------即将废弃--------
def get_cookies(self, as_dict=False, all_domains=False, all_info=False):
return self.cookies(as_dict=as_dict, all_domains=all_domains, all_info=all_info)

View File

@ -18,7 +18,6 @@ from .._base.driver import Driver
from .._configs.chromium_options import ChromiumOptions
from .._configs.session_options import SessionOptions
from .._elements.chromium_element import ChromiumElement
from .._elements.none_element import NoneElement
from .._elements.session_element import SessionElement
from .._units.setter import WebPageSetter
@ -31,15 +30,16 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
chromium_options: Union[ChromiumOptions, bool] = None,
session_or_options: Union[Session, SessionOptions, bool] = None) -> None:
self._mode: str = ...
self._set: WebPageSetter = ...
self._has_driver: bool = ...
self._has_session: bool = ...
self._session_options: Union[SessionOptions, None] = ...
self._chromium_options: Union[ChromiumOptions, None] = ...
def __call__(self,
loc_or_str: Union[Tuple[str, str], str, ChromiumElement, SessionElement],
locator: Union[Tuple[str, str], str, ChromiumElement, SessionElement],
index: int = 1,
timeout: float = None) -> Union[ChromiumElement, SessionElement, NoneElement]: ...
timeout: float = None) -> Union[ChromiumElement, SessionElement]: ...
# -----------------共有属性和方法-------------------
@property
@ -66,9 +66,6 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
@property
def mode(self) -> str: ...
@property
def cookies(self) -> dict: ...
@property
def user_agent(self) -> str: ...
@ -105,19 +102,19 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
cert: Any | None = ...) -> Union[bool, None]: ...
def ele(self,
loc_or_ele: Union[Tuple[str, str], str, ChromiumElement, SessionElement],
locator: Union[Tuple[str, str], str, ChromiumElement, SessionElement],
index: int = 1,
timeout: float = None) -> Union[ChromiumElement, SessionElement, NoneElement]: ...
timeout: float = None) -> Union[ChromiumElement, SessionElement]: ...
def eles(self,
loc_or_str: Union[Tuple[str, str], str],
locator: Union[Tuple[str, str], str],
timeout: float = None) -> List[Union[ChromiumElement, SessionElement]]: ...
def s_ele(self,
loc_or_ele: Union[Tuple[str, str], str] = None,
index: int = 1) -> Union[SessionElement, NoneElement]: ...
locator: Union[Tuple[str, str], str] = None,
index: int = 1) -> SessionElement: ...
def s_eles(self, loc_or_str: Union[Tuple[str, str], str]) -> List[SessionElement]: ...
def s_eles(self, locator: Union[Tuple[str, str], str]) -> List[SessionElement]: ...
def change_mode(self, mode: str = None, go: bool = True, copy_cookies: bool = True) -> None: ...
@ -125,10 +122,10 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
def cookies_to_browser(self) -> None: ...
def get_cookies(self,
as_dict: bool = False,
all_domains: bool = False,
all_info: bool = False) -> Union[dict, list]: ...
def cookies(self,
as_dict: bool = False,
all_domains: bool = False,
all_info: bool = False) -> Union[dict, list]: ...
def get_tab(self, id_or_num: Union[str, WebPageTab, int] = None) -> WebPageTab: ...
@ -169,12 +166,12 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
def set(self) -> WebPageSetter: ...
def _find_elements(self,
loc_or_ele: Union[Tuple[str, str], str, ChromiumElement, SessionElement, ChromiumFrame],
locator: Union[Tuple[str, str], str, ChromiumElement, SessionElement, ChromiumFrame],
timeout: float = None,
index: Optional[int] = 1,
relative: bool = False,
raise_err: bool = None) \
-> Union[ChromiumElement, SessionElement, ChromiumFrame, NoneElement, List[SessionElement],
-> Union[ChromiumElement, SessionElement, ChromiumFrame, List[SessionElement],
List[Union[ChromiumElement, ChromiumFrame]]]: ...
def _set_start_options(self,

View File

@ -39,7 +39,7 @@ class Actions:
is_loc = True
lx = ele_or_loc[0] + offset_x
ly = ele_or_loc[1] + offset_y
elif isinstance(ele_or_loc, str) or 'ChromiumElement' in str(type(ele_or_loc)):
elif isinstance(ele_or_loc, str) or ele_or_loc._type == 'ChromiumElement':
ele_or_loc = self.page(ele_or_loc)
self.page.scroll.to_see(ele_or_loc)
x, y = ele_or_loc.rect.location if offset_x or offset_y else ele_or_loc.rect.midpoint

View File

@ -5,7 +5,7 @@
@Copyright: (c) 2024 by g1879, Inc. All Rights Reserved.
@License : BSD 3-Clause.
"""
from typing import Union, Optional
from typing import Optional
from .._elements.chromium_element import ChromiumElement

View File

@ -100,6 +100,9 @@ class Listener(object):
Manifest, SignedExchange, Ping, CSPViolationReport, Preflight, Other
:return: None
"""
if targets is not None:
if is_regex is None:
is_regex = False
if targets or is_regex is not None or method or res_type:
self.set_targets(targets, is_regex, method, res_type)
self.clear()

View File

@ -6,7 +6,7 @@
@License : BSD 3-Clause.
"""
from typing import Tuple, Union, List
from typing import Tuple, Union
from .._elements.chromium_element import ChromiumElement
from .._pages.chromium_base import ChromiumBase

View File

@ -87,7 +87,7 @@ class Scroller(object):
if not self._wait_complete:
return
page = self._driver.page if 'ChromiumElement' in str(type(self._driver)) else self._driver
page = self._driver.page if self._driver._type == 'ChromiumElement' else self._driver
r = page.run_cdp('Page.getLayoutMetrics')
x = r['layoutViewport']['pageX']
y = r['layoutViewport']['pageY']
@ -173,5 +173,5 @@ class FrameScroller(PageScroller):
:param center: 是否尽量滚动到页面正中为None时如果被遮挡则滚动到页面正中
:return: None
"""
ele = loc_or_ele if 'ChromiumElement' in str(type(loc_or_ele)) else self._driver._ele(loc_or_ele)
ele = loc_or_ele if loc_or_ele._type == 'ChromiumElement' else self._driver._ele(loc_or_ele)
self._to_see(ele, center)

View File

@ -103,13 +103,13 @@ class SelectElement(object):
"""
return self._select(index, 'index', False, timeout)
def by_loc(self, loc, timeout=None):
def by_locator(self, locator, timeout=None):
"""用定位符选择指定的项
:param loc: 定位符
:param locator: 定位符
:param timeout: 超时时间
:return: 是否选择成功
"""
return self._by_loc(loc, timeout)
return self._by_loc(locator, timeout)
def by_option(self, option):
"""选中单个或多个option元素
@ -142,13 +142,13 @@ class SelectElement(object):
"""
return self._select(index, 'index', True, timeout)
def cancel_by_loc(self, loc, timeout=None):
def cancel_by_locator(self, locator, timeout=None):
"""用定位符取消选择指定的项
:param loc: 定位符
:param locator: 定位符
:param timeout: 超时时间
:return: 是否选择成功
"""
return self._by_loc(loc, timeout, True)
return self._by_loc(locator, timeout, True)
def cancel_by_option(self, option):
"""取消选中单个或多个option元素

View File

@ -38,7 +38,7 @@ class SelectElement(object):
def by_index(self, index: Union[int, list, tuple], timeout: float = None) -> bool: ...
def by_loc(self, loc: Union[str, Tuple[str, str]], timeout: float = None) -> bool: ...
def by_locator(self, locator: Union[Tuple[str, str], str], timeout: float = None) -> bool: ...
def by_option(self, option: Union[ChromiumElement, List[ChromiumElement], Tuple[ChromiumElement]]) -> None: ...
@ -48,7 +48,7 @@ class SelectElement(object):
def cancel_by_index(self, index: Union[int, list, tuple], timeout: float = None) -> bool: ...
def cancel_by_loc(self, loc: Union[str, Tuple[str, str]], timeout: float = None) -> bool: ...
def cancel_by_locator(self, locator: Union[Tuple[str, str], str], timeout: float = None) -> bool: ...
def cancel_by_option(self,
option: Union[ChromiumElement, List[ChromiumElement], Tuple[ChromiumElement]]) -> None: ...

View File

@ -6,6 +6,7 @@
@License : BSD 3-Clause.
"""
from pathlib import Path
from time import sleep
from requests.structures import CaseInsensitiveDict
@ -292,13 +293,13 @@ class SessionPageSetter(BasePageSetter):
"""
self._page._headers = CaseInsensitiveDict(headers)
def header(self, attr, value):
def header(self, name, value):
"""设置headers中一个项
:param attr: 设置名称
:param name: 设置名称
:param value: 设置值
:return: None
"""
self._page._headers[attr] = value
self._page._headers[name] = value
def user_agent(self, ua):
"""设置user agent
@ -449,39 +450,46 @@ class ChromiumElementSetter(object):
"""
self._ele = ele
def attr(self, attr, value):
def attr(self, name, value):
"""设置元素attribute属性
:param attr: 属性名
:param name: 属性名
:param value: 属性值
:return: None
"""
self._ele.page.run_cdp('DOM.setAttributeValue', nodeId=self._ele._node_id, name=attr, value=str(value))
self._ele.page.run_cdp('DOM.setAttributeValue', nodeId=self._ele._node_id, name=name, value=str(value))
def prop(self, prop, value):
def property(self, name, value):
"""设置元素property属性
:param prop: 属性名
:param name: 属性名
:param value: 属性值
:return: None
"""
value = value.replace('"', r'\"')
self._ele.run_js(f'this.{prop}="{value}";')
self._ele.run_js(f'this.{name}="{value}";')
def innerHTML(self, html):
"""设置元素innerHTML
:param html: html文本
:return: None
"""
self.prop('innerHTML', html)
self.property('innerHTML', html)
def value(self, value):
"""设置元素value值
:param value: value值
:return: None
"""
self.property('value', value)
class ChromiumFrameSetter(ChromiumBaseSetter):
def attr(self, attr, value):
def attr(self, name, value):
"""设置frame元素attribute属性
:param attr: 属性名
:param name: 属性名
:param value: 属性值
:return: None
"""
self._page.frame_ele.set.attr(attr, value)
self._page.frame_ele.set.attr(name, value)
class LoadMode(object):
@ -608,7 +616,11 @@ class WindowSetter(object):
def _get_info(self):
"""获取窗口位置及大小信息"""
return self._page.run_cdp('Browser.getWindowForTarget')
for _ in range(50):
try:
return self._page.run_cdp('Browser.getWindowForTarget')
except:
sleep(.1)
def _perform(self, bounds):
"""执行改变窗口大小操作

View File

@ -113,7 +113,7 @@ class SessionPageSetter(BasePageSetter):
def headers(self, headers: dict) -> None: ...
def header(self, attr: str, value: str) -> None: ...
def header(self, name: str, value: str) -> None: ...
def user_agent(self, ua: str) -> None: ...
@ -168,17 +168,19 @@ class ChromiumElementSetter(object):
def __init__(self, ele: ChromiumElement):
self._ele: ChromiumElement = ...
def attr(self, attr: str, value: str) -> None: ...
def attr(self, name: str, value: str) -> None: ...
def prop(self, prop: str, value: str) -> None: ...
def property(self, name: str, value: str) -> None: ...
def innerHTML(self, html: str) -> None: ...
def value(self, value: str) -> None: ...
class ChromiumFrameSetter(ChromiumBaseSetter):
_page: ChromiumFrame = ...
def attr(self, attr: str, value: str) -> None: ...
def attr(self, name: str, value: str) -> None: ...
class LoadMode(object):

View File

@ -29,8 +29,9 @@ class ElementStates(object):
@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.prop('hidden'))
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):

View File

@ -73,14 +73,14 @@ class BaseWaiter(object):
return False
return ele.wait.hidden(timeout, raise_err=raise_err)
def ele_loaded(self, loc, timeout=None, raise_err=None):
def ele_loaded(self, locator, timeout=None, raise_err=None):
"""等待元素加载到DOM
:param loc: 要等待的元素输入定位符
:param locator: 要等待的元素输入定位符
:param timeout: 超时时间默认读取页面超时时间
:param raise_err: 等待失败时是否报错为None时根据Settings设置
:return: 成功返回元素对象失败返回False
"""
ele = self._driver._ele(loc, raise_err=False, timeout=timeout)
ele = self._driver._ele(locator, raise_err=False, timeout=timeout)
if ele:
return ele
if raise_err is True or Settings.raise_when_wait_failed is True:
@ -119,6 +119,8 @@ class BaseWaiter(object):
:param cancel_it: 是否取消该任务
:return: 成功返回任务对象失败返回False
"""
if not self._driver.browser._dl_mgr._running:
raise RuntimeError('使用下载管理功能前需显式设置下载路径使用set.download_path()方法、配置对象或ini文件均可')
self._driver.browser._dl_mgr.set_flag(self._driver.tab_id, False if cancel_it else True)
if timeout is None:
timeout = self._driver.timeout
@ -232,6 +234,8 @@ class TabWaiter(BaseWaiter):
:param cancel_if_timeout: 超时时是否取消剩余任务
:return: 是否等待成功
"""
if not self._driver.browser._dl_mgr._running:
raise RuntimeError('使用下载管理功能前需显式设置下载路径使用set.download_path()方法、配置对象或ini文件均可')
if not timeout:
while self._driver.browser._dl_mgr.get_tab_missions(self._driver.tab_id):
sleep(.5)
@ -290,6 +294,8 @@ class PageWaiter(TabWaiter):
:param cancel_if_timeout: 超时时是否取消剩余任务
:return: 是否等待成功
"""
if not self._driver.browser._dl_mgr._running:
raise RuntimeError('使用下载管理功能前需显式设置下载路径使用set.download_path()方法、配置对象或ini文件均可')
if not timeout:
while self._driver.browser._dl_mgr._missions:
sleep(.5)
@ -458,7 +464,8 @@ class ElementWaiter(object):
timeout = self._page.timeout
end_time = perf_counter() + timeout
while perf_counter() < end_time:
if self._ele.states.__getattribute__(attr) == mode:
a = self._ele.states.__getattribute__(attr)
if (a and mode) or (not a and not mode):
return True
sleep(.05)

View File

@ -5,7 +5,7 @@
@Copyright: (c) 2024 by g1879, Inc. All Rights Reserved.
@License : BSD 3-Clause.
"""
from typing import Union
from typing import Union, Tuple
from .downloader import DownloadMission
from .._elements.chromium_element import ChromiumElement
@ -34,7 +34,7 @@ class BaseWaiter(object):
raise_err: bool = None) -> bool: ...
def ele_loaded(self,
loc: Union[str, tuple],
locator: Union[Tuple[str, str], str],
timeout: float = None,
raise_err: bool = None) -> Union[bool, ChromiumElement]: ...

View File

@ -10,6 +10,7 @@ from ._functions.by import By
from ._functions.keys import Keys
from ._functions.settings import Settings
from ._functions.tools import wait_until, configs_to_here
from ._functions.web import get_blob
from ._units.actions import Actions
__all__ = ['make_session_ele', 'Actions', 'Keys', 'By', 'Settings', 'wait_until', 'configs_to_here']
__all__ = ['make_session_ele', 'Actions', 'Keys', 'By', 'Settings', 'wait_until', 'configs_to_here', 'get_blob']

15
DrissionPage/items.py Normal file
View File

@ -0,0 +1,15 @@
# -*- coding:utf-8 -*-
"""
@Author : g1879
@Contact : g1879@qq.com
@Copyright: (c) 2024 by g1879, Inc. All Rights Reserved.
@License : BSD 3-Clause.
"""
from ._elements.chromium_element import ChromiumElement, ShadowRoot
from ._elements.none_element import NoneElement
from ._elements.session_element import SessionElement
from ._pages.chromium_frame import ChromiumFrame
from ._pages.chromium_tab import ChromiumTab, WebPageTab
__all__ = ['ChromiumElement', 'ShadowRoot', 'NoneElement', 'SessionElement', 'ChromiumFrame', 'ChromiumTab',
'WebPageTab']

View File

@ -123,4 +123,4 @@ python 版本3.6 及以上
如果本项目对您有所帮助,不妨请作者我喝杯咖啡
![](http://g1879.gitee.io/drissionpagedocs/imgs/code.jpg)
![](https://gitee.com/g1879/DrissionPageDocs/raw/master/docs/imgs/code.jpg)

View File

@ -1,12 +1,13 @@
# -*- coding:utf-8 -*-
from setuptools import setup, find_packages
from DrissionPage import __version__
with open("README.md", "r", encoding='utf-8') as fh:
long_description = fh.read()
setup(
name="DrissionPage",
version="4.0.2",
version=__version__,
author="g1879",
author_email="g1879@qq.com",
description="Python based web automation tool. It can control the browser and send and receive data packets.",