支持用文本查找shadow-root中元素,基本完成

This commit is contained in:
g1879 2020-09-02 16:33:55 +08:00
parent b3d4cc67f4
commit 9863aea69c

View File

@ -60,24 +60,76 @@ class ShadowRootElement(DrissionElement):
def ele(self,
loc_or_str: Union[tuple, str],
mode: str = None,
mode: str = 'single',
timeout: float = None,
show_errmsg: bool = False):
"""返回当前元素下级符合条件的子元素,默认返回第一个 \n
示例 \n
- 用loc元组查找 \n
ele.ele((By.CLASS_NAME, 'ele_class')) - 返回第一个class为ele_class的子元素 \n
- 用查询字符串查找 \n
查找方式属性tag name和属性文本css selector \n
其中@表示属性=表示精确匹配:表示模糊匹配无控制字符串时默认搜索该字符串 \n
ele.ele('@class:ele_class') - 返回第一个class含有ele_class的子元素 \n
ele.ele('@name=ele_name') - 返回第一个name等于ele_name的子元素 \n
ele.ele('@placeholder') - 返回第一个带placeholder属性的子元素 \n
ele.ele('tag:p') - 返回第一个<p>子元素 \n
ele.ele('tag:div@class:ele_class') - 返回第一个class含有ele_class的div子元素 \n
ele.ele('tag:div@class=ele_class') - 返回第一个class等于ele_class的div子元素 \n
ele.ele('tag:div@text():some_text') - 返回第一个文本含有some_text的div子元素 \n
ele.ele('tag:div@text()=some_text') - 返回第一个文本等于some_text的div子元素 \n
ele.ele('text:some_text') - 返回第一个文本含有some_text的子元素 \n
ele.ele('some_text') - 返回第一个文本含有some_text的子元素等价于上一行 \n
ele.ele('text=some_text') - 返回第一个文本等于some_text的子元素 \n
ele.ele('css:div.ele_class') - 返回第一个符合css selector的子元素 \n
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param mode: 'single' 'all'对应查找一个或全部
:param timeout: 查找元素超时时间
:param show_errmsg: 出现异常时是否打印信息
:return: DriverElement对象
"""
if isinstance(loc_or_str, str):
loc_or_str = get_css_from_str(loc_or_str)
elif isinstance(loc_or_str, tuple) and len(loc_or_str) == 2:
pass
if loc_or_str[0] == 'xpath':
raise ValueError('不支持xpath')
else:
raise ValueError('Argument loc_or_str can only be tuple or str.')
if loc_or_str[0] == 'xpath':
raise ValueError('不支持xpath')
timeout = timeout or self.timeout
return execute_driver_find(self.inner_ele, loc_or_str, mode, show_errmsg, timeout)
if loc_or_str[0] == 'css selector':
return execute_driver_find(self.inner_ele, loc_or_str, mode, show_errmsg, timeout)
elif loc_or_str[0] == 'text':
return self._find_eles_by_text(loc_or_str[1], loc_or_str[2], loc_or_str[3], mode)
def eles(self,
loc_or_str: Union[tuple, str],
timeout: float = None,
show_errmsg: bool = False):
"""返回当前元素下级所有符合条件的子元素 \n
示例 \n
- 用loc元组查找 \n
ele.eles((By.CLASS_NAME, 'ele_class')) - 返回所有class为ele_class的子元素 \n
- 用查询字符串查找 \n
查找方式属性tag name和属性文本css selector \n
其中@表示属性=表示精确匹配:表示模糊匹配无控制字符串时默认搜索该字符串 \n
ele.eles('@class:ele_class') - 返回所有class含有ele_class的子元素 \n
ele.eles('@name=ele_name') - 返回所有name等于ele_name的子元素 \n
ele.eles('@placeholder') - 返回所有带placeholder属性的子元素 \n
ele.eles('tag:p') - 返回所有<p>子元素 \n
ele.eles('tag:div@class:ele_class') - 返回所有class含有ele_class的div子元素 \n
ele.eles('tag:div@class=ele_class') - 返回所有class等于ele_class的div子元素 \n
ele.eles('tag:div@text():some_text') - 返回所有文本含有some_text的div子元素 \n
ele.eles('tag:div@text()=some_text') - 返回所有文本等于some_text的div子元素 \n
ele.eles('text:some_text') - 返回所有文本含有some_text的子元素 \n
ele.eles('some_text') - 返回所有文本含有some_text的子元素等价于上一行 \n
ele.eles('text=some_text') - 返回所有文本等于some_text的子元素 \n
ele.eles('css:div.ele_class') - 返回所有符合css selector的子元素 \n
:param loc_or_str: 元素的定位信息可以是loc元组或查询字符串
:param timeout: 查找元素超时时间
:param show_errmsg: 出现异常时是否打印信息
:return: DriverElement对象组成的列表
"""
return self.ele(loc_or_str, mode='all', show_errmsg=show_errmsg, timeout=timeout)
def run_script(self, script: str, *args) -> Any:
@ -100,21 +152,31 @@ class ShadowRootElement(DrissionElement):
except:
return False
def _find_eles_by_text(self, text: str, mode: str = 'single', match: str = 'exact'):
eles = self.run_script('return arguments[0].querySelectorAll("*")')
def _find_eles_by_text(self, text: str, tag: str = '', match: str = 'exact', mode: str = 'single'):
"""根据文本获取页面元素 \n
:param text: 文本字符串
:param tag: tag name
:param match: 'exact' 'fuzzy'对应精确或模糊匹配
:param mode: 'single' 'all'对应匹配一个或全部
:return: 返回DriverElement对象或组成的列表
"""
eles = self.run_script('return arguments[0].querySelectorAll("*")') # 获取所有元素
from .driver_element import DriverElement
results = []
for ele in eles:
for ele in eles: # 遍历所有元素,找到符合条件的
if tag and tag != ele.tag_name:
continue
txt = self.driver.execute_script(
'if(arguments[0].firstChild!=null){return arguments[0].firstChild.nodeValue}', ele)
if match == 'exact':
txt = txt or ''
if text == '' or match == 'exact': # 匹配没有文本的元素或精确匹配
if text == txt:
if mode == 'single':
return DriverElement(ele)
elif mode == 'all':
results.append(DriverElement(ele))
elif match == 'fuzzy':
if txt and text in txt:
elif match == 'fuzzy': # 模糊匹配
if text in txt:
if mode == 'single':
return DriverElement(ele)
elif mode == 'all':
@ -123,18 +185,24 @@ class ShadowRootElement(DrissionElement):
def get_css_from_str(loc: str) -> tuple:
"""处理元素查找语句 \n
查找方式属性tag name及属性css selector \n
"""处理元素查找语句 \n
查找方式属性tag name及属性文本css selector \n
=表示精确匹配:表示模糊匹配无控制字符串时默认搜索该字符串 \n
示例 \n
@class:ele_class - class含有ele_class的元素 \n
@class=ele_class - class等于ele_class的元素 \n
@class - 带class属性的元素 \n
tag:div - div元素 \n
tag:div@class:ele_class - class含有ele_class的div元素 \n
tag:div@class=ele_class - class等于ele_class的div元素 \n
css:div.ele_class \n
=表示精确匹配:表示模糊匹配无控制字符串时默认搜索该字符串 \n
示例 \n
@class:ele_class - class含有ele_class的元素 \n
@class=ele_class - class等于ele_class的元素 \n
@class - 带class属性的元素 \n
tag:div - div元素 \n
tag:div@class:ele_class - class含有ele_class的div元素 \n
tag:div@class=ele_class - class等于ele_class的div元素 \n
tag:div@text():search_text - 文本含有search_text的div元素 \n
tag:div@text()=search_text - 文本等于search_text的div元素 \n
text:search_text - 文本含有search_text的元素 \n
text=search_text - 文本等于search_text的元素 \n
css:div.ele_class \n
"""
loc_by = 'css selector'
if loc.startswith('@'): # 根据属性查找
r = re_SPLIT(r'([:=])', loc[1:], maxsplit=1)
if len(r) == 3:
@ -150,17 +218,19 @@ def get_css_from_str(loc: str) -> tuple:
r = re_SPLIT(r'([:=])', at_lst[1], maxsplit=1)
if len(r) == 3:
if r[0] == 'text()':
raise ValueError('不支持按文本查找')
match = 'exact' if r[1] == '=' else 'fuzzy'
return 'text', r[2], at_lst[0], match
mode = '=' if r[1] == '=' else '*='
loc_str = f'{at_lst[0]}[{r[0]}{mode}"{r[2]}"]'
else:
loc_str = f'{at_lst[0]}[{r[0]}]'
elif loc.startswith(('css=', 'css:')): # 用css selector查找
loc_str = loc[4:]
elif loc.startswith(('text=', 'text:')): # 根据文本查找
raise ValueError('不支持按文本查找')
elif loc.startswith(('xpath=', 'xpath:')): # 用xpath查找
raise ValueError('不支持xpath')
else:
raise ValueError('不支持的查询语句')
return 'css selector', loc_str
elif loc.startswith(('text=', 'text:')): # 根据文本查找
match = 'exact' if loc[4] == '=' else 'fuzzy'
return 'text', loc[5:], '', match
else: # 根据文本模糊查找
return 'text', loc, '', 'fuzzy'
return loc_by, loc_str