diff --git a/DrissionPage/_base/base.py b/DrissionPage/_base/base.py index 32fb08e..c23e851 100644 --- a/DrissionPage/_base/base.py +++ b/DrissionPage/_base/base.py @@ -57,6 +57,24 @@ class BaseElement(BaseParser): self.owner = owner self._type = 'BaseElement' + def get_frame(self, loc_or_ind, timeout=None): + if not isinstance(loc_or_ind, (int, str, tuple)): + raise TypeError('loc_or_ind参数是定位符或序号。') + return get_frame(self, loc_ind_ele=loc_or_ind, timeout=timeout) + + def _ele(self, locator, timeout=None, index=1, relative=False, raise_err=None, method=None): + if hasattr(locator, '_type'): + return locator + r = self._find_elements(locator, timeout=timeout, index=index, relative=relative, raise_err=raise_err) + if r or isinstance(r, list): + return r + if Settings.raise_when_ele_not_found or raise_err is True: + raise ElementNotFoundError(None, method, {'locator': locator, 'index': index}) + + r.method = method + r.args = {'locator': locator, 'index': index} + return r + # ----------------以下属性或方法由后代实现---------------- @property def tag(self): @@ -71,78 +89,31 @@ class BaseElement(BaseParser): def nexts(self): pass - def get_frame(self, loc_or_ind, timeout=None): - """获取元素中一个frame对象 - :param loc_or_ind: 定位符、iframe序号,序号从1开始,可传入负数获取倒数第几个 - :param timeout: 查找元素超时时间(秒) - :return: ChromiumFrame对象 - """ - if not isinstance(loc_or_ind, (int, str, tuple)): - raise TypeError('loc_or_ind参数是定位符或序号。') - return get_frame(self, loc_ind_ele=loc_or_ind, timeout=timeout) - - def _ele(self, locator, timeout=None, index=1, relative=False, raise_err=None, method=None): - """调用获取元素的方法 - :param locator: 定位符 - :param timeout: 超时时间(秒) - :param index: 获取第几个,从1开始,可传入负数获取倒数第几个 - :param relative: 是否相对定位 - :param raise_err: 找不到时是否抛出异常 - :param method: 调用的方法名 - :return: 元素对象或它们组成的列表 - """ - if hasattr(locator, '_type'): - return locator - r = self._find_elements(locator, timeout=timeout, index=index, relative=relative, raise_err=raise_err) - if r or isinstance(r, list): - return r - if Settings.raise_when_ele_not_found or raise_err is True: - raise ElementNotFoundError(None, method, {'locator': locator, 'index': index}) - - r.method = method - r.args = {'locator': locator, 'index': index} - return r - class DrissionElement(BaseElement): - """ChromiumElement 和 SessionElement的基类,但不是ShadowRoot的基类""" @property def link(self): - """返回href或src绝对url""" return self.attr('href') or self.attr('src') @property def css_path(self): - """返回css path路径""" return self._get_ele_path('css') @property def xpath(self): - """返回xpath路径""" return self._get_ele_path('xpath') @property def comments(self): - """返回元素注释文本组成的列表""" return self.eles('xpath:.//comment()') def texts(self, text_node_only=False): - """返回元素内所有直接子节点的文本,包括元素和文本节点 - :param text_node_only: 是否只返回文本节点 - :return: 文本列表 - """ texts = self.eles('xpath:/text()') if text_node_only else [x if isinstance(x, str) else x.text for x in self.eles('xpath:./text() | *')] return [format_html(x.strip(' ').rstrip('\n')) for x in texts if x and sub('[\r\n\t ]', '', x) != ''] def parent(self, level_or_loc=1, index=1, timeout=None): - """返回上面某一级父元素,可指定层数或用查询语法定位 - :param level_or_loc: 第几级父元素,1开始,或定位符 - :param index: 当level_or_loc传入定位符,使用此参数选择第几个结果,1开始 - :param timeout: 时间(秒) - :return: 上级元素对象 - """ if isinstance(level_or_loc, int): loc = f'xpath:./ancestor::*[{level_or_loc}]' @@ -158,13 +129,6 @@ class DrissionElement(BaseElement): return self._ele(loc, timeout=timeout, relative=True, raise_err=False, method='parent()') def child(self, locator='', index=1, timeout=None, ele_only=True): - """返回直接子元素元素或节点组成的列表,可用查询语法筛选 - :param locator: 用于筛选的查询语法 - :param index: 第几个查询结果,1开始 - :param timeout: 查找节点的超时时间(秒) - :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 - :return: 直接子元素或节点文本组成的列表 - """ if isinstance(locator, int): index = locator locator = '' @@ -181,52 +145,18 @@ class DrissionElement(BaseElement): {'locator': locator, 'index': index, 'ele_only': ele_only}) def prev(self, locator='', index=1, timeout=None, ele_only=True): - """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 - :param locator: 用于筛选的查询语法 - :param index: 前面第几个查询结果,1开始 - :param timeout: 查找节点的超时时间(秒) - :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 - :return: 兄弟元素 - """ return self._get_relative('prev()', 'preceding', True, locator, index, timeout, ele_only) def next(self, locator='', index=1, timeout=None, ele_only=True): - """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 - :param locator: 用于筛选的查询语法 - :param index: 后面第几个查询结果,1开始 - :param timeout: 查找节点的超时时间(秒) - :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 - :return: 兄弟元素 - """ return self._get_relative('next()', 'following', True, locator, index, timeout, ele_only) def before(self, locator='', index=1, timeout=None, ele_only=True): - """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 - :param locator: 用于筛选的查询语法 - :param index: 前面第几个查询结果,1开始 - :param timeout: 查找节点的超时时间(秒) - :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 - :return: 本元素前面的某个元素或节点 - """ return self._get_relative('before()', 'preceding', False, locator, index, timeout, ele_only) def after(self, locator='', index=1, timeout=None, ele_only=True): - """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 - :param locator: 用于筛选的查询语法 - :param index: 后面第几个查询结果,1开始 - :param timeout: 查找节点的超时时间(秒) - :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 - :return: 本元素后面的某个元素或节点 - """ return self._get_relative('after()', 'following', False, locator, index, timeout, ele_only) def children(self, locator='', timeout=None, ele_only=True): - """返回直接子元素元素或节点组成的列表,可用查询语法筛选 - :param locator: 用于筛选的查询语法 - :param timeout: 查找节点的超时时间(秒) - :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 - :return: 直接子元素或节点文本组成的列表 - """ if not locator: loc = '*' if ele_only else 'node()' else: @@ -240,53 +170,20 @@ class DrissionElement(BaseElement): return [e for e in nodes if not (isinstance(e, str) and sub('[ \n\t\r]', '', e) == '')] def prevs(self, locator='', timeout=None, ele_only=True): - """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 - :param locator: 用于筛选的查询语法 - :param timeout: 查找节点的超时时间(秒) - :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 - :return: 兄弟元素或节点文本组成的列表 - """ return self._get_relatives(locator=locator, direction='preceding', timeout=timeout, ele_only=ele_only) def nexts(self, locator='', timeout=None, ele_only=True): - """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 - :param locator: 用于筛选的查询语法 - :param timeout: 查找节点的超时时间(秒) - :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 - :return: 兄弟元素或节点文本组成的列表 - """ return self._get_relatives(locator=locator, direction='following', timeout=timeout, ele_only=ele_only) def befores(self, locator='', timeout=None, ele_only=True): - """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 - :param locator: 用于筛选的查询语法 - :param timeout: 查找节点的超时时间(秒) - :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 - :return: 本元素前面的元素或节点组成的列表 - """ return self._get_relatives(locator=locator, direction='preceding', brother=False, timeout=timeout, ele_only=ele_only) def afters(self, locator='', timeout=None, ele_only=True): - """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 - :param locator: 用于筛选的查询语法 - :param timeout: 查找节点的超时时间(秒) - :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 - :return: 本元素后面的元素或节点组成的列表 - """ return self._get_relatives(locator=locator, direction='following', brother=False, timeout=timeout, ele_only=ele_only) def _get_relative(self, func, direction, brother, locator='', index=1, timeout=None, ele_only=True): - """获取一个亲戚元素或节点,可用查询语法筛选,可指定返回筛选结果的第几个 - :param func: 方法名称 - :param direction: 方向,'following' 或 'preceding' - :param locator: 用于筛选的查询语法 - :param index: 前面第几个查询结果,1开始 - :param timeout: 查找节点的超时时间(秒) - :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 - :return: 本元素前面的某个元素或节点 - """ if isinstance(locator, int): index = locator locator = '' @@ -295,14 +192,6 @@ class DrissionElement(BaseElement): {'locator': locator, 'index': index, 'ele_only': ele_only}) def _get_relatives(self, index=None, locator='', direction='following', brother=True, timeout=.5, ele_only=True): - """按要求返回兄弟元素或节点组成的列表 - :param index: 获取第几个,该参数不为None时只获取该编号的元素 - :param locator: 用于筛选的查询语法 - :param direction: 'following' 或 'preceding',查找的方向 - :param brother: 查找范围,在同级查找还是整个dom前后查找 - :param timeout: 查找等待时间(秒) - :return: 元素对象或字符串 - """ brother = '-sibling' if brother else '' if not locator: @@ -348,10 +237,8 @@ class DrissionElement(BaseElement): class BasePage(BaseParser): - """页面类的基类""" def __init__(self): - """初始化函数""" self._url = None self._url_available = None self.retry_times = 3 @@ -364,34 +251,24 @@ class BasePage(BaseParser): @property def title(self): - """返回网页title""" ele = self._ele('xpath://title', raise_err=False, method='title') return ele.text if ele else None @property def url_available(self): - """返回当前访问的url有效性""" return self._url_available @property def download_path(self): - """返回默认下载路径""" return self._download_path @property def download(self): - """返回下载器对象""" if self._DownloadKit is None: self._DownloadKit = DownloadKit(driver=self, goal_path=self.download_path) return self._DownloadKit def _before_connect(self, url, retry, interval): - """连接前的准备 - :param url: 要访问的url - :param retry: 重试次数 - :param interval: 重试间隔 - :return: 重试次数、间隔、是否文件组成的tuple - """ is_file = False if isinstance(url, Path) or ('://' not in url and ':\\\\' not in url): p = Path(url) @@ -422,14 +299,6 @@ class BasePage(BaseParser): pass def _ele(self, locator, timeout=None, index=1, raise_err=None, method=None): - """调用获取元素的方法 - :param locator: 定位符 - :param timeout: 超时时间(秒) - :param index: 获取第几个,从1开始,可传入负数获取倒数第几个 - :param raise_err: 找不到时是否抛出异常 - :param method: 调用的方法名 - :return: 元素对象或它们组成的列表 - """ if not locator: raise ElementNotFoundError(None, method, {'locator': locator}) diff --git a/DrissionPage/_base/base.pyi b/DrissionPage/_base/base.pyi index 05eb3e1..a509972 100644 --- a/DrissionPage/_base/base.pyi +++ b/DrissionPage/_base/base.pyi @@ -57,22 +57,14 @@ class BaseParser(object): class BaseElement(BaseParser): + owner: BasePage = ... - def __init__(self, owner: BasePage = None): - self.owner: BasePage = ... + def __init__(self, owner: BasePage = None): ... # ----------------以下属性或方法由后代实现---------------- @property def tag(self) -> str: ... - def _ele(self, - locator: Union[Tuple[str, str], str], - timeout: float = None, - index: Optional[int] = 1, - relative: bool = False, - raise_err: bool = None, - method: str = None): ... - def parent(self, level_or_loc: Union[tuple, str, int] = 1): ... def prev(self, index: int = 1) -> None: ... @@ -83,86 +75,206 @@ class BaseElement(BaseParser): def nexts(self): ... - def get_frame(self, loc_or_ind, timeout=None) -> ChromiumFrame: ... + def get_frame(self, loc_or_ind, timeout=None) -> ChromiumFrame: + """获取元素中一个frame对象 + :param loc_or_ind: 定位符、iframe序号,序号从1开始,可传入负数获取倒数第几个 + :param timeout: 查找元素超时时间(秒) + :return: ChromiumFrame对象 + """ + ... + + def _ele(self, + locator: Union[Tuple[str, str], str], + timeout: float = None, + index: Optional[int] = 1, + relative: bool = False, + raise_err: bool = None, + method: str = None): + """调用获取元素的方法 + :param locator: 定位符 + :param timeout: 超时时间(秒) + :param index: 获取第几个,从1开始,可传入负数获取倒数第几个 + :param relative: 是否相对定位 + :param raise_err: 找不到时是否抛出异常 + :param method: 调用的方法名 + :return: 元素对象或它们组成的列表 + """ + ... class DrissionElement(BaseElement): + """ChromiumElement 和 SessionElement的基类,但不是ShadowRoot的基类""" def __init__(self, owner: BasePage = None): ... @property - def link(self) -> str: ... + def link(self) -> str: + """返回href或src绝对url""" + ... @property - def css_path(self) -> str: ... + def css_path(self) -> str: + """返回css path路径""" + ... @property - def xpath(self) -> str: ... + def xpath(self) -> str: + """返回xpath路径""" + ... @property - def comments(self) -> list: ... + def comments(self) -> list: + """返回元素注释文本组成的列表""" + ... - def texts(self, text_node_only: bool = False) -> list: ... + def texts(self, text_node_only: bool = False) -> list: + """返回元素内所有直接子节点的文本,包括元素和文本节点 + :param text_node_only: 是否只返回文本节点 + :return: 文本列表 + """ + ... def parent(self, level_or_loc: Union[tuple, str, int] = 1, index: int = 1, - timeout: float = None) -> Union[DrissionElement, None]: ... + timeout: float = None) -> Union[DrissionElement, None]: + """返回上面某一级父元素,可指定层数或用查询语法定位 + :param level_or_loc: 第几级父元素,1开始,或定位符 + :param index: 当level_or_loc传入定位符,使用此参数选择第几个结果,1开始 + :param timeout: 时间(秒) + :return: 上级元素对象 + """ + ... def child(self, locator: Union[Tuple[str, str], str, int] = '', index: int = 1, timeout: float = None, - ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]: ... + ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]: + """返回直接子元素元素或节点组成的列表,可用查询语法筛选 + :param locator: 用于筛选的查询语法 + :param index: 第几个查询结果,1开始 + :param timeout: 查找节点的超时时间(秒) + :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 + :return: 直接子元素或节点文本组成的列表 + """ + ... def prev(self, locator: Union[Tuple[str, str], str, int] = '', index: int = 1, timeout: float = None, - ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]: ... + ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]: + """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 + :param locator: 用于筛选的查询语法 + :param index: 前面第几个查询结果,1开始 + :param timeout: 查找节点的超时时间(秒) + :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 + :return: 兄弟元素 + """ + ... def next(self, locator: Union[Tuple[str, str], str, int] = '', index: int = 1, timeout: float = None, - ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]: ... + ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]: + """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 + :param locator: 用于筛选的查询语法 + :param index: 后面第几个查询结果,1开始 + :param timeout: 查找节点的超时时间(秒) + :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 + :return: 兄弟元素 + """ + ... def before(self, locator: Union[Tuple[str, str], str, int] = '', index: int = 1, timeout: float = None, - ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]: ... + ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]: + """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 + :param locator: 用于筛选的查询语法 + :param index: 前面第几个查询结果,1开始 + :param timeout: 查找节点的超时时间(秒) + :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 + :return: 本元素前面的某个元素或节点 + """ + ... def after(self, locator: Union[Tuple[str, str], str, int] = '', index: int = 1, timeout: float = None, - ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]: ... + ele_only: bool = True) -> Union[DrissionElement, str, NoneElement]: + """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 + :param locator: 用于筛选的查询语法 + :param index: 后面第几个查询结果,1开始 + :param timeout: 查找节点的超时时间(秒) + :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 + :return: 本元素后面的某个元素或节点 + """ + ... def children(self, locator: Union[Tuple[str, str], str] = '', timeout: float = None, - ele_only: bool = True) -> List[Union[DrissionElement, str]]: ... + ele_only: bool = True) -> List[Union[DrissionElement, str]]: + """返回直接子元素元素或节点组成的列表,可用查询语法筛选 + :param locator: 用于筛选的查询语法 + :param timeout: 查找节点的超时时间(秒) + :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 + :return: 直接子元素或节点文本组成的列表 + """ + ... def prevs(self, locator: Union[Tuple[str, str], str] = '', timeout: float = None, - ele_only: bool = True) -> List[Union[DrissionElement, str]]: ... + ele_only: bool = True) -> List[Union[DrissionElement, str]]: + """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 + :param locator: 用于筛选的查询语法 + :param timeout: 查找节点的超时时间(秒) + :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 + :return: 兄弟元素或节点文本组成的列表 + """ + ... def nexts(self, locator: Union[Tuple[str, str], str] = '', timeout: float = None, - ele_only: bool = True) -> List[Union[DrissionElement, str]]: ... + ele_only: bool = True) -> List[Union[DrissionElement, str]]: + """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 + :param locator: 用于筛选的查询语法 + :param timeout: 查找节点的超时时间(秒) + :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 + :return: 兄弟元素或节点文本组成的列表 + """ + ... def befores(self, locator: Union[Tuple[str, str], str] = '', timeout: float = None, - ele_only: bool = True) -> List[Union[DrissionElement, str]]: ... + ele_only: bool = True) -> List[Union[DrissionElement, str]]: + """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 + :param locator: 用于筛选的查询语法 + :param timeout: 查找节点的超时时间(秒) + :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 + :return: 本元素前面的元素或节点组成的列表 + """ + ... def afters(self, locator: Union[Tuple[str, str], str] = '', timeout: float = None, - ele_only: bool = True) -> List[Union[DrissionElement, str]]: ... + ele_only: bool = True) -> List[Union[DrissionElement, str]]: + """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 + :param locator: 用于筛选的查询语法 + :param timeout: 查找节点的超时时间(秒) + :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 + :return: 本元素后面的元素或节点组成的列表 + """ + ... def _get_relative(self, func: str, @@ -171,7 +283,17 @@ class DrissionElement(BaseElement): locator: Union[Tuple[str, str], str] = '', index: int = 1, timeout: float = None, - ele_only: bool = True) -> DrissionElement: ... + ele_only: bool = True) -> DrissionElement: + """获取一个亲戚元素或节点,可用查询语法筛选,可指定返回筛选结果的第几个 + :param func: 方法名称 + :param direction: 方向,'following' 或 'preceding' + :param locator: 用于筛选的查询语法 + :param index: 前面第几个查询结果,1开始 + :param timeout: 查找节点的超时时间(秒) + :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 + :return: 本元素前面的某个元素或节点 + """ + ... def _get_relatives(self, index: int = None, @@ -179,7 +301,16 @@ class DrissionElement(BaseElement): direction: str = 'following', brother: bool = True, timeout: float = 0.5, - ele_only: bool = True) -> List[Union[DrissionElement, str]]: ... + ele_only: bool = True) -> List[Union[DrissionElement, str]]: + """按要求返回兄弟元素或节点组成的列表 + :param index: 获取第几个,该参数不为None时只获取该编号的元素 + :param locator: 用于筛选的查询语法 + :param direction: 'following' 或 'preceding',查找的方向 + :param brother: 查找范围,在同级查找还是整个dom前后查找 + :param timeout: 查找等待时间(秒) + :return: 元素对象或字符串 + """ + ... # ----------------以下属性或方法由后代实现---------------- @property @@ -198,30 +329,47 @@ class DrissionElement(BaseElement): class BasePage(BaseParser): + """页面类的基类""" - def __init__(self): - self._url_available: bool = ... - self.retry_times: int = ... - self.retry_interval: float = ... - self._download_path: str = ... - self._DownloadKit: DownloadKit = ... - self._none_ele_return_value: bool = ... - self._none_ele_value: Any = ... - self._page: Union[ChromiumPage, SessionPage, MixPage] = ... + _url_available: Optional[bool] = ... + retry_times: int = ... + retry_interval: float = ... + _download_path: Optional[str] = ... + _DownloadKit: Optional[DownloadKit] = ... + _none_ele_return_value: bool = ... + _none_ele_value: Any = ... + _page: Union[ChromiumPage, SessionPage, MixPage] = ... + + def __init__(self): ... @property - def title(self) -> Union[str, None]: ... + def title(self) -> Union[str, None]: + """返回网页title""" + ... @property - def url_available(self) -> bool: ... + def url_available(self) -> bool: + """返回当前访问的url有效性""" + ... @property - def download_path(self) -> str: ... + def download_path(self) -> str: + """返回默认下载路径""" + ... @property - def download(self) -> DownloadKit: ... + def download(self) -> DownloadKit: + """返回下载器对象""" + ... - def _before_connect(self, url: str, retry: int, interval: float) -> tuple: ... + def _before_connect(self, url: str, retry: int, interval: float) -> tuple: + """连接前的准备 + :param url: 要访问的url + :param retry: 重试次数 + :param interval: 重试间隔 + :return: 重试次数、间隔、是否文件组成的tuple + """ + ... # ----------------以下属性或方法由后代实现---------------- @property @@ -241,4 +389,13 @@ class BasePage(BaseParser): timeout: float = None, index: Optional[int] = 1, raise_err: bool = None, - method: str = None): ... + method: str = None): + """调用获取元素的方法 + :param locator: 定位符 + :param timeout: 超时时间(秒) + :param index: 获取第几个,从1开始,可传入负数获取倒数第几个 + :param raise_err: 找不到时是否抛出异常 + :param method: 调用的方法名 + :return: 元素对象或它们组成的列表 + """ + ... diff --git a/DrissionPage/_base/browser.py b/DrissionPage/_base/browser.py index b3be05e..ce65d36 100644 --- a/DrissionPage/_base/browser.py +++ b/DrissionPage/_base/browser.py @@ -37,10 +37,6 @@ class Chromium(object): _lock = Lock() def __new__(cls, addr_or_opts=None, session_options=None): - """ - :param addr_or_opts: 浏览器地址:端口、ChromiumOptions对象或端口数字(int) - :param session_options: 使用双模Tab时使用的默认Session配置,为True使用ini文件配置 - """ opt = handle_options(addr_or_opts) is_headless, browser_id, is_exists = run_browser(opt) with cls._lock: @@ -51,17 +47,13 @@ class Chromium(object): return r r = object.__new__(cls) r._chromium_options = opt - r.is_headless = is_headless + r._is_headless = is_headless r._is_exists = is_exists r.id = browser_id cls._BROWSERS[browser_id] = r return r def __init__(self, addr_or_opts=None, session_options=None): - """ - :param addr_or_opts: 浏览器地址:端口、ChromiumOptions对象或端口数字(int) - :param session_options: 使用双模Tab时使用的默认Session配置,为True使用ini文件配置 - """ if hasattr(self, '_created'): return self._created = True @@ -81,7 +73,7 @@ class Chromium(object): self.address = self._chromium_options.address self._driver = BrowserDriver(self.id, 'browser', self.address, self) - if (not self._chromium_options._ua_set and self.is_headless != self._chromium_options.is_headless) or ( + if (not self._chromium_options._ua_set and self._is_headless != self._chromium_options.is_headless) or ( self._is_exists and self._chromium_options._new_env): self.quit(3, True) connect_browser(self._chromium_options) @@ -118,32 +110,26 @@ class Chromium(object): @property def user_data_path(self): - """返回用户文件夹路径""" return self._chromium_options.user_data_path @property def process_id(self): - """返回浏览器进程id""" return self._process_id @property def timeout(self): - """返回timeouts设置""" return self._timeouts.base @property def timeouts(self): - """返回timeouts设置""" return self._timeouts @property def load_mode(self): - """返回加载模式""" return self._load_mode @property def download_path(self): - """返回默认下载路径""" return self._download_path @property @@ -154,69 +140,38 @@ class Chromium(object): @property def wait(self): - """返回用于等待的对象""" if self._wait is None: self._wait = BrowserWaiter(self) return self._wait @property def tabs_count(self): - """返回标签页数量""" j = self._run_cdp('Target.getTargets')['targetInfos'] # 不要改用get,避免卡死 return len([i for i in j if i['type'] in ('page', 'webview') and not i['url'].startswith('devtools://')]) @property def tab_ids(self): - """返回所有标签页id组成的列表""" j = self._driver.get(f'http://{self.address}/json').json() # 不要改用cdp,因为顺序不对 return [i['id'] for i in j if i['type'] in ('page', 'webview') and not i['url'].startswith('devtools://')] @property def latest_tab(self): - """返回最新的标签页,最新标签页指最后创建或最后被激活的 - 当Settings.singleton_tab_obj==True时返回Tab对象,否则返回tab id""" return self.get_tab(self.tab_ids[0], as_id=not Settings.singleton_tab_obj) def cookies(self, all_info=False): - """以list格式返回所有域名的cookies - :param all_info: 是否返回所有内容,False则只返回name, value, domain - :return: cookies组成的列表 - """ cks = self._run_cdp(f'Storage.getCookies')['cookies'] r = cks if all_info else [{'name': c['name'], 'value': c['value'], 'domain': c['domain']} for c in cks] return CookiesList(r) def new_tab(self, url=None, new_window=False, background=False, new_context=False): - """新建一个标签页 - :param url: 新标签页跳转到的网址 - :param new_window: 是否在新窗口打开标签页 - :param background: 是否不激活新标签页,如new_window为True则无效 - :param new_context: 是否创建新的上下文 - :return: 新标签页对象 - """ return self._new_tab(ChromiumTab, url=url, new_window=new_window, background=background, new_context=new_context) def new_mix_tab(self, url=None, new_window=False, background=False, new_context=False): - """新建一个标签页 - :param url: 新标签页跳转到的网址 - :param new_window: 是否在新窗口打开标签页 - :param background: 是否不激活新标签页,如new_window为True则无效 - :param new_context: 是否创建新的上下文 - :return: 新标签页对象 - """ return self._new_tab(MixTab, url=url, new_window=new_window, background=background, new_context=new_context) def _new_tab(self, obj, url=None, new_window=False, background=False, new_context=False): - """新建一个标签页 - :param obj: 要创建的Tab类型 - :param url: 新标签页跳转到的网址 - :param new_window: 是否在新窗口打开标签页 - :param background: 是否不激活新标签页,如new_window为True则无效 - :param new_context: 是否创建新的上下文 - :return: 新标签页对象 - """ tab = None if new_context: tab = self._run_cdp('Target.createBrowserContext')['browserContextId'] @@ -244,57 +199,18 @@ class Chromium(object): return tab def get_tab(self, id_or_num=None, title=None, url=None, tab_type='page', as_id=False): - """获取一个标签页对象,id_or_num不为None时,后面几个参数无效 - :param id_or_num: 要获取的标签页id或序号,序号从1开始,可传入负数获取倒数第几个,不是视觉排列顺序,而是激活顺序 - :param title: 要匹配title的文本,模糊匹配,为None则匹配所有 - :param url: 要匹配url的文本,模糊匹配,为None则匹配所有 - :param tab_type: tab类型,可用列表输入多个,如 'page', 'iframe' 等,为None则匹配所有 - :param as_id: 是否返回标签页id而不是标签页对象 - :return: Tab对象 - """ return self._get_tab(id_or_num=id_or_num, title=title, url=url, tab_type=tab_type, as_id=as_id) def get_tabs(self, title=None, url=None, tab_type='page', as_id=False): - """查找符合条件的tab,返回它们组成的列表,title和url是与关系 - :param title: 要匹配title的文本 - :param url: 要匹配url的文本 - :param tab_type: tab类型,可用列表输入多个 - :param as_id: 是否返回标签页id而不是标签页对象 - :return: Tab对象列表 - """ return self._get_tabs(title=title, url=url, tab_type=tab_type, as_id=as_id) def get_mix_tab(self, id_or_num=None, title=None, url=None, tab_type='page', as_id=False): - """获取一个标签页对象,id_or_num不为None时,后面几个参数无效 - :param id_or_num: 要获取的标签页id或序号,序号从1开始,可传入负数获取倒数第几个,不是视觉排列顺序,而是激活顺序 - :param title: 要匹配title的文本,模糊匹配,为None则匹配所有 - :param url: 要匹配url的文本,模糊匹配,为None则匹配所有 - :param tab_type: tab类型,可用列表输入多个,如 'page', 'iframe' 等,为None则匹配所有 - :param as_id: 是否返回标签页id而不是标签页对象 - :return: Tab对象 - """ return self._get_tab(id_or_num=id_or_num, title=title, url=url, tab_type=tab_type, mix=True, as_id=as_id) def get_mix_tabs(self, title=None, url=None, tab_type='page', as_id=False): - """查找符合条件的tab,返回它们组成的列表,title和url是与关系 - :param title: 要匹配title的文本 - :param url: 要匹配url的文本 - :param tab_type: tab类型,可用列表输入多个 - :param as_id: 是否返回标签页id而不是标签页对象 - :return: Tab对象列表 - """ return self._get_tabs(title=title, url=url, tab_type=tab_type, mix=True, as_id=as_id) def _get_tab(self, id_or_num=None, title=None, url=None, tab_type='page', mix=False, as_id=False): - """获取一个标签页对象,id_or_num不为None时,后面几个参数无效 - :param id_or_num: 要获取的标签页id或序号,序号从1开始,可传入负数获取倒数第几个,不是视觉排列顺序,而是激活顺序 - :param title: 要匹配title的文本,模糊匹配,为None则匹配所有 - :param url: 要匹配url的文本,模糊匹配,为None则匹配所有 - :param tab_type: tab类型,可用列表输入多个,如 'page', 'iframe' 等,为None则匹配所有 - :param mix: 是否返回可切换模式的Tab对象 - :param as_id: 是否返回标签页id而不是标签页对象,mix=False时无效 - :return: Tab对象 - """ if id_or_num is not None: if isinstance(id_or_num, str): id_or_num = id_or_num @@ -319,14 +235,6 @@ class Chromium(object): return MixTab(self, id_or_num) if mix else ChromiumTab(self, id_or_num) def _get_tabs(self, title=None, url=None, tab_type='page', mix=False, as_id=False): - """查找符合条件的tab,返回它们组成的列表,title和url是与关系 - :param title: 要匹配title的文本 - :param url: 要匹配url的文本 - :param tab_type: tab类型,可用列表输入多个 - :param mix: 是否返回可切换模式的Tab对象 - :param as_id: 是否返回标签页id而不是标签页对象,mix=False时无效 - :return: Tab对象列表 - """ tabs = self._driver.get(f'http://{self.address}/json').json() # 不要改用cdp if isinstance(tab_type, str): @@ -347,11 +255,6 @@ class Chromium(object): return [ChromiumTab(self, tab['id']) for tab in tabs] def close_tabs(self, tabs_or_ids=None, others=False): - """关闭传入的标签页,默认关闭当前页。可传入多个 - :param tabs_or_ids: 要关闭的标签页对象或id,可传入列表或元组,为None时关闭最后操作的 - :param others: 是否关闭指定标签页之外的 - :return: None - """ all_tabs = set(self.tab_ids) if isinstance(tabs_or_ids, str): tabs = {tabs_or_ids} @@ -381,10 +284,6 @@ class Chromium(object): sleep(.1) def activate_tab(self, id_ind_tab): - """使标签页变为活动状态 - :param id_ind_tab: 标签页id(str)、Tab对象或标签页序号(int),序号从1开始 - :return: None - """ if isinstance(id_ind_tab, int): id_ind_tab += -1 if id_ind_tab else 1 id_ind_tab = self.tab_ids[id_ind_tab] @@ -393,7 +292,6 @@ class Chromium(object): self._run_cdp('Target.activateTarget', targetId=id_ind_tab) def reconnect(self): - """断开重连""" self._driver.stop() BrowserDriver.BROWSERS.pop(self.id) self._driver = BrowserDriver(self.id, 'browser', self.address, self) @@ -402,12 +300,6 @@ class Chromium(object): self._driver.set_callback('Target.targetCreated', self._onTargetCreated) def quit(self, timeout=5, force=False, del_data=False): - """关闭浏览器 - :param timeout: 等待浏览器关闭超时时间(秒) - :param force: 是否立刻强制终止进程 - :param del_data: 是否删除用户文件夹 - :return: None - """ try: self._run_cdp('Browser.close') except PageDisconnectedError: @@ -457,12 +349,12 @@ class Chromium(object): path = Path(self._chromium_options.user_data_path) rmtree(path, True) + def _run_cdp(self, cmd, **cmd_args): + ignore = cmd_args.pop('_ignore', None) + r = self._driver.run(cmd, **cmd_args) + return r if __ERROR__ not in r else raise_error(r, ignore) + def _get_driver(self, tab_id, owner=None): - """新建并返回指定tab id的Driver - :param tab_id: 标签页id - :param owner: 使用该驱动的对象 - :return: Driver对象 - """ d = self._drivers.pop(tab_id, None) if not d: d = Driver(tab_id, 'page', self.address) @@ -471,7 +363,6 @@ class Chromium(object): return d def _onTargetCreated(self, **kwargs): - """标签页创建时执行""" if (kwargs['targetInfo']['type'] in ('page', 'webview') and kwargs['targetInfo']['targetId'] not in self._all_drivers and not kwargs['targetInfo']['url'].startswith('devtools://')): @@ -484,7 +375,6 @@ class Chromium(object): pass def _onTargetDestroyed(self, **kwargs): - """标签页关闭时执行""" tab_id = kwargs['targetId'] self._dl_mgr.clear_tab_info(tab_id) for key in [k for k, i in self._frames.items() if i == tab_id]: @@ -494,16 +384,6 @@ class Chromium(object): self._drivers.pop(tab_id, None) self._all_drivers.pop(tab_id, None) - def _run_cdp(self, cmd, **cmd_args): - """执行Chrome DevTools Protocol语句 - :param cmd: 协议项目 - :param cmd_args: 参数 - :return: 执行的结果 - """ - ignore = cmd_args.pop('_ignore', None) - r = self._driver.run(cmd, **cmd_args) - return r if __ERROR__ not in r else raise_error(r, ignore) - def _on_disconnect(self): Chromium._BROWSERS.pop(self.id, None) if self._chromium_options.is_auto_port and self._chromium_options.user_data_path: diff --git a/DrissionPage/_base/browser.pyi b/DrissionPage/_base/browser.pyi index 9bd107c..ed5305c 100644 --- a/DrissionPage/_base/browser.pyi +++ b/DrissionPage/_base/browser.pyi @@ -25,7 +25,6 @@ class Chromium(object): version: str = ... retry_times: int = ... retry_interval: float = ... - is_headless: bool = ... _BROWSERS: dict = ... _chromium_options: ChromiumOptions = ... @@ -44,81 +43,191 @@ class Chromium(object): _load_mode: str = ... _download_path: str = ... _is_exists: bool = ... + _is_headless: bool = ... def __new__(cls, addr_or_opts: Union[str, int, ChromiumOptions] = None, - session_options: Optional[SessionOptions] = None): ... + session_options: Optional[SessionOptions] = None): + """ + :param addr_or_opts: 浏览器地址:端口、ChromiumOptions对象或端口数字(int) + :param session_options: 使用双模Tab时使用的默认Session配置,为True使用ini文件配置 + """ + ... def __init__(self, addr_or_opts: Union[str, int, ChromiumOptions] = None, - session_options: Optional[SessionOptions] = None): ... - - def _get_driver(self, tab_id: str, owner=None) -> Driver: ... - - def _run_cdp(self, cmd, **cmd_args) -> dict: ... + session_options: Optional[SessionOptions] = None): + """ + :param addr_or_opts: 浏览器地址:端口、ChromiumOptions对象或端口数字(int) + :param session_options: 使用双模Tab时使用的默认Session配置,为True使用ini文件配置 + """ + ... @property - def user_data_path(self) -> str: ... + def user_data_path(self) -> str: + """返回用户文件夹路径""" + ... @property - def process_id(self) -> Optional[int]: ... + def process_id(self) -> Optional[int]: + """返回浏览器进程id""" + ... @property - def timeout(self) -> float: ... + def timeout(self) -> float: + """返回基础超时设置""" + ... @property - def timeouts(self) -> Timeout: ... + def timeouts(self) -> Timeout: + """返回所有超时设置""" + ... @property - def load_mode(self) -> str: ... + def load_mode(self) -> str: + """返回加载模式""" + ... @property - def download_path(self) -> str: ... + def download_path(self) -> str: + """返回默认下载路径""" + ... @property - def set(self) -> BrowserSetter: ... + def set(self) -> BrowserSetter: + """返回用于设置的对象""" + ... @property - def wait(self) -> BrowserWaiter: ... + def wait(self) -> BrowserWaiter: + """返回用于等待的对象""" + ... @property - def tabs_count(self) -> int: ... + def tabs_count(self) -> int: + """返回标签页数量""" + ... @property - def tab_ids(self) -> List[str]: ... + def tab_ids(self) -> List[str]: + """返回所有标签页id组成的列表""" + ... @property - def latest_tab(self) -> Union[ChromiumTab, str]: ... + def latest_tab(self) -> Union[ChromiumTab, str]: + """返回最新的标签页,最新标签页指最后创建或最后被激活的 + 当Settings.singleton_tab_obj==True时返回Tab对象,否则返回tab id""" + ... - def cookies(self, all_info: bool = False) -> CookiesList: ... + def cookies(self, all_info: bool = False) -> CookiesList: + """以list格式返回所有域名的cookies + :param all_info: 是否返回所有内容,False则只返回name, value, domain + :return: cookies组成的列表 + """ + ... - def close_tabs(self, - tabs_or_ids: Union[str, ChromiumTab, List[Union[str, ChromiumTab]], - Tuple[Union[str, ChromiumTab]]] = None, - others: bool = False) -> None: ... + def new_tab(self, + url: str = None, + new_window: bool = False, + background: bool = False, + new_context: bool = False) -> ChromiumTab: + """新建一个标签页 + :param url: 新标签页跳转到的网址 + :param new_window: 是否在新窗口打开标签页 + :param background: 是否不激活新标签页,如new_window为True则无效 + :param new_context: 是否创建新的上下文 + :return: 新标签页对象 + """ + ... + + def new_mix_tab(self, + url: str = None, + new_window: bool = False, + background: bool = False, + new_context: bool = False) -> MixTab: + """新建一个标签页 + :param url: 新标签页跳转到的网址 + :param new_window: 是否在新窗口打开标签页 + :param background: 是否不激活新标签页,如new_window为True则无效 + :param new_context: 是否创建新的上下文 + :return: 新标签页对象 + """ + ... + + def _new_tab(self, + obj, + url: str = None, + new_window: bool = False, + background: bool = False, + new_context: bool = False) -> Union[ChromiumTab, MixTab]: + """新建一个标签页 + :param obj: 要创建的Tab类型 + :param url: 新标签页跳转到的网址 + :param new_window: 是否在新窗口打开标签页 + :param background: 是否不激活新标签页,如new_window为True则无效 + :param new_context: 是否创建新的上下文 + :return: 新标签页对象 + """ + ... def get_tab(self, id_or_num: Union[str, int] = None, title: str = None, url: str = None, tab_type: str = 'page', - as_id: bool = False) -> Union[ChromiumTab, str]: ... + as_id: bool = False) -> Union[ChromiumTab, str]: + """获取一个标签页对象,id_or_num不为None时,后面几个参数无效 + :param id_or_num: 要获取的标签页id或序号,序号从1开始,可传入负数获取倒数第几个,不是视觉排列顺序,而是激活顺序 + :param title: 要匹配title的文本,模糊匹配,为None则匹配所有 + :param url: 要匹配url的文本,模糊匹配,为None则匹配所有 + :param tab_type: tab类型,可用列表输入多个,如 'page', 'iframe' 等,为None则匹配所有 + :param as_id: 是否返回标签页id而不是标签页对象 + :return: Tab对象 + """ + ... def get_tabs(self, title: str = None, url: str = None, tab_type: str = 'page', - as_id: bool = False) -> List[ChromiumTab, str]: ... + as_id: bool = False) -> List[ChromiumTab, str]: + """查找符合条件的tab,返回它们组成的列表,title和url是与关系 + :param title: 要匹配title的文本 + :param url: 要匹配url的文本 + :param tab_type: tab类型,可用列表输入多个 + :param as_id: 是否返回标签页id而不是标签页对象 + :return: Tab对象列表 + """ + ... def get_mix_tab(self, id_or_num: Union[str, int] = None, title: str = None, url: str = None, - tab_type: str = 'page') -> Union[MixTab, str]: ... + tab_type: str = 'page', + as_id: bool = False) -> Union[MixTab, str]: + """获取一个标签页对象,id_or_num不为None时,后面几个参数无效 + :param id_or_num: 要获取的标签页id或序号,序号从1开始,可传入负数获取倒数第几个,不是视觉排列顺序,而是激活顺序 + :param title: 要匹配title的文本,模糊匹配,为None则匹配所有 + :param url: 要匹配url的文本,模糊匹配,为None则匹配所有 + :param tab_type: tab类型,可用列表输入多个,如 'page', 'iframe' 等,为None则匹配所有 + :param as_id: 是否返回标签页id而不是标签页对象 + :return: Tab对象 + """ + ... def get_mix_tabs(self, title: str = None, url: str = None, - tab_type: str = 'page') -> List[MixTab, str]: ... + tab_type: str = 'page', + as_id: bool = False) -> List[MixTab, str]: + """查找符合条件的tab,返回它们组成的列表,title和url是与关系 + :param title: 要匹配title的文本 + :param url: 要匹配url的文本 + :param tab_type: tab类型,可用列表输入多个 + :param as_id: 是否返回标签页id而不是标签页对象 + :return: Tab对象列表 + """ + ... def _get_tab(self, id_or_num: Union[str, int] = None, @@ -126,42 +235,83 @@ class Chromium(object): url: str = None, tab_type: str = 'page', mix: bool = False, - as_id: bool = False) -> Union[ChromiumTab, str]: ... + as_id: bool = False) -> Union[ChromiumTab, str]: + """获取一个标签页对象,id_or_num不为None时,后面几个参数无效 + :param id_or_num: 要获取的标签页id或序号,序号从1开始,可传入负数获取倒数第几个,不是视觉排列顺序,而是激活顺序 + :param title: 要匹配title的文本,模糊匹配,为None则匹配所有 + :param url: 要匹配url的文本,模糊匹配,为None则匹配所有 + :param tab_type: tab类型,可用列表输入多个,如 'page', 'iframe' 等,为None则匹配所有 + :param mix: 是否返回可切换模式的Tab对象 + :param as_id: 是否返回标签页id而不是标签页对象,mix=False时无效 + :return: Tab对象 + """ + ... def _get_tabs(self, title: str = None, url: str = None, tab_type: str = 'page', mix: bool = False, - as_id: bool = False) -> List[ChromiumTab, str]: ... + as_id: bool = False) -> List[ChromiumTab, str]: + """查找符合条件的tab,返回它们组成的列表,title和url是与关系 + :param title: 要匹配title的文本 + :param url: 要匹配url的文本 + :param tab_type: tab类型,可用列表输入多个 + :param mix: 是否返回可切换模式的Tab对象 + :param as_id: 是否返回标签页id而不是标签页对象,mix=False时无效 + :return: Tab对象列表 + """ + ... - def activate_tab(self, id_ind_tab: Union[int, str, ChromiumTab]) -> None: ... + def close_tabs(self, + tabs_or_ids: Union[str, ChromiumTab, List[Union[str, ChromiumTab]], + Tuple[Union[str, ChromiumTab]]] = None, + others: bool = False) -> None: + """关闭传入的标签页,默认关闭当前页。可传入多个 + :param tabs_or_ids: 要关闭的标签页对象或id,可传入列表或元组,为None时关闭最后操作的 + :param others: 是否关闭指定标签页之外的 + :return: None + """ + ... - def _new_tab(self, - obj, - url: str = None, - new_window: bool = False, - background: bool = False, - new_context: bool = False) -> Union[ChromiumTab, MixTab]: ... + def activate_tab(self, id_ind_tab: Union[int, str, ChromiumTab]) -> None: + """使标签页变为活动状态 + :param id_ind_tab: 标签页id(str)、Tab对象或标签页序号(int),序号从1开始 + :return: None + """ + ... - def new_tab(self, - url: str = None, - new_window: bool = False, - background: bool = False, - new_context: bool = False) -> ChromiumTab: ... + def reconnect(self) -> None: + """断开重连""" + ... - def new_mix_tab(self, - url: str = None, - new_window: bool = False, - background: bool = False, - new_context: bool = False) -> MixTab: ... + def quit(self, timeout: float = 5, force: bool = False, del_data: bool = False) -> None: + """关闭浏览器 + :param timeout: 等待浏览器关闭超时时间(秒) + :param force: 是否立刻强制终止进程 + :param del_data: 是否删除用户文件夹 + :return: None + """ + ... - def reconnect(self) -> None: ... + def _run_cdp(self, cmd, **cmd_args) -> dict: + """执行Chrome DevTools Protocol语句 + :param cmd: 协议项目 + :param cmd_args: 参数 + :return: 执行的结果 + """ + ... - def _onTargetCreated(self, **kwargs) -> None: ... + def _get_driver(self, tab_id: str, owner=None) -> Driver: + """新建并返回指定tab id的Driver + :param tab_id: 标签页id + :param owner: 使用该驱动的对象 + :return: Driver对象 + """ + ... - def _onTargetDestroyed(self, **kwargs) -> None: ... + def _onTargetCreated(self, **kwargs): ... - def quit(self, timeout: float = 5, force: bool = False, del_data: bool = False) -> None: ... + def _onTargetDestroyed(self, **kwargs): ... - def _on_disconnect(self) -> None: ... + def _on_disconnect(self): ... diff --git a/DrissionPage/_base/driver.py b/DrissionPage/_base/driver.py index 807b648..6374f6a 100644 --- a/DrissionPage/_base/driver.py +++ b/DrissionPage/_base/driver.py @@ -23,12 +23,6 @@ adapters.DEFAULT_RETRIES = 5 class Driver(object): def __init__(self, tab_id, tab_type, address, owner=None): - """ - :param tab_id: 标签页id - :param tab_type: 标签页类型 - :param address: 浏览器连接地址 - :param owner: 创建这个驱动的对象 - """ self.id = tab_id self.address = address self.type = tab_type @@ -58,11 +52,6 @@ class Driver(object): self.start() def _send(self, message, timeout=None): - """发送信息到浏览器,并返回浏览器返回的信息 - :param message: 发送给浏览器的数据 - :param timeout: 超时时间,为None表示无限 - :return: 浏览器返回的数据 - """ self._cur_id += 1 ws_id = self._cur_id message['id'] = ws_id @@ -110,7 +99,6 @@ class Driver(object): return {'error': {'message': 'connection disconnected'}, 'type': 'connection_error'} def _recv_loop(self): - """接收浏览器信息的守护线程方法""" while self.is_running: try: # self._ws.settimeout(1) @@ -148,7 +136,6 @@ class Driver(object): # print(f'未知信息:{msg}') def _handle_event_loop(self): - """当接收到浏览器信息,执行已绑定的方法""" while self.is_running: try: event = self.event_queue.get(timeout=1) @@ -170,11 +157,6 @@ class Driver(object): pass def _handle_immediate_event(self, function, kwargs): - """处理立即执行的动作 - :param function: 要运行下方法 - :param kwargs: 方法参数 - :return: None - """ self.immediate_event_queue.put((function, kwargs)) if self._handle_immediate_event_th is None or not self._handle_immediate_event_th.is_alive(): self._handle_immediate_event_th = Thread(target=self._handle_immediate_event_loop) @@ -200,7 +182,6 @@ class Driver(object): return result['result'] def start(self): - """启动连接""" self.is_running = True try: self._ws = create_connection(self._websocket_url, enable_multithread=True, suppress_origin=True) @@ -216,14 +197,12 @@ class Driver(object): return True def stop(self): - """中断连接""" self._stop() while self._handle_event_th.is_alive() or self._recv_th.is_alive(): sleep(.1) return True def _stop(self): - """中断连接""" if not self.is_running: return False @@ -259,12 +238,6 @@ class Driver(object): self.owner._on_disconnect() def set_callback(self, event, callback, immediate=False): - """绑定cdp event和回调方法 - :param event: cdp event - :param callback: 绑定到cdp event的回调方法 - :param immediate: 是否要立即处理的动作 - :return: None - """ handler = self.immediate_event_handlers if immediate else self.event_handlers if callback: handler[event] = callback diff --git a/DrissionPage/_base/driver.pyi b/DrissionPage/_base/driver.pyi index 539676d..cf9bcee 100644 --- a/DrissionPage/_base/driver.pyi +++ b/DrissionPage/_base/driver.pyi @@ -15,14 +15,6 @@ from websocket import WebSocket from .browser import Chromium -class GenericAttr(object): - def __init__(self, name: str, tab: Driver): ... - - def __getattr__(self, item: str) -> Callable: ... - - def __setattr__(self, key: str, value: Callable) -> None: ... - - class Driver(object): id: str address: str @@ -35,7 +27,6 @@ class Driver(object): _recv_th: Thread _handle_event_th: Thread _handle_immediate_event_th: Optional[Thread] - # _stopped: Event is_running: bool event_handlers: dict immediate_event_handlers: dict @@ -43,27 +34,69 @@ class Driver(object): event_queue: Queue immediate_event_queue: Queue - def __init__(self, tab_id: str, tab_type: str, address: str, owner=None): ... + def __init__(self, tab_id: str, tab_type: str, address: str, owner=None): + """ + :param tab_id: 标签页id + :param tab_type: 标签页类型 + :param address: 浏览器连接地址 + :param owner: 创建这个驱动的对象 + """ + ... - def _send(self, message: dict, timeout: float = None) -> dict: ... + def _send(self, message: dict, timeout: float = None) -> dict: + """发送信息到浏览器,并返回浏览器返回的信息 + :param message: 发送给浏览器的数据 + :param timeout: 超时时间,为None表示无限 + :return: 浏览器返回的数据 + """ + ... - def _recv_loop(self) -> None: ... + def _recv_loop(self) -> None: + """接收浏览器信息的守护线程方法""" + ... - def _handle_event_loop(self) -> None: ... + def _handle_event_loop(self) -> None: + """当接收到浏览器信息,执行已绑定的方法""" + ... def _handle_immediate_event_loop(self): ... - def _handle_immediate_event(self, function: Callable, kwargs: dict): ... + def _handle_immediate_event(self, function: Callable, kwargs: dict): + """处理立即执行的动作 + :param function: 要运行下方法 + :param kwargs: 方法参数 + :return: None + """ + ... - def run(self, _method: str, **kwargs) -> dict: ... + def run(self, _method: str, **kwargs) -> dict: + """执行cdp方法 + :param _method: cdp方法名 + :param kwargs: cdp参数 + :return: 执行结果 + """ + ... - def start(self) -> bool: ... + def start(self) -> bool: + """启动连接""" + ... - def stop(self) -> bool: ... + def stop(self) -> bool: + """中断连接""" + ... - def _stop(self) -> None: ... + def _stop(self) -> None: + """中断连接""" + ... - def set_callback(self, event: str, callback: Union[Callable, None], immediate: bool = False) -> None: ... + def set_callback(self, event: str, callback: Union[Callable, None], immediate: bool = False) -> None: + """绑定cdp event和回调方法 + :param event: cdp event + :param callback: 绑定到cdp event的回调方法 + :param immediate: 是否要立即处理的动作 + :return: None + """ + ... class BrowserDriver(Driver): @@ -74,4 +107,9 @@ class BrowserDriver(Driver): def __init__(self, tab_id: str, tab_type: str, address: str, owner: Chromium): ... - def get(self, url) -> Response: ... + def get(self, url) -> Response: + """ + :param url: 要访问的链接 + :return: Response对象 + """ + ... diff --git a/DrissionPage/_elements/chromium_element.py b/DrissionPage/_elements/chromium_element.py index c0cb800..ecaa47f 100644 --- a/DrissionPage/_elements/chromium_element.py +++ b/DrissionPage/_elements/chromium_element.py @@ -37,12 +37,6 @@ class ChromiumElement(DrissionElement): """控制浏览器元素的对象""" def __init__(self, owner, node_id=None, obj_id=None, backend_id=None): - """node_id、obj_id和backend_id必须至少传入一个 - :param owner: 元素所在页面对象 - :param node_id: cdp中的node id - :param obj_id: js中的object id - :param backend_id: backend id - """ super().__init__(owner) self.tab = self.owner._tab self._select = None @@ -81,11 +75,6 @@ class ChromiumElement(DrissionElement): return f'' def __call__(self, locator, index=1, timeout=None): - """在内部查找元素 - :param locator: 元素的定位信息,可以是loc元组,或查询字符串 - :param timeout: 超时时间(秒) - :return: ChromiumElement对象或属性、文本 - """ return self.ele(locator, index=index, timeout=timeout) def __eq__(self, other): @@ -93,7 +82,6 @@ class ChromiumElement(DrissionElement): @property def tag(self): - """返回元素tag""" if self._tag is None: self._tag = self.owner._run_cdp('DOM.describeNode', backendNodeId=self._backend_id)['node']['localName'].lower() @@ -101,17 +89,14 @@ class ChromiumElement(DrissionElement): @property def html(self): - """返回元素outerHTML文本""" return self.owner._run_cdp('DOM.getOuterHTML', backendNodeId=self._backend_id)['outerHTML'] @property def inner_html(self): - """返回元素innerHTML文本""" return self._run_js('return this.innerHTML;') @property def attrs(self): - """返回元素所有attribute属性""" try: attrs = self.owner._run_cdp('DOM.getAttributes', nodeId=self._node_id)['attributes'] return {attrs[i]: attrs[i + 1] for i in range(0, len(attrs), 2)} @@ -124,46 +109,39 @@ class ChromiumElement(DrissionElement): @property def text(self): - """返回元素内所有文本,文本已格式化""" return get_ele_txt(make_session_ele(self.html)) @property def raw_text(self): - """返回未格式化处理的元素内文本""" return self.property('innerText') # -----------------d模式独有属性------------------- @property def set(self): - """返回用于设置元素属性的对象""" if self._set is None: self._set = ChromiumElementSetter(self) return self._set @property def states(self): - """返回用于获取元素状态的对象""" if self._states is None: self._states = ElementStates(self) return self._states @property def pseudo(self): - """返回用于获取伪元素内容的对象""" if self._pseudo is None: self._pseudo = Pseudo(self) return self._pseudo @property def rect(self): - """返回用于获取元素位置的对象""" if self._rect is None: self._rect = ElementRect(self) return self._rect @property def sr(self): - """返回当前元素的shadow_root元素对象""" end_time = perf_counter() + self.owner.timeout while perf_counter() < end_time: info = self.owner._run_cdp('DOM.describeNode', backendNodeId=self._backend_id)['node'] @@ -173,39 +151,33 @@ class ChromiumElement(DrissionElement): @property def shadow_root(self): - """返回当前元素的shadow_root元素对象""" return self.sr @property def scroll(self): - """用于滚动滚动条的对象""" if self._scroll is None: self._scroll = ElementScroller(self) return self._scroll @property def click(self): - """返回用于点击的对象""" if self._clicker is None: self._clicker = Clicker(self) return self._clicker @property def wait(self): - """返回用于等待的对象""" if self._wait is None: self._wait = ElementWaiter(self) return self._wait @property def select(self): - """返回专门处理下拉列表的Select类,非下拉列表元素返回False""" if self._select is None: if self.tag != 'select': self._select = False else: self._select = SelectElement(self) - return self._select @property @@ -234,118 +206,39 @@ class ChromiumElement(DrissionElement): self.click() def parent(self, level_or_loc=1, index=1, timeout=0): - """返回上面某一级父元素,可指定层数或用查询语法定位 - :param level_or_loc: 第几级父元素,1开始,或定位符 - :param index: 当level_or_loc传入定位符,使用此参数选择第几个结果,1开始 - :param timeout: 查找超时时间(秒) - :return: 上级元素对象 - """ return super().parent(level_or_loc, index, timeout=timeout) def child(self, locator='', index=1, timeout=None, ele_only=True): - """返回当前元素的一个符合条件的直接子元素,可用查询语法筛选,可指定返回筛选结果的第几个 - :param locator: 用于筛选的查询语法 - :param index: 第几个查询结果,1开始 - :param timeout: 查找节点的超时时间(秒) - :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 - :return: 直接子元素或节点文本 - """ return super().child(locator, index, timeout, ele_only=ele_only) def prev(self, locator='', index=1, timeout=None, ele_only=True): - """返回当前元素前面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个 - :param locator: 用于筛选的查询语法 - :param index: 前面第几个查询结果,1开始 - :param timeout: 查找节点的超时时间(秒) - :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 - :return: 兄弟元素或节点文本 - """ return super().prev(locator, index, timeout, ele_only=ele_only) def next(self, locator='', index=1, timeout=None, ele_only=True): - """返回当前元素后面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个 - :param locator: 用于筛选的查询语法 - :param index: 第几个查询结果,1开始 - :param timeout: 查找节点的超时时间(秒) - :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 - :return: 兄弟元素或节点文本 - """ return super().next(locator, index, timeout, ele_only=ele_only) def before(self, locator='', index=1, timeout=None, ele_only=True): - """返回文档中当前元素前面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个 - 查找范围不限同级元素,而是整个DOM文档 - :param locator: 用于筛选的查询语法 - :param index: 前面第几个查询结果,1开始 - :param timeout: 查找节点的超时时间(秒) - :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 - :return: 本元素前面的某个元素或节点 - """ return super().before(locator, index, timeout, ele_only=ele_only) def after(self, locator='', index=1, timeout=None, ele_only=True): - """返回文档中此当前元素后面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个 - 查找范围不限同级元素,而是整个DOM文档 - :param locator: 用于筛选的查询语法 - :param index: 第几个查询结果,1开始 - :param timeout: 查找节点的超时时间(秒) - :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 - :return: 本元素后面的某个元素或节点 - """ return super().after(locator, index, timeout, ele_only=ele_only) def children(self, locator='', timeout=None, ele_only=True): - """返回当前元素符合条件的直接子元素或节点组成的列表,可用查询语法筛选 - :param locator: 用于筛选的查询语法 - :param timeout: 查找节点的超时时间(秒) - :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 - :return: 直接子元素或节点文本组成的列表 - """ return ChromiumElementsList(self.owner, super().children(locator, timeout, ele_only=ele_only)) def prevs(self, locator='', timeout=None, ele_only=True): - """返回当前元素前面符合条件的同级元素或节点组成的列表,可用查询语法筛选 - :param locator: 用于筛选的查询语法 - :param timeout: 查找节点的超时时间(秒) - :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 - :return: 兄弟元素或节点文本组成的列表 - """ return ChromiumElementsList(self.owner, super().prevs(locator, timeout, ele_only=ele_only)) def nexts(self, locator='', timeout=None, ele_only=True): - """返回当前元素后面符合条件的同级元素或节点组成的列表,可用查询语法筛选 - :param locator: 用于筛选的查询语法 - :param timeout: 查找节点的超时时间(秒) - :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 - :return: 兄弟元素或节点文本组成的列表 - """ return ChromiumElementsList(self.owner, super().nexts(locator, timeout, ele_only=ele_only)) def befores(self, locator='', timeout=None, ele_only=True): - """返回文档中当前元素前面符合条件的元素或节点组成的列表,可用查询语法筛选 - 查找范围不限同级元素,而是整个DOM文档 - :param locator: 用于筛选的查询语法 - :param timeout: 查找节点的超时时间(秒) - :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 - :return: 本元素前面的元素或节点组成的列表 - """ return ChromiumElementsList(self.owner, super().befores(locator, timeout, ele_only=ele_only)) def afters(self, locator='', timeout=None, ele_only=True): - """返回文档中当前元素后面符合条件的元素或节点组成的列表,可用查询语法筛选 - 查找范围不限同级元素,而是整个DOM文档 - :param locator: 用于筛选的查询语法 - :param timeout: 查找节点的超时时间(秒) - :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 - :return: 本元素后面的元素或节点组成的列表 - """ return ChromiumElementsList(self.owner, super().afters(locator, timeout, ele_only=ele_only)) def over(self, timeout=None): - """获取覆盖在本元素上最上层的元素 - :param timeout: 等待元素出现的超时时间(秒) - :return: 元素对象 - """ timeout = timeout if timeout is None else self.owner.timeout bid = self.wait.covered(timeout=timeout) if bid: @@ -354,13 +247,6 @@ class ChromiumElement(DrissionElement): return NoneElement(page=self.owner, method='on()', args={'timeout': timeout}) def offset(self, locator=None, x=None, y=None, timeout=None): - """获取相对本元素左上角左边指定偏移量位置的元素,如果offset_x和offset_y都是None,定位到元素中间点 - :param locator: 定位符,只支持str,且不支持xpath和css方式 - :param x: 横坐标偏移量,向右为正 - :param y: 纵坐标偏移量,向下为正 - :param timeout: 超时时间(秒),为None使用所在页面设置 - :return: 元素对象 - """ if locator and not (isinstance(locator, str) and not locator.startswith( ('x:', 'xpath:', 'x=', 'xpath=', 'c:', 'css:', 'c=', 'css='))): raise ValueError('locator参数只能是str格式且不支持xpath和css形式。') @@ -405,43 +291,18 @@ class ChromiumElement(DrissionElement): args={'locator': locator, 'offset_x': x, 'offset_y': y, 'timeout': timeout}) def east(self, loc_or_pixel=None, index=1): - """获取元素右边某个指定元素 - :param loc_or_pixel: 定位符,只支持str或int,且不支持xpath和css方式,传入int按像素距离获取 - :param index: 第几个,从1开始 - :return: 获取到的元素对象 - """ return self._get_relative_eles(mode='east', locator=loc_or_pixel, index=index) def south(self, loc_or_pixel=None, index=1): - """获取元素下方某个指定元素 - :param loc_or_pixel: 定位符,只支持str或int,且不支持xpath和css方式,传入int按像素距离获取 - :param index: 第几个,从1开始 - :return: 获取到的元素对象 - """ return self._get_relative_eles(mode='south', locator=loc_or_pixel, index=index) def west(self, loc_or_pixel=None, index=1): - """获取元素左边某个指定元素 - :param loc_or_pixel: 定位符,只支持str或int,且不支持xpath和css方式,传入int按像素距离获取 - :param index: 第几个,从1开始 - :return: 获取到的元素对象 - """ return self._get_relative_eles(mode='west', locator=loc_or_pixel, index=index) def north(self, loc_or_pixel=None, index=1): - """获取元素上方某个指定元素 - :param loc_or_pixel: 定位符,只支持str或int,且不支持xpath和css方式,传入int按像素距离获取 - :param index: 第几个,从1开始 - :return: 获取到的元素对象 - """ return self._get_relative_eles(mode='north', locator=loc_or_pixel, index=index) def _get_relative_eles(self, mode='north', locator=None, index=1): - """获取元素下方某个指定元素 - :param locator: 定位符,只支持str或int,且不支持xpath和css方式 - :param index: 第几个,从1开始 - :return: 获取到的元素对象 - """ if locator and not (isinstance(locator, str) and not locator.startswith( ('x:', 'xpath:', 'x=', 'xpath=', 'c:', 'css:', 'c=', 'css=')) or isinstance(locator, int)): raise ValueError('locator参数只能是str格式且不支持xpath和css形式。') @@ -508,10 +369,6 @@ class ChromiumElement(DrissionElement): return NoneElement(page=self.owner, method=f'{mode}()', args={'locator': locator}) def attr(self, attr): - """返回一个attribute属性值 - :param attr: 属性名 - :return: 属性值文本,没有该属性返回None - """ attrs = self.attrs if attr == 'href': # 获取href属性时返回绝对url link = attrs.get('href') @@ -539,17 +396,9 @@ class ChromiumElement(DrissionElement): return attrs.get(attr, None) def remove_attr(self, name): - """删除元素一个attribute属性 - :param name: 属性名 - :return: None - """ self._run_js(f'this.removeAttribute("{name}");') def property(self, name): - """获取一个property属性值 - :param name: 属性名 - :return: 属性值文本 - """ try: value = self._run_js(f'return this.{name};') return format_html(value) if isinstance(value, str) else value @@ -557,98 +406,38 @@ class ChromiumElement(DrissionElement): return None def run_js(self, script, *args, as_expr=False, timeout=None): - """对本元素执行javascript代码 - :param script: js文本,文本中用this表示本元素 - :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]... - :param as_expr: 是否作为表达式运行,为True时args无效 - :param timeout: js超时时间(秒),为None则使用页面timeouts.script设置 - :return: 运行的结果 - """ return self._run_js(script, *args, as_expr=as_expr, timeout=timeout) def _run_js(self, script, *args, as_expr=False, timeout=None): - """对本元素执行javascript代码 - :param script: js文本,文本中用this表示本元素 - :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]... - :param as_expr: 是否作为表达式运行,为True时args无效 - :param timeout: js超时时间(秒),为None则使用页面timeouts.script设置 - :return: 运行的结果 - """ return run_js(self, script, as_expr, self.owner.timeouts.script if timeout is None else timeout, args) def run_async_js(self, script, *args, as_expr=False): - """以异步方式对本元素执行javascript代码 - :param script: js文本,文本中用this表示本元素 - :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]... - :param as_expr: 是否作为表达式运行,为True时args无效 - :return: None - """ run_js(self, script, as_expr, 0, args) def ele(self, locator, index=1, timeout=None): - """返回当前元素下级符合条件的一个元素、属性或节点文本 - :param locator: 元素的定位信息,可以是loc元组,或查询字符串 - :param index: 获取第几个元素,从1开始,可传入负数获取倒数第几个 - :param timeout: 查找元素超时时间(秒),默认与元素所在页面等待时间一致 - :return: ChromiumElement对象或属性、文本 - """ return self._ele(locator, timeout, index=index, method='ele()') def eles(self, locator, timeout=None): - """返回当前元素下级所有符合条件的子元素、属性或节点文本 - :param locator: 元素的定位信息,可以是loc元组,或查询字符串 - :param timeout: 查找元素超时时间(秒),默认与元素所在页面等待时间一致 - :return: ChromiumElement对象或属性、文本组成的列表 - """ return self._ele(locator, timeout=timeout, index=None) def s_ele(self, locator=None, index=1, timeout=None): - """查找一个符合条件的元素,以SessionElement形式返回 - :param locator: 元素的定位信息,可以是loc元组,或查询字符串 - :param index: 获取第几个,从1开始,可传入负数获取倒数第几个 - :param timeout: 查找元素超时时间(秒),默认与元素所在页面等待时间一致 - :return: SessionElement对象或属性、文本 - """ return (make_session_ele(self, locator, index=index, method='s_ele()') if self.ele(locator, index=index, timeout=timeout) else NoneElement(self, method='s_ele()', args={'locator': locator, 'index': index})) def s_eles(self, locator=None, timeout=None): - """查找所有符合条件的元素,以SessionElement列表形式返回 - :param locator: 定位符 - :param timeout: 查找元素超时时间(秒),默认与元素所在页面等待时间一致 - :return: SessionElement或属性、文本组成的列表 - """ return (make_session_ele(self, locator, index=None) if self.ele(locator, timeout=timeout) else SessionElementsList()) def _find_elements(self, locator, timeout=None, index=1, relative=False, raise_err=None): - """返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个 - :param locator: 元素的定位信息,可以是loc元组,或查询字符串 - :param timeout: 查找元素超时时间(秒) - :param index: 第几个结果,从1开始,可传入负数获取倒数第几个,为None返回所有 - :param relative: MixTab用的表示是否相对定位的参数 - :param raise_err: 找不到元素是是否抛出异常,为None时根据全局设置 - :return: ChromiumElement对象或文本、属性或其组成的列表 - """ return find_in_chromium_ele(self, locator, index, timeout, relative=relative) def style(self, style, pseudo_ele=''): - """返回元素样式属性值,可获取伪元素属性值 - :param style: 样式属性名称 - :param pseudo_ele: 伪元素名称(如有) - :return: 样式属性的值 - """ if pseudo_ele: pseudo_ele = f', "{pseudo_ele}"' if pseudo_ele.startswith(':') else f', "::{pseudo_ele}"' return self._run_js(f'return window.getComputedStyle(this{pseudo_ele}).getPropertyValue("{style}");') def src(self, timeout=None, base64_to_bytes=True): - """返回元素src资源,base64的可转为bytes返回,其它返回str - :param timeout: 等待资源加载的超时时间(秒) - :param base64_to_bytes: 为True时,如果是base64数据,转换为bytes格式 - :return: 资源内容 - """ timeout = self.owner.timeout if timeout is None else timeout if self.tag == 'img': # 等待图片加载完成 js = ('return this.complete && typeof this.naturalWidth != "undefined" ' @@ -658,7 +447,7 @@ class ChromiumElement(DrissionElement): while not self._run_js(js) and perf_counter() < end_time: sleep(.1) - src = self.attr('src') + src = self.attr('href') if self.tag == 'link' else self.attr('src') if not src: raise RuntimeError('元素没有src值或该值为空。') if src.lower().startswith('data:image'): @@ -680,8 +469,9 @@ class ChromiumElement(DrissionElement): else: while perf_counter() < end_time: - src = self.property('currentSrc') + src = self.attr('href') if self.tag == 'link' else self.property('currentSrc') or self.property('src') if not src: + sleep(.01) continue node = self.owner._run_cdp('DOM.describeNode', backendNodeId=self._backend_id)['node'] @@ -707,13 +497,6 @@ class ChromiumElement(DrissionElement): return result['content'] def save(self, path=None, name=None, timeout=None, rename=True): - """保存图片或其它有src属性的元素的资源 - :param path: 文件保存路径,为None时保存到当前文件夹 - :param name: 文件名称,为None时从资源url获取 - :param timeout: 等待资源加载的超时时间(秒) - :param rename: 遇到重名文件时是否自动重命名 - :return: 返回保存路径 - """ data = self.src(timeout=timeout) if not data: raise NoResourceError @@ -739,14 +522,6 @@ class ChromiumElement(DrissionElement): return str(path) def get_screenshot(self, path=None, name=None, as_bytes=None, as_base64=None, scroll_to_center=True): - """对当前元素截图,可保存到文件,或以字节方式返回 - :param path: 文件保存路径 - :param name: 完整文件名,后缀可选 'jpg','jpeg','png','webp' - :param as_bytes: 是否以字节形式返回图片,可选 'jpg','jpeg','png','webp',生效时path参数和as_base64参数无效 - :param as_base64: 是否以base64字符串形式返回图片,可选 'jpg','jpeg','png','webp',生效时path参数无效 - :param scroll_to_center: 截图前是否滚动到视口中央 - :return: 图片完整路径或字节文本 - """ if self.tag == 'img': # 等待图片加载完成 js = ('return this.complete && typeof this.naturalWidth != "undefined" && this.naturalWidth > 0 ' '&& typeof this.naturalHeight != "undefined" && this.naturalHeight > 0') @@ -767,12 +542,6 @@ class ChromiumElement(DrissionElement): left_top=left_top, right_bottom=right_bottom, ele=self) def input(self, vals, clear=False, by_js=False): - """输入文本或组合键,也可用于输入文件路径到input元素(路径间用\n间隔) - :param vals: 文本值或按键组合 - :param clear: 输入前是否清空文本框 - :param by_js: 是否用js方式输入,不能输入组合键 - :return: None - """ if self.tag == 'input' and self.attr('type') == 'file': return self._set_file_input(vals) @@ -798,10 +567,6 @@ class ChromiumElement(DrissionElement): self.owner.actions.type(vals) def clear(self, by_js=False): - """清空元素文本 - :param by_js: 是否用js方式清空,为False则用全选+del模拟输入删除 - :return: None - """ if by_js: self._run_js("this.value='';") self._run_js('this.dispatchEvent(new Event("change", {bubbles: true}));') @@ -811,45 +576,27 @@ class ChromiumElement(DrissionElement): self.input(('\ue009', 'a', '\ue017'), clear=False) def _input_focus(self): - """输入前使元素获取焦点""" try: self.owner._run_cdp('DOM.focus', backendNodeId=self._backend_id) except Exception: self.click(by_js=None) def focus(self): - """使元素获取焦点""" try: self.owner._run_cdp('DOM.focus', backendNodeId=self._backend_id) except Exception: self._run_js('this.focus();') def hover(self, offset_x=None, offset_y=None): - """鼠标悬停,可接受偏移量,偏移量相对于元素左上角坐标。不传入offset_x和offset_y值时悬停在元素中点 - :param offset_x: 相对元素左上角坐标的x轴偏移量 - :param offset_y: 相对元素左上角坐标的y轴偏移量 - :return: None - """ self.owner.actions.move_to(self, offset_x=offset_x, offset_y=offset_y, duration=.1) def drag(self, offset_x=0, offset_y=0, duration=.5): - """拖拽当前元素到相对位置 - :param offset_x: x变化值 - :param offset_y: y变化值 - :param duration: 拖动用时,传入0即瞬间到达 - :return: None - """ curr_x, curr_y = self.rect.midpoint offset_x += curr_x offset_y += curr_y self.drag_to((offset_x, offset_y), duration) def drag_to(self, ele_or_loc, duration=.5): - """拖拽当前元素,目标为另一个元素或坐标元组(x, y) - :param ele_or_loc: 另一个元素或坐标元组,坐标为元素中点的坐标 - :param duration: 拖动用时,传入0即瞬间到达 - :return: None - """ if isinstance(ele_or_loc, ChromiumElement): ele_or_loc = ele_or_loc.rect.midpoint elif not isinstance(ele_or_loc, (list, tuple)): @@ -857,22 +604,12 @@ class ChromiumElement(DrissionElement): self.owner.actions.hold(self).move_to(ele_or_loc, duration=duration).release() def _get_obj_id(self, node_id=None, backend_id=None): - """根据传入node id或backend id获取js中的object id - :param node_id: cdp中的node id - :param backend_id: backend id - :return: js中的object id - """ if node_id: return self.owner._run_cdp('DOM.resolveNode', nodeId=node_id)['object']['objectId'] else: return self.owner._run_cdp('DOM.resolveNode', backendNodeId=backend_id)['object']['objectId'] def _get_node_id(self, obj_id=None, backend_id=None): - """根据传入object id或backend id获取cdp中的node id - :param obj_id: js中的object id - :param backend_id: backend id - :return: cdp中的node id - """ if obj_id: return self.owner._run_cdp('DOM.requestNode', objectId=obj_id)['nodeId'] else: @@ -881,21 +618,15 @@ class ChromiumElement(DrissionElement): return n['nodeId'] def _get_backend_id(self, node_id): - """根据传入node id获取backend id - :param node_id: - :return: backend id - """ n = self.owner._run_cdp('DOM.describeNode', nodeId=node_id)['node'] self._tag = n['localName'] return n['backendNodeId'] def _refresh_id(self): - """根据backend id刷新其它id""" self._obj_id = self._get_obj_id(backend_id=self._backend_id) self._node_id = self._get_node_id(obj_id=self._obj_id) def _get_ele_path(self, mode): - """返获取绝对的css路径或xpath路径""" if mode == 'xpath': txt1 = 'let tag = el.nodeName.toLowerCase();' txt3 = ''' && sib.nodeName.toLowerCase()==tag''' @@ -940,10 +671,6 @@ class ChromiumElement(DrissionElement): return f'{t}' if mode == 'css' else t def _set_file_input(self, files): - """对上传控件写入路径 - :param files: 文件路径列表或字符串,字符串时多个文件用回车分隔 - :return: None - """ if isinstance(files, str): files = files.split('\n') files = [str(Path(i).absolute()) for i in files] @@ -954,11 +681,6 @@ class ShadowRoot(BaseElement): """ShadowRoot是用于处理ShadowRoot的类,使用方法和ChromiumElement基本一致""" def __init__(self, parent_ele, obj_id=None, backend_id=None): - """ - :param parent_ele: shadow root 所在父元素 - :param obj_id: js中的object id - :param backend_id: cdp中的backend id - """ super().__init__(parent_ele.owner) self.tab = self.owner._tab self.parent_ele = parent_ele @@ -977,13 +699,6 @@ class ShadowRoot(BaseElement): return f'' def __call__(self, locator, index=1, timeout=None): - """在内部查找元素 - 例:ele2 = ele1('@id=ele_id') - :param locator: 元素的定位信息,可以是loc元组,或查询字符串 - :param index: 获取第几个,从1开始,可传入负数获取倒数第几个 - :param timeout: 超时时间(秒) - :return: 元素对象或属性、文本 - """ return self.ele(locator, index=index, timeout=timeout) def __eq__(self, other): @@ -991,65 +706,34 @@ class ShadowRoot(BaseElement): @property def tag(self): - """返回元素标签名""" return 'shadow-root' @property def html(self): - """返回outerHTML文本""" return f'{self.inner_html}' @property def inner_html(self): - """返回内部的html文本""" return self._run_js('return this.innerHTML;') @property def states(self): - """返回用于获取元素状态的对象""" if self._states is None: self._states = ShadowRootStates(self) return self._states def run_js(self, script, *args, as_expr=False, timeout=None): - """运行javascript代码 - :param script: js文本 - :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]... - :param as_expr: 是否作为表达式运行,为True时args无效 - :param timeout: js超时时间(秒),为None则使用页面timeouts.script设置 - :return: 运行的结果 - """ return self._run_js(script, *args, as_expr=as_expr, timeout=timeout) def _run_js(self, script, *args, as_expr=False, timeout=None): - """运行javascript代码 - :param script: js文本 - :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]... - :param as_expr: 是否作为表达式运行,为True时args无效 - :param timeout: js超时时间(秒),为None则使用页面timeouts.script设置 - :return: 运行的结果 - """ return run_js(self, script, as_expr, self.owner.timeouts.script if timeout is None else timeout, args) def run_async_js(self, script, *args, as_expr=False, timeout=None): - """以异步方式执行js代码 - :param script: js文本 - :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]... - :param as_expr: 是否作为表达式运行,为True时args无效 - :param timeout: js超时时间(秒),为None则使用页面timeouts.script设置 - :return: None - """ from threading import Thread Thread(target=run_js, args=(self, script, as_expr, self.owner.timeouts.script if timeout is None else timeout, args)).start() def parent(self, level_or_loc=1, index=1, timeout=0): - """返回上面某一级父元素,可指定层数或用查询语法定位 - :param level_or_loc: 第几级父元素,或定位符 - :param index: 当level_or_loc传入定位符,使用此参数选择第几个结果 - :param timeout: 查找超时时间(秒) - :return: ChromiumElement对象 - """ if isinstance(level_or_loc, int): loc = f'xpath:./ancestor-or-self::*[{level_or_loc}]' @@ -1067,12 +751,6 @@ class ShadowRoot(BaseElement): return self.parent_ele._ele(loc, timeout=timeout, relative=True, raise_err=False, method='parent()') def child(self, locator='', index=1, timeout=None): - """返回直接子元素元素或节点组成的列表,可用查询语法筛选 - :param locator: 用于筛选的查询语法 - :param index: 第几个查询结果,1开始 - :param timeout: 查找超时时间(秒) - :return: 直接子元素或节点文本组成的列表 - """ if not locator: loc = '*' else: @@ -1088,12 +766,6 @@ class ShadowRoot(BaseElement): {'locator': locator, 'index': index, 'timeout': timeout}) def next(self, locator='', index=1, timeout=None): - """返回当前元素后面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个 - :param locator: 用于筛选的查询语法 - :param index: 第几个查询结果,1开始 - :param timeout: 查找超时时间(秒) - :return: ChromiumElement对象 - """ loc = get_loc(locator, True) if loc[0] == 'css selector': raise ValueError('此css selector语法不受支持,请换成xpath。') @@ -1106,13 +778,6 @@ class ShadowRoot(BaseElement): {'locator': locator, 'index': index, 'timeout': timeout}) def before(self, locator='', index=1, timeout=None): - """返回文档中当前元素前面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个 - 查找范围不限同级元素,而是整个DOM文档 - :param locator: 用于筛选的查询语法 - :param index: 前面第几个查询结果,1开始 - :param timeout: 查找超时时间(秒) - :return: 本元素前面的某个元素或节点 - """ loc = get_loc(locator, True) if loc[0] == 'css selector': raise ValueError('此css selector语法不受支持,请换成xpath。') @@ -1125,23 +790,11 @@ class ShadowRoot(BaseElement): {'locator': locator, 'index': index, 'timeout': timeout}) def after(self, locator='', index=1, timeout=None): - """返回文档中此当前元素后面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个 - 查找范围不限同级元素,而是整个DOM文档 - :param locator: 用于筛选的查询语法 - :param index: 后面第几个查询结果,1开始 - :param timeout: 查找超时时间(秒) - :return: 本元素后面的某个元素或节点 - """ nodes = self.afters(locator=locator, timeout=timeout) return nodes[index - 1] if nodes else NoneElement(self.owner, 'after()', {'locator': locator, 'index': index, 'timeout': timeout}) def children(self, locator='', timeout=None): - """返回当前元素符合条件的直接子元素或节点组成的列表,可用查询语法筛选 - :param locator: 用于筛选的查询语法 - :param timeout: 查找超时时间(秒) - :return: 直接子元素或节点文本组成的列表 - """ if not locator: loc = '*' else: @@ -1154,11 +807,6 @@ class ShadowRoot(BaseElement): return self._ele(loc, index=None, relative=True, timeout=timeout) def nexts(self, locator='', timeout=None): - """返回当前元素后面符合条件的同级元素或节点组成的列表,可用查询语法筛选 - :param locator: 用于筛选的查询语法 - :param timeout: 查找超时时间(秒) - :return: ChromiumElement对象组成的列表 - """ loc = get_loc(locator, True) if loc[0] == 'css selector': raise ValueError('此css selector语法不受支持,请换成xpath。') @@ -1168,12 +816,6 @@ class ShadowRoot(BaseElement): return self.parent_ele._ele(xpath, index=None, relative=True, timeout=timeout) def befores(self, locator='', timeout=None): - """返回文档中当前元素前面符合条件的元素或节点组成的列表,可用查询语法筛选 - 查找范围不限同级元素,而是整个DOM文档 - :param locator: 用于筛选的查询语法 - :param timeout: 查找超时时间(秒) - :return: 本元素前面的元素或节点组成的列表 - """ loc = get_loc(locator, True) if loc[0] == 'css selector': raise ValueError('此css selector语法不受支持,请换成xpath。') @@ -1183,63 +825,27 @@ class ShadowRoot(BaseElement): return self.parent_ele._ele(xpath, index=None, relative=True, timeout=timeout) def afters(self, locator='', timeout=None): - """返回文档中当前元素后面符合条件的元素或节点组成的列表,可用查询语法筛选 - 查找范围不限同级元素,而是整个DOM文档 - :param locator: 用于筛选的查询语法 - :param timeout: 查找超时时间(秒) - :return: 本元素后面的元素或节点组成的列表 - """ eles1 = self.nexts(locator) loc = get_loc(locator, True)[1].lstrip('./') xpath = f'xpath:./following::{loc}' return eles1 + self.parent_ele._ele(xpath, index=None, relative=True, timeout=timeout) def ele(self, locator, index=1, timeout=None): - """返回当前元素下级符合条件的一个元素 - :param locator: 元素的定位信息,可以是loc元组,或查询字符串 - :param index: 获取第几个元素,从1开始,可传入负数获取倒数第几个 - :param timeout: 查找元素超时时间(秒),默认与元素所在页面等待时间一致 - :return: ChromiumElement对象 - """ return self._ele(locator, timeout, index=index, method='ele()') def eles(self, locator, timeout=None): - """返回当前元素下级所有符合条件的子元素 - :param locator: 元素的定位信息,可以是loc元组,或查询字符串 - :param timeout: 查找元素超时时间(秒),默认与元素所在页面等待时间一致 - :return: ChromiumElement对象组成的列表 - """ return self._ele(locator, timeout=timeout, index=None) def s_ele(self, locator=None, index=1, timeout=None): - """查找一个符合条件的元素以SessionElement形式返回,处理复杂页面时效率很高 - :param locator: 元素的定位信息,可以是loc元组,或查询字符串 - :param index: 获取第几个,从1开始,可传入负数获取倒数第几个 - :param timeout: 查找元素超时时间(秒),默认与元素所在页面等待时间一致 - :return: SessionElement对象或属性、文本 - """ return (make_session_ele(self, locator, index=index, method='s_ele()') if self.ele(locator, index=index, timeout=timeout) else NoneElement(self, method='s_ele()', args={'locator': locator, 'index': index})) def s_eles(self, locator, timeout=None): - """查找所有符合条件的元素以SessionElement列表形式返回,处理复杂页面时效率很高 - :param locator: 元素的定位信息,可以是loc元组,或查询字符串 - :param timeout: 查找元素超时时间(秒),默认与元素所在页面等待时间一致 - :return: SessionElement对象 - """ return (make_session_ele(self, locator, index=None) if self.ele(locator, timeout=timeout) else SessionElementsList()) def _find_elements(self, locator, timeout=None, index=1, relative=False, raise_err=None): - """返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个 - :param locator: 元素的定位信息,可以是loc元组,或查询字符串 - :param timeout: 查找元素超时时间(秒) - :param index: 第几个结果,从1开始,可传入负数获取倒数第几个,为None返回所有 - :param relative: MixTab用的表示是否相对定位的参数 - :param raise_err: 找不到元素是是否抛出异常,为None时根据全局设置 - :return: ChromiumElement对象或其组成的列表 - """ loc = get_loc(locator, css_mode=False) if loc[0] == 'css selector' and str(loc[1]).startswith(':root'): loc = loc[0], loc[1][5:] @@ -1301,29 +907,18 @@ class ShadowRoot(BaseElement): return NoneElement(self.owner) if index is not None else ChromiumElementsList() def _get_node_id(self, obj_id): - """返回元素node id""" return self.owner._run_cdp('DOM.requestNode', objectId=obj_id)['nodeId'] def _get_obj_id(self, back_id): - """返回元素object id""" return self.owner._run_cdp('DOM.resolveNode', backendNodeId=back_id)['object']['objectId'] def _get_backend_id(self, node_id): - """返回元素object id""" r = self.owner._run_cdp('DOM.describeNode', nodeId=node_id)['node'] self._tag = r['localName'].lower() return r['backendNodeId'] def find_in_chromium_ele(ele, locator, index=1, timeout=None, relative=True): - """在chromium元素中查找 - :param ele: ChromiumElement对象 - :param locator: 元素定位元组 - :param index: 第几个结果,从1开始,可传入负数获取倒数第几个,为None返回所有 - :param timeout: 查找元素超时时间(秒) - :param relative: MixTab用于标记是否相对定位使用 - :return: 返回ChromiumElement元素或它们组成的列表 - """ # ---------------处理定位符--------------- if isinstance(locator, (str, tuple)): loc = get_loc(locator) @@ -1348,14 +943,6 @@ def find_in_chromium_ele(ele, locator, index=1, timeout=None, relative=True): def find_by_xpath(ele, xpath, index, timeout, relative=True): - """执行用xpath在元素中查找元素 - :param ele: 在此元素中查找 - :param xpath: 查找语句 - :param index: 第几个结果,从1开始,可传入负数获取倒数第几个,为None返回所有 - :param timeout: 超时时间(秒) - :param relative: 是否相对定位 - :return: ChromiumElement或其组成的列表 - """ type_txt = '9' if index == 1 else '7' node_txt = 'this.contentDocument' if ele.tag in __FRAME_ELEMENT__ and not relative else 'this' js = make_js_for_find_ele_by_xpath(xpath, type_txt, node_txt) @@ -1419,13 +1006,6 @@ def find_by_xpath(ele, xpath, index, timeout, relative=True): def find_by_css(ele, selector, index, timeout): - """执行用css selector在元素中查找元素 - :param ele: 在此元素中查找 - :param selector: 查找语句 - :param index: 第几个结果,从1开始,可传入负数获取倒数第几个,为None返回所有 - :param timeout: 超时时间(秒) - :return: ChromiumElement或其组成的列表 - """ selector = selector.replace('"', r'\"') find_all = '' if index == 1 else 'All' node_txt = 'this.contentDocument' if ele.tag in ('iframe', 'frame', 'shadow-root') else 'this' @@ -1465,14 +1045,6 @@ def find_by_css(ele, selector, index, timeout): def make_chromium_eles(page, _ids, index=1, is_obj_id=True, ele_only=False): - """根据node id或object id生成相应元素对象 - :param page: ChromiumPage对象 - :param _ids: 元素的id列表 - :param index: 获取第几个,为None返回全部 - :param is_obj_id: 传入的id是obj id还是node id - :param ele_only: 是否只返回ele,在页面查找元素时生效 - :return: 浏览器元素对象或它们组成的列表,生成失败返回False - """ if is_obj_id: get_node_func = _get_node_by_obj_id else: @@ -1551,12 +1123,6 @@ def _make_ele(page, obj_id, node): def make_js_for_find_ele_by_xpath(xpath, type_txt, node_txt): - """生成用xpath在元素中查找元素的js文本 - :param xpath: xpath文本 - :param type_txt: 查找类型 - :param node_txt: 节点类型 - :return: js文本 - """ for_txt = '' # 获取第一个元素、节点或属性 @@ -1593,14 +1159,6 @@ else{a.push(e.snapshotItem(i));}}""" def run_js(page_or_ele, script, as_expr, timeout, args=None): - """运行javascript代码 - :param page_or_ele: 页面对象或元素对象 - :param script: js文本 - :param as_expr: 是否作为表达式运行,为True时args无效 - :param timeout: 超时时间(秒) - :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]... - :return: js执行结果 - """ if isinstance(page_or_ele, (ChromiumElement, ShadowRoot)): is_page = False page = page_or_ele.owner @@ -1663,7 +1221,6 @@ def run_js(page_or_ele, script, as_expr, timeout, args=None): def parse_js_result(page, ele, result, end_time): - """解析js返回的结果""" if 'unserializableValue' in result: return result['unserializableValue'] @@ -1713,7 +1270,6 @@ def parse_js_result(page, ele, result, end_time): def convert_argument(arg): - """把参数转换成js能够接收的形式""" if isinstance(arg, ChromiumElement): return {'objectId': arg._obj_id} @@ -1731,19 +1287,14 @@ def convert_argument(arg): class Pseudo(object): def __init__(self, ele): - """ - :param ele: ChromiumElement - """ self._ele = ele @property def before(self): - """返回当前元素的::before伪元素内容""" return self._ele.style('content', 'before') @property def after(self): - """返回当前元素的::after伪元素内容""" return self._ele.style('content', 'after') diff --git a/DrissionPage/_elements/chromium_element.pyi b/DrissionPage/_elements/chromium_element.pyi index 1af2011..415d8ec 100644 --- a/DrissionPage/_elements/chromium_element.pyi +++ b/DrissionPage/_elements/chromium_element.pyi @@ -28,238 +28,552 @@ PIC_TYPE = Literal['jpg', 'jpeg', 'png', 'webp', True] class ChromiumElement(DrissionElement): + _tag: Optional[str] = ... + owner: ChromiumBase = ... + page: Union[ChromiumPage, MixPage] = ... + tab: Union[ChromiumPage, ChromiumTab] = ... + _node_id: int = ... + _obj_id: str = ... + _backend_id: int = ... + _doc_id: Optional[str] = ... + _scroll: Optional[ElementScroller] = ... + _clicker: Optional[Clicker] = ... + _select: Union[SelectElement, None, False] = ... + _wait: Optional[ElementWaiter] = ... + _rect: Optional[ElementRect] = ... + _set: Optional[ChromiumElementSetter] = ... + _states: Optional[ElementStates] = ... + _pseudo: Optional[Pseudo] = ... - def __init__(self, owner: ChromiumBase, node_id: int = None, obj_id: str = None, backend_id: int = None): - self._tag: str = ... - self.owner: ChromiumBase = ... - self.page: Union[ChromiumPage, MixPage] = ... - self.tab: Union[ChromiumPage, ChromiumTab] = ... - self._node_id: int = ... - self._obj_id: str = ... - self._backend_id: int = ... - self._doc_id: str = ... - self._scroll: ElementScroller = ... - self._clicker: Clicker = ... - self._select: SelectElement = ... - self._wait: ElementWaiter = ... - self._rect: ElementRect = ... - self._set: ChromiumElementSetter = ... - self._states: ElementStates = ... - self._pseudo: Pseudo = ... + def __init__(self, + owner: ChromiumBase, + node_id: int = None, + obj_id: str = None, + backend_id: int = None): + """node_id、obj_id和backend_id必须至少传入一个 + :param owner: 元素所在页面对象 + :param node_id: cdp中的node id + :param obj_id: js中的object id + :param backend_id: backend id + """ + ... def __repr__(self) -> str: ... def __call__(self, locator: Union[Tuple[str, str], str], index: int = 1, - timeout: float = None) -> ChromiumElement: ... + timeout: float = None) -> ChromiumElement: + """在内部查找元素 + :param locator: 元素的定位信息,可以是loc元组,或查询字符串 + :param timeout: 超时时间(秒) + :return: ChromiumElement对象或属性、文本 + """ + ... def __eq__(self, other: ChromiumElement) -> bool: ... @property - def tag(self) -> str: ... + def tag(self) -> str: + """返回元素tag""" + ... @property - def html(self) -> str: ... + def html(self) -> str: + """返回元素outerHTML文本""" + ... @property - def inner_html(self) -> str: ... + def inner_html(self) -> str: + """返回元素innerHTML文本""" + ... @property - def attrs(self) -> dict: ... + def attrs(self) -> dict: + """返回元素所有attribute属性""" + ... @property - def text(self) -> str: ... + def text(self) -> str: + """返回元素内所有文本,文本已格式化""" + ... @property - def raw_text(self) -> str: ... - - # -----------------d模式独有属性------------------- - @property - def set(self) -> ChromiumElementSetter: ... + def raw_text(self) -> str: + """返回未格式化处理的元素内文本""" + ... @property - def states(self) -> ElementStates: ... + def set(self) -> ChromiumElementSetter: + """返回用于设置元素属性的对象""" + ... @property - def rect(self) -> ElementRect: ... + def states(self) -> ElementStates: + """返回用于获取元素状态的对象""" + ... @property - def pseudo(self) -> Pseudo: ... + def pseudo(self) -> Pseudo: + """返回用于获取伪元素内容的对象""" + ... @property - def shadow_root(self) -> Union[None, ShadowRoot]: ... + def rect(self) -> ElementRect: + """返回用于获取元素位置的对象""" + ... @property - def sr(self) -> Union[None, ShadowRoot]: ... + def shadow_root(self) -> Union[None, ShadowRoot]: + """返回当前元素的shadow_root元素对象""" + ... @property - def scroll(self) -> ElementScroller: ... + def sr(self) -> Union[None, ShadowRoot]: + """返回当前元素的shadow_root元素对象""" + ... @property - def click(self) -> Clicker: ... + def scroll(self) -> ElementScroller: + """用于滚动滚动条的对象""" + ... + + @property + def click(self) -> Clicker: + """返回用于点击的对象""" + ... + + @property + def wait(self) -> ElementWaiter: + """返回用于等待的对象""" + ... + + @property + def select(self) -> Union[SelectElement, False]: + """返回专门处理下拉列表的Select类,非