From c077aa0c2e884d35387c6e2ed8893190c7dd18dd Mon Sep 17 00:00:00 2001 From: g1879 Date: Fri, 21 Jan 2022 14:39:58 +0800 Subject: [PATCH] =?UTF-8?q?ShadowRootElement=E8=A1=A5=E8=B6=B3DOM=E7=9B=B8?= =?UTF-8?q?=E5=AF=B9=E5=AE=9A=E4=BD=8D=E6=96=B9=E6=B3=95=EF=BC=9B=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E7=9B=B8=E5=AF=B9=E5=AE=9A=E4=BD=8D=E6=97=B6=E7=94=A8?= =?UTF-8?q?=E6=96=87=E6=9C=AC=E6=9F=A5=E6=89=BE=E5=AF=BC=E8=87=B4=E7=9A=84?= =?UTF-8?q?=E9=94=99=E8=AF=AF=EF=BC=9B=E5=AE=8C=E5=96=84=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E6=B3=A8=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/base.py | 91 ++++++++++++++++----------- DrissionPage/driver_element.py | 95 +++++++++++++++++++++++++++++ DrissionPage/session_element.py | 95 +++++++++++++++++++++++++++++ DrissionPage/shadow_root_element.py | 50 ++++++++++++++- docs/使用方法/查找页面元素.md | 25 ++++++++ setup.py | 2 +- 6 files changed, 321 insertions(+), 37 deletions(-) diff --git a/DrissionPage/base.py b/DrissionPage/base.py index f10ced5..6ce74cc 100644 --- a/DrissionPage/base.py +++ b/DrissionPage/base.py @@ -69,10 +69,10 @@ class BaseElement(BaseParser): def parent(self, level_or_loc: Union[tuple, str, int] = 1): pass - def prev(self, index: int = 1): + def prev(self, index: int = 1) -> None: return None # ShadowRootElement直接继承 - def prevs(self): + def prevs(self) -> None: return None # ShadowRootElement直接继承 def next(self, index: int = 1): @@ -117,10 +117,10 @@ class DrissionElement(BaseElement): 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: Union[tuple, str, int] = 1) -> 'DrissionElement': + def parent(self, level_or_loc: Union[tuple, str, int] = 1) -> Union['DrissionElement', None]: """返回上面某一级父元素,可指定层数或用查询语法定位 \n :param level_or_loc: 第几级父元素,或定位符 - :return: DriverElement对象 + :return: 上级元素对象 """ if isinstance(level_or_loc, int): loc = f'xpath:./ancestor::*[{level_or_loc}]' @@ -138,43 +138,36 @@ class DrissionElement(BaseElement): return self.ele(loc, timeout=0) - def prev(self, index: int = 1, filter_loc: Union[tuple, str] = '', timeout: float = 0): + def prev(self, + index: int = 1, + filter_loc: Union[tuple, str] = '', + timeout: float = 0) -> Union['DrissionElement', str, None]: """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n :param index: 前面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 :param timeout: 查找元素的超时时间 - :return: 兄弟元素或节点文本组成的列表 + :return: 兄弟元素 """ nodes = self._get_brothers(index, filter_loc, 'preceding', timeout=timeout) return nodes[-1] if nodes else None - def next(self, index: int = 1, filter_loc: Union[tuple, str] = '', timeout: float = 0): + def next(self, + index: int = 1, + filter_loc: Union[tuple, str] = '', + timeout: float = 0) -> Union['DrissionElement', str, None]: """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n :param index: 后面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 :param timeout: 查找元素的超时时间 - :return: 兄弟元素或节点文本组成的列表 + :return: 兄弟元素 """ nodes = self._get_brothers(index, filter_loc, 'following', timeout=timeout) return nodes[0] if nodes else None - def nexts(self, filter_loc: Union[tuple, str] = '', timeout: float = 0): - """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n - :param filter_loc: 用于筛选元素的查询语法 - :param timeout: 查找元素的超时时间 - :return: 兄弟元素或节点文本组成的列表 - """ - return self._get_brothers(filter_loc=filter_loc, direction='following', timeout=timeout) - - def prevs(self, filter_loc: Union[tuple, str] = '', timeout: float = 0): - """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n - :param filter_loc: 用于筛选元素的查询语法 - :param timeout: 查找元素的超时时间 - :return: 兄弟元素或节点文本组成的列表 - """ - return self._get_brothers(filter_loc=filter_loc, direction='preceding', timeout=timeout) - - def before(self, index: int = 1, filter_loc: Union[tuple, str] = '', timeout: float = None): + def before(self, + index: int = 1, + filter_loc: Union[tuple, str] = '', + timeout: float = None) -> Union['DrissionElement', str, None]: """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n :param index: 前面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -184,7 +177,10 @@ class DrissionElement(BaseElement): nodes = self._get_brothers(index, filter_loc, 'preceding', False, timeout=timeout) return nodes[-1] if nodes else None - def after(self, index: int = 1, filter_loc: Union[tuple, str] = '', timeout: float = None): + def after(self, + index: int = 1, + filter_loc: Union[tuple, str] = '', + timeout: float = None) -> Union['DrissionElement', str, None]: """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n :param index: 后面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -194,7 +190,29 @@ class DrissionElement(BaseElement): nodes = self._get_brothers(index, filter_loc, 'following', False, timeout) return nodes[0] if nodes else None - def befores(self, filter_loc: Union[tuple, str] = '', timeout: float = None): + def prevs(self, + filter_loc: Union[tuple, str] = '', + timeout: float = 0) -> List[Union['DrissionElement', str]]: + """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n + :param filter_loc: 用于筛选元素的查询语法 + :param timeout: 查找元素的超时时间 + :return: 兄弟元素或节点文本组成的列表 + """ + return self._get_brothers(filter_loc=filter_loc, direction='preceding', timeout=timeout) + + def nexts(self, + filter_loc: Union[tuple, str] = '', + timeout: float = 0) -> List[Union['DrissionElement', str]]: + """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n + :param filter_loc: 用于筛选元素的查询语法 + :param timeout: 查找元素的超时时间 + :return: 兄弟元素或节点文本组成的列表 + """ + return self._get_brothers(filter_loc=filter_loc, direction='following', timeout=timeout) + + def befores(self, + filter_loc: Union[tuple, str] = '', + timeout: float = None) -> List[Union['DrissionElement', str]]: """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n :param filter_loc: 用于筛选元素的查询语法 :param timeout: 查找元素的超时时间 @@ -202,7 +220,9 @@ class DrissionElement(BaseElement): """ return self._get_brothers(filter_loc=filter_loc, direction='preceding', brother=False, timeout=timeout) - def afters(self, filter_loc: Union[tuple, str] = '', timeout: float = None): + def afters(self, + filter_loc: Union[tuple, str] = '', + timeout: float = None) -> List[Union['DrissionElement', str]]: """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n :param filter_loc: 用于筛选元素的查询语法 :param timeout: 查找元素的超时时间 @@ -215,7 +235,7 @@ class DrissionElement(BaseElement): filter_loc: Union[tuple, str] = '', direction: str = 'following', brother: bool = True, - timeout: float = .5) -> List['DrissionElement']: + timeout: float = .5) -> List[Union['DrissionElement', str]]: """按要求返回兄弟元素或节点组成的列表 \n :param index: 获取第几个,该参数不为None时只获取该编号的元素 :param filter_loc: 用于筛选元素的查询语法 @@ -238,15 +258,18 @@ class DrissionElement(BaseElement): raise ValueError('此css selector语法不受支持,请换成xpath。') loc = loc[1].lstrip('./') - if index: - loc = f'xpath:./{direction}{brother}::{loc}[{index}]' - else: - loc = f'xpath:./{direction}{brother}::{loc}' + loc = f'xpath:./{direction}{brother}::{loc}' nodes = self._ele(loc, timeout=timeout, single=False) nodes = [e for e in nodes if not (isinstance(e, str) and sub('[ \n\t\r]', '', e) == '')] - return nodes + if nodes and index is not None: + try: + return [nodes[index - 1]] + except IndexError: + return [] + else: + return nodes # ----------------以下属性或方法由后代实现---------------- @property diff --git a/DrissionPage/driver_element.py b/DrissionPage/driver_element.py index fe1c3b5..fb1096f 100644 --- a/DrissionPage/driver_element.py +++ b/DrissionPage/driver_element.py @@ -248,6 +248,101 @@ class DriverElement(DrissionElement): self._scroll = Scroll(self) return self._scroll + def parent(self, level_or_loc: Union[tuple, str, int] = 1) -> Union['DriverElement', None]: + """返回上面某一级父元素,可指定层数或用查询语法定位 \n + :param level_or_loc: 第几级父元素,或定位符 + :return: 上级元素对象 + """ + return super().parent(level_or_loc) + + def prev(self, + index: int = 1, + filter_loc: Union[tuple, str] = '', + timeout: float = 0) -> Union['DriverElement', str, None]: + """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n + :param index: 前面第几个查询结果元素 + :param filter_loc: 用于筛选元素的查询语法 + :param timeout: 查找元素的超时时间 + :return: 兄弟元素 + """ + return super().prev(index, filter_loc, timeout) + + def next(self, + index: int = 1, + filter_loc: Union[tuple, str] = '', + timeout: float = 0) -> Union['DriverElement', str, None]: + """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n + :param index: 后面第几个查询结果元素 + :param filter_loc: 用于筛选元素的查询语法 + :param timeout: 查找元素的超时时间 + :return: 兄弟元素 + """ + return super().next(index, filter_loc, timeout) + + def before(self, + index: int = 1, + filter_loc: Union[tuple, str] = '', + timeout: float = None) -> Union['DriverElement', str, None]: + """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n + :param index: 前面第几个查询结果元素 + :param filter_loc: 用于筛选元素的查询语法 + :param timeout: 查找元素的超时时间 + :return: 本元素前面的某个元素或节点 + """ + return super().before(index, filter_loc, timeout) + + def after(self, + index: int = 1, + filter_loc: Union[tuple, str] = '', + timeout: float = None) -> Union['DriverElement', str, None]: + """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n + :param index: 后面第几个查询结果元素 + :param filter_loc: 用于筛选元素的查询语法 + :param timeout: 查找元素的超时时间 + :return: 本元素后面的某个元素或节点 + """ + return super().after(index, filter_loc, timeout) + + def prevs(self, + filter_loc: Union[tuple, str] = '', + timeout: float = 0) -> List[Union['DriverElement', str]]: + """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n + :param filter_loc: 用于筛选元素的查询语法 + :param timeout: 查找元素的超时时间 + :return: 兄弟元素或节点文本组成的列表 + """ + return super().prevs(filter_loc, timeout) + + def nexts(self, + filter_loc: Union[tuple, str] = '', + timeout: float = 0) -> List[Union['DriverElement', str]]: + """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n + :param filter_loc: 用于筛选元素的查询语法 + :param timeout: 查找元素的超时时间 + :return: 兄弟元素或节点文本组成的列表 + """ + return super().nexts(filter_loc, timeout) + + def befores(self, + filter_loc: Union[tuple, str] = '', + timeout: float = None) -> List[Union['DriverElement', str]]: + """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n + :param filter_loc: 用于筛选元素的查询语法 + :param timeout: 查找元素的超时时间 + :return: 本元素前面的元素或节点组成的列表 + """ + return super().befores(filter_loc, timeout) + + def afters(self, + filter_loc: Union[tuple, str] = '', + timeout: float = None) -> List[Union['DriverElement', str]]: + """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n + :param filter_loc: 用于筛选元素的查询语法 + :param timeout: 查找元素的超时时间 + :return: 本元素后面的元素或节点组成的列表 + """ + return super().afters(filter_loc, timeout) + def left(self, index: int = 1, filter_loc: Union[tuple, str] = '') -> 'DriverElement': """获取网页上显示在当前元素左边的某个元素,可设置选取条件,可指定结果中第几个 \n :param index: 获取第几个 diff --git a/DrissionPage/session_element.py b/DrissionPage/session_element.py index 410673d..e383591 100644 --- a/DrissionPage/session_element.py +++ b/DrissionPage/session_element.py @@ -72,6 +72,101 @@ class SessionElement(DrissionElement): """返回未格式化处理的元素内文本""" return str(self._inner_ele.text_content()) + def parent(self, level_or_loc: Union[tuple, str, int] = 1) -> Union['SessionElement', None]: + """返回上面某一级父元素,可指定层数或用查询语法定位 \n + :param level_or_loc: 第几级父元素,或定位符 + :return: 上级元素对象 + """ + return super().parent(level_or_loc) + + def prev(self, + index: int = 1, + filter_loc: Union[tuple, str] = '', + timeout: float = 0) -> Union['SessionElement', str, None]: + """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n + :param index: 前面第几个查询结果元素 + :param filter_loc: 用于筛选元素的查询语法 + :param timeout: 查找元素的超时时间 + :return: 兄弟元素 + """ + return super().prev(index, filter_loc, timeout) + + def next(self, + index: int = 1, + filter_loc: Union[tuple, str] = '', + timeout: float = 0) -> Union['SessionElement', str, None]: + """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n + :param index: 后面第几个查询结果元素 + :param filter_loc: 用于筛选元素的查询语法 + :param timeout: 查找元素的超时时间 + :return: 兄弟元素 + """ + return super().next(index, filter_loc, timeout) + + def before(self, + index: int = 1, + filter_loc: Union[tuple, str] = '', + timeout: float = None) -> Union['SessionElement', str, None]: + """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n + :param index: 前面第几个查询结果元素 + :param filter_loc: 用于筛选元素的查询语法 + :param timeout: 查找元素的超时时间 + :return: 本元素前面的某个元素或节点 + """ + return super().before(index, filter_loc, timeout) + + def after(self, + index: int = 1, + filter_loc: Union[tuple, str] = '', + timeout: float = None) -> Union['SessionElement', str, None]: + """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n + :param index: 后面第几个查询结果元素 + :param filter_loc: 用于筛选元素的查询语法 + :param timeout: 查找元素的超时时间 + :return: 本元素后面的某个元素或节点 + """ + return super().after(index, filter_loc, timeout) + + def prevs(self, + filter_loc: Union[tuple, str] = '', + timeout: float = 0) -> List[Union['SessionElement', str]]: + """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n + :param filter_loc: 用于筛选元素的查询语法 + :param timeout: 查找元素的超时时间 + :return: 兄弟元素或节点文本组成的列表 + """ + return super().prevs(filter_loc, timeout) + + def nexts(self, + filter_loc: Union[tuple, str] = '', + timeout: float = 0) -> List[Union['SessionElement', str]]: + """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n + :param filter_loc: 用于筛选元素的查询语法 + :param timeout: 查找元素的超时时间 + :return: 兄弟元素或节点文本组成的列表 + """ + return super().nexts(filter_loc, timeout) + + def befores(self, + filter_loc: Union[tuple, str] = '', + timeout: float = None) -> List[Union['SessionElement', str]]: + """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n + :param filter_loc: 用于筛选元素的查询语法 + :param timeout: 查找元素的超时时间 + :return: 本元素前面的元素或节点组成的列表 + """ + return super().befores(filter_loc, timeout) + + def afters(self, + filter_loc: Union[tuple, str] = '', + timeout: float = None) -> List[Union['SessionElement', str]]: + """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n + :param filter_loc: 用于筛选元素的查询语法 + :param timeout: 查找元素的超时时间 + :return: 本元素后面的元素或节点组成的列表 + """ + return super().afters(filter_loc, timeout) + def attr(self, attr: str) -> Union[str, None]: """返回attribute属性值 \n :param attr: 属性名 diff --git a/DrissionPage/shadow_root_element.py b/DrissionPage/shadow_root_element.py index bd1fabd..d67c042 100644 --- a/DrissionPage/shadow_root_element.py +++ b/DrissionPage/shadow_root_element.py @@ -72,7 +72,9 @@ class ShadowRootElement(BaseElement): return self.parent_ele.ele(loc, timeout=0) - def next(self, index: int = 1, filter_loc: Union[tuple, str] = '') -> Union[DriverElement, None]: + def next(self, + index: int = 1, + filter_loc: Union[tuple, str] = '') -> Union[DriverElement, str, None]: """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n :param index: 第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -81,7 +83,28 @@ class ShadowRootElement(BaseElement): nodes = self.nexts(filter_loc=filter_loc) return nodes[index - 1] if nodes else None - def nexts(self, filter_loc: Union[tuple, str] = '') -> List[DriverElement]: + def before(self, + index: int = 1, + filter_loc: Union[tuple, str] = '') -> Union[DriverElement, str, None]: + """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n + :param index: 前面第几个查询结果元素 + :param filter_loc: 用于筛选元素的查询语法 + :return: 本元素前面的某个元素或节点 + """ + nodes = self.befores(filter_loc=filter_loc) + return nodes[index - 1] if nodes else None + + def after(self, index: int = 1, + filter_loc: Union[tuple, str] = '') -> Union[DriverElement, str, None]: + """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n + :param index: 后面第几个查询结果元素 + :param filter_loc: 用于筛选元素的查询语法 + :return: 本元素后面的某个元素或节点 + """ + nodes = self.afters(filter_loc=filter_loc) + return nodes[index - 1] if nodes else None + + def nexts(self, filter_loc: Union[tuple, str] = '') -> List[Union[DriverElement, str]]: """返回后面所有兄弟元素或节点组成的列表 \n :param filter_loc: 用于筛选元素的查询语法 :return: DriverElement对象组成的列表 @@ -94,6 +117,29 @@ class ShadowRootElement(BaseElement): xpath = f'xpath:./{loc}' return self.parent_ele.eles(xpath, timeout=0.1) + def befores(self, filter_loc: Union[tuple, str] = '') -> List[Union[DriverElement, str]]: + """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n + :param filter_loc: 用于筛选元素的查询语法 + :return: 本元素前面的元素或节点组成的列表 + """ + loc = get_loc(filter_loc, True) + if loc[0] == 'css selector': + raise ValueError('此css selector语法不受支持,请换成xpath。') + + loc = loc[1].lstrip('./') + xpath = f'xpath:./preceding::{loc}' + return self.parent_ele.eles(xpath, timeout=0.1) + + def afters(self, filter_loc: Union[tuple, str] = '') -> List[Union[DriverElement, str]]: + """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n + :param filter_loc: 用于筛选元素的查询语法 + :return: 本元素后面的元素或节点组成的列表 + """ + eles1 = self.nexts(filter_loc) + loc = get_loc(filter_loc, True)[1].lstrip('./') + xpath = f'xpath:./following::{loc}' + return eles1 + self.parent_ele.eles(xpath, timeout=0.1) + def ele(self, loc_or_str: Union[Tuple[str, str], str], timeout: float = None) -> Union[DriverElement, str, None]: diff --git a/docs/使用方法/查找页面元素.md b/docs/使用方法/查找页面元素.md index 6b2d78d..163fb76 100644 --- a/docs/使用方法/查找页面元素.md +++ b/docs/使用方法/查找页面元素.md @@ -882,6 +882,31 @@ eles = ele1.nears() divs = ele1.nears('tag:div') ``` +# `ShadowRootElement`相关查找 + +本库把 shadow-root 也作为元素对象看待,是为`ShadowRootElement`对象。对`ShadowRootElement`对象可与普通元素一样查找下级元素和 DOM 内相对定位,但不能用页面布局相对定位。 +对`ShadowRootElement`对象进行相对定位时,把它看作其父对象内部的第一个对象,其余定位逻辑与普通对象一致。 + +!> **注意:**
+如果`ShadowRootElement`元素的下级元素中有其它`ShadowRootElement`元素,那这些下级`ShadowRootElement`元素内部是无法直接通过定位语句查找到的,只能先定位到其父元素,再用`shadow-root`属性获取。 + +```python +# 获取一个 shadow-root 元素 +sr_ele = page.ele('#app').shadow_root + +# 在该元素下查找下级元素 +ele1 = sr_ele.ele('tag:div') + +# 用相对定位获取其它元素 +ele1 = sr_ele.parent(2) +ele1 = sr_ele.next(1, 'tag:div') +ele1 = sr_ele.after(1, 'tag:div') +eles = sr_ele.nexts('tag:div') + +# 定位下级元素中的 shadow+-root 元素 +sr_ele2 = sr_ele.ele('tag:div').shadow_root +``` + # 简化写法 为进一步精简代码,对语法进行了精简 diff --git a/setup.py b/setup.py index 0760d42..056bf37 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ with open("README.md", "r", encoding='utf-8') as fh: setup( name="DrissionPage", - version="2.4.1", + version="2.4.2", author="g1879", author_email="g1879@qq.com", description="A module that integrates selenium and requests session, encapsulates common page operations.",