From ca9d2124d93363f52b24733123bbfeb40144c2f7 Mon Sep 17 00:00:00 2001 From: g1879 Date: Sun, 30 Aug 2020 20:09:48 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AF=B9shadow-dom=E6=94=AF?= =?UTF-8?q?=E6=8C=81=EF=BC=8C=E5=88=9D=E6=AD=A5=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 | 100 +++++++++++++++++++--------- 1 file changed, 68 insertions(+), 32 deletions(-) diff --git a/DrissionPage/shadow_root_element.py b/DrissionPage/shadow_root_element.py index f75cf79..36c5a84 100644 --- a/DrissionPage/shadow_root_element.py +++ b/DrissionPage/shadow_root_element.py @@ -1,11 +1,12 @@ #!/usr/bin/env python # -*- coding:utf-8 -*- from html import unescape +from re import split as re_SPLIT from typing import Union, Any from selenium.webdriver.remote.webelement import WebElement -from .common import DrissionElement, get_loc_from_str +from .common import DrissionElement from .driver_element import execute_driver_find @@ -16,6 +17,9 @@ class ShadowRootElement(DrissionElement): self.timeout = timeout self._driver = inner_ele.parent + def __repr__(self): + return f'' + @property def driver(self): """返回控制元素的WebDriver对象""" @@ -41,18 +45,18 @@ class ShadowRootElement(DrissionElement): loc = 'xpath', f'.{"/.." * (num - 1)}' return self.parent_ele.ele(loc, timeout=0.01, show_errmsg=False) - # @property - # def next(self): - # """返回后一个兄弟元素""" - # return - # - # def nexts(self, num: int = 1): - # """返回后面第num个兄弟元素 \n - # :param num: 后面第几个兄弟元素 - # :return: DriverElement对象 - # """ - # # loc = 'xpath', f'./following-sibling::*[{num}]' - # return + @property + def next(self): + """返回后一个兄弟元素""" + return self.nexts() + + def nexts(self, num: int = 1): + """返回后面第num个兄弟元素 \n + :param num: 后面第几个兄弟元素 + :return: DriverElement对象 + """ + loc = 'css selector', f':nth-child({num})' + return self.parent_ele.ele(loc) def ele(self, loc_or_str: Union[tuple, str], @@ -60,33 +64,21 @@ class ShadowRootElement(DrissionElement): timeout: float = None, show_errmsg: bool = False): if isinstance(loc_or_str, str): - loc_or_str = get_loc_from_str(loc_or_str) + loc_or_str = get_css_from_str(loc_or_str) elif isinstance(loc_or_str, tuple) and len(loc_or_str) == 2: pass else: raise ValueError('Argument loc_or_str can only be tuple or str.') - if loc_or_str[0] == 'xpath': - # 确保查询语句最前面是. - # loc_str = loc_or_str[1] if loc_or_str[1].startswith(('.', '/')) else f'.//{loc_or_str[1]}' - # loc_str = loc_str if loc_str.startswith('.') else f'.{loc_str}' - loc_str = loc_or_str[1] - # print(self.inner_ele) - # print(loc_str) - js = f'''return document.evaluate('{loc_str}', arguments[0]).iterateNext()''' # - print(js) - return self.inner_ele.parent.execute_script(js, self.inner_ele) - # return self.run_script(js) - # else: - # if loc_or_str[1].lstrip().startswith('>'): - # loc_or_str = loc_or_str[0], f'{self.css_path}{loc_or_str[1]}' - + raise ValueError('不支持xpath') timeout = timeout or self.timeout - return execute_driver_find(self.inner_ele, loc_or_str, mode, show_errmsg, timeout) - def eles(self, loc: Union[tuple, str], show_errmsg: bool = True): - pass + def eles(self, + loc_or_str: Union[tuple, str], + timeout: float = None, + show_errmsg: bool = False): + return self.ele(loc_or_str, mode='all', show_errmsg=show_errmsg, timeout=timeout) def run_script(self, script: str, *args) -> Any: """执行js代码,传入自己为第一个参数 \n @@ -107,3 +99,47 @@ class ShadowRootElement(DrissionElement): return True except: return False + + +def get_css_from_str(loc: str) -> tuple: + """处理元素查找语句 \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 + """ + if loc.startswith('@'): # 根据属性查找 + r = re_SPLIT(r'([:=])', loc[1:], maxsplit=1) + if len(r) == 3: + mode = '=' if r[1] == '=' else '*=' + loc_str = f'*[{r[0]}{mode}{r[2]}]' + else: + loc_str = f'*[{loc[1:]}]' + elif loc.startswith(('tag=', 'tag:')): # 根据tag name查找 + if '@' not in loc[4:]: + loc_str = f'{loc[4:]}' + else: + at_lst = loc[4:].split('@', maxsplit=1) + r = re_SPLIT(r'([:=])', at_lst[1], maxsplit=1) + if len(r) == 3: + if r[0] == 'text()': + raise ValueError('不支持按文本查找') + 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