修改prev,next,parent用法,和旧版不兼容

This commit is contained in:
g1879 2021-12-19 00:58:43 +08:00
parent 32194aa886
commit 8784f7dd48
4 changed files with 77 additions and 84 deletions

View File

@ -55,22 +55,23 @@ class BaseElement(BaseParser):
def inner_ele(self) -> Union[WebElement, HtmlElement]: def inner_ele(self) -> Union[WebElement, HtmlElement]:
return self._inner_ele return self._inner_ele
@property def next(self, index: int = 1):
def next(self): """返回后面的一个兄弟元素,可指定第几个 \n
"""返回后一个兄弟元素""" :param index: 后面第几个兄弟元素
return self.nexts() :return: 兄弟元素
"""
nexts = self.nexts(total=1, begin=index)
return nexts[0] if nexts else None
# ----------------以下属性或方法由后代实现---------------- # ----------------以下属性或方法由后代实现----------------
@property @property
def tag(self): def tag(self):
return return
@property def parent(self, level: int = 1):
def parent(self): pass
return
@property def prev(self, index: int = 1):
def prev(self):
return return
@property @property
@ -78,22 +79,25 @@ class BaseElement(BaseParser):
return True return True
@abstractmethod @abstractmethod
def nexts(self, num: int = 1): def nexts(self, total: int = None, begin: int = 1):
pass pass
class DrissionElement(BaseElement): class DrissionElement(BaseElement):
"""DriverElement 和 SessionElement的基类但不是ShadowRootElement的基类""" """DriverElement 和 SessionElement的基类但不是ShadowRootElement的基类"""
@property @abstractmethod
def parent(self): def parent(self, level: int = 1):
"""返回父级元素""" """返回父级元素"""
return self.parents() pass
@property def prev(self, index: int = 1):
def prev(self): """返回前面的一个兄弟元素,可指定第几个 \n
"""返回前一个兄弟元素""" :param index: 前面第几个
return self.prevs() :return: 兄弟元素
"""
prevs = self.prevs(total=1, begin=index)
return prevs[0] if prevs else None
@property @property
def link(self) -> str: def link(self) -> str:
@ -125,60 +129,57 @@ class DrissionElement(BaseElement):
else: else:
texts = [x if isinstance(x, str) else x.text for x in self.eles('xpath:./text() | *')] texts = [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('[\n\t ]', '', x) != ''] return [format_html(x.strip(' ').rstrip('\n')) for x in texts if x and sub('[\r\n\t ]', '', x) != '']
def nexts(self, num: int = 1, mode: str = 'ele'): def nexts(self, total: int = None, begin: int = 1, mode: str = 'ele'):
"""返回后面第num个兄弟元素或节点 \n """返回后面若干个兄弟元素或节点组成的列表total为None返回所有 \n
:param num: 后面第几个兄弟元素或节点 :param total: 获取多少个元素或节点
:param begin: 从第几个开始获取从1起
:param mode: 'ele', 'node' 'text'匹配元素节点或文本节点 :param mode: 'ele', 'node' 'text'匹配元素节点或文本节点
:return: SessionElement对象 :return: SessionElement对象
""" """
return self._get_brother(num, mode, 'next') return self._get_brothers(begin=begin, total=total, mode=mode, direction='next')
def prevs(self, num: int = 1, mode: str = 'ele'): def prevs(self, total: int = None, begin: int = 1, mode: str = 'ele'):
"""返回前面第num个兄弟元素或节点 \n """返回前面若干个兄弟元素或节点组成的列表total为None返回所有 \n
:param num: 前面第几个兄弟元素或节点 :param total: 获取多少个元素或节点
:param begin: 从第几个开始获取从1起
:param mode: 'ele', 'node' 'text'匹配元素节点或文本节点 :param mode: 'ele', 'node' 'text'匹配元素节点或文本节点
:return: SessionElement对象 :return: SessionElement对象
""" """
return self._get_brother(num, mode, 'prev') return self._get_brothers(begin=begin, total=total, mode=mode, direction='prev')
def _get_brother(self, num: int = 1, mode: str = 'ele', direction: str = 'next'): def _get_brothers(self, begin: int = 1, total: int = None, mode: str = 'ele', direction: str = 'next'):
"""返回前面第num个兄弟节点或元素 \n """按要求返回兄弟元素或节点组成的列表 \n
:param num: 前面第几个兄弟节点或元素 :param begin: 从第几个兄弟节点或元素开始
:param total: 获取多少个
:param mode: 'ele', 'node' 'text'匹配元素节点或文本节点 :param mode: 'ele', 'node' 'text'匹配元素节点或文本节点
:param direction: 'next' 'prev'查找的方向 :param direction: 'next' 'prev'查找的方向
:return: DriverElement对象或字符串 :return: DriverElement对象或字符串
""" """
# 查找节点的类型 # 查找节点的类型
if mode == 'ele': node_txt = {'ele': '*', 'node': 'node()', 'text': 'text()'}.get(mode)
node_txt = '*' if not node_txt:
elif mode == 'node':
node_txt = 'node()'
elif mode == 'text':
node_txt = 'text()'
else:
raise ValueError(f"mode参数只能是'node''ele''text',现在是:'{mode}'") raise ValueError(f"mode参数只能是'node''ele''text',现在是:'{mode}'")
# 查找节点的方向 # 查找节点的方向
if direction == 'next': direction_txt = {'next': 'following', 'prev': 'preceding'}.get(direction)
direction_txt = 'following' if not direction_txt:
elif direction == 'prev':
direction_txt = 'preceding'
else:
raise ValueError(f"direction参数只能是'next''prev',现在是:'{direction}'") raise ValueError(f"direction参数只能是'next''prev',现在是:'{direction}'")
timeout = 0 if direction == 'prev' else .5 timeout = 0 if direction == 'prev' else .5
# 获取节点 # 获取所有节点
ele_or_node = self._ele(f'xpath:./{direction_txt}-sibling::{node_txt}[{num}]', timeout=timeout) nodes = self._ele(f'xpath:./{direction_txt}-sibling::{node_txt}', timeout=timeout, single=False)
# 跳过元素间的换行符 if direction == 'next':
while isinstance(ele_or_node, str) and sub('[\n\t ]', '', ele_or_node) == '': end = None if not total or total >= len(nodes) else begin + total - 1
num += 1 begin -= 1
ele_or_node = self._ele(f'xpath:./{direction_txt}-sibling::{node_txt}[{num}]', timeout=timeout) else:
begin = None if not total or total >= len(nodes) else begin - total - 1
end = None
return ele_or_node return [e for e in nodes[begin:end] if not (isinstance(e, str) and sub('[ \n\t\r]', '', e) == '')]
# ----------------以下属性或方法由后代实现---------------- # ----------------以下属性或方法由后代实现----------------
@property @property
@ -193,9 +194,9 @@ class DrissionElement(BaseElement):
def raw_text(self): def raw_text(self):
return return
@abstractmethod # @abstractmethod
def parents(self, num: int = 1): # def parents(self, num: int = 1):
pass # pass
@abstractmethod @abstractmethod
def attr(self, attr: str): def attr(self, attr: str):

View File

@ -91,17 +91,12 @@ class DriverElement(DrissionElement):
"""返回未格式化处理的元素内文本""" """返回未格式化处理的元素内文本"""
return self.inner_ele.get_attribute('innerText') return self.inner_ele.get_attribute('innerText')
@property def parent(self, level: int = 1) -> 'DriverElement':
def parent(self): """返回上面第level级父元素 \n
"""返回父级元素""" :param level: 第几级父元素
return self.parents()
def parents(self, num: int = 1):
"""返回上面第num级父元素 \n
:param num: 第几级父元素
:return: DriverElement对象 :return: DriverElement对象
""" """
loc = 'xpath', f'.{"/.." * num}' loc = 'xpath', f'.{"/.." * level}'
return self.ele(loc, timeout=0) return self.ele(loc, timeout=0)
def attr(self, attr: str) -> str: def attr(self, attr: str) -> str:

View File

@ -70,17 +70,12 @@ class SessionElement(DrissionElement):
"""返回未格式化处理的元素内文本""" """返回未格式化处理的元素内文本"""
return str(self._inner_ele.text_content()) return str(self._inner_ele.text_content())
@property def parent(self, level: int = 1) -> 'SessionElement':
def parent(self): """返回上面第level级父元素 \n
"""返回父级元素""" :param level: 第几级父元素
return self.parents()
def parents(self, num: int = 1):
"""返回上面第num级父元素 \n
:param num: 第几级父元素
:return: SessionElement对象 :return: SessionElement对象
""" """
return self.ele(f'xpath:..{"/.." * (num - 1)}') return self.ele(f'xpath:..{"/.." * (level - 1)}')
def attr(self, attr: str) -> Union[str, None]: def attr(self, attr: str) -> Union[str, None]:
"""返回attribute属性值 \n """返回attribute属性值 \n

View File

@ -45,26 +45,28 @@ class ShadowRootElement(BaseElement):
"""返回内部的html文本""" """返回内部的html文本"""
return self.inner_ele.get_attribute('innerHTML') return self.inner_ele.get_attribute('innerHTML')
@property def parent(self, level: int = 1) -> DriverElement:
def parent(self) -> DriverElement: """返回上面第level级父元素 \n
"""shadow-root所依赖的父元素""" :param level: 第几级父元素
return self.parent_ele
def parents(self, num: int = 1) -> DriverElement:
"""返回上面第num级父元素 \n
:param num: 第几级父元素
:return: DriverElement对象 :return: DriverElement对象
""" """
loc = 'xpath', f'.{"/.." * (num - 1)}' if level == 1:
return self.parent_ele.ele(loc, timeout=0.1) return self.parent_ele
else:
loc = 'xpath', f'.{"/.." * (level - 1)}'
return self.parent_ele.ele(loc, timeout=0.1)
def nexts(self, num: int = 1) -> DriverElement: def nexts(self, total: int = None, begin: int = 1) -> DriverElement:
"""返回后面第num个兄弟元素 \n """返回后面若干个兄弟元素或节点组成的列表total为None返回所有 \n
:param num: 后面第几个兄弟元素 :param total: 获取多少个元素或节点
:param begin: 从第几个开始获取从1起
:return: DriverElement对象 :return: DriverElement对象
""" """
loc = 'css selector', f':nth-child({num})' loc = 'css selector', f':nth-child(n)'
return self.parent_ele.ele(loc, timeout=0.1) eles = self.parent_ele.eles(loc, timeout=0.1)
end = None if not total or total >= len(eles) else begin + total - 1
return eles[begin - 1:end]
def ele(self, def ele(self,
loc_or_str: Union[Tuple[str, str], str], loc_or_str: Union[Tuple[str, str], str],