From 9863aea69cb26e670c45b6843a23b884dbc5c413 Mon Sep 17 00:00:00 2001 From: g1879 Date: Wed, 2 Sep 2020 16:33:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=94=A8=E6=96=87=E6=9C=AC?= =?UTF-8?q?=E6=9F=A5=E6=89=BEshadow-root=E4=B8=AD=E5=85=83=E7=B4=A0?= =?UTF-8?q?=EF=BC=8C=E5=9F=BA=E6=9C=AC=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/shadow_root_element.py | 124 ++++++++++++++++++++++------ 1 file changed, 97 insertions(+), 27 deletions(-) diff --git a/DrissionPage/shadow_root_element.py b/DrissionPage/shadow_root_element.py index 1e1e036..ab2a098 100644 --- a/DrissionPage/shadow_root_element.py +++ b/DrissionPage/shadow_root_element.py @@ -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') - 返回第一个

子元素 \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') - 返回所有

子元素 \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