diff --git a/DrissionPage/chromium_base.py b/DrissionPage/chromium_base.py index 212708d..f2c2f7f 100644 --- a/DrissionPage/chromium_base.py +++ b/DrissionPage/chromium_base.py @@ -8,6 +8,7 @@ from json import loads, JSONDecodeError from os import sep from pathlib import Path from time import perf_counter, sleep, time +from urllib.parse import urlparse from warnings import warn from requests import Session @@ -990,11 +991,10 @@ class ChromiumBaseSetter(object): result_cookies = [] for cookie in cookies: if not cookie.get('domain', None): - continue - c = {'value': '' if cookie['value'] is None else cookie['value'], - 'name': cookie['name'], - 'domain': cookie['domain']} - result_cookies.append(c) + cookie['domain'] = urlparse(self._page.url).netloc + result_cookies.append({'value': '' if cookie['value'] is None else cookie['value'], + 'name': cookie['name'], + 'domain': cookie['domain']}) self._page.run_cdp_loaded('Network.setCookies', cookies=result_cookies) def upload_files(self, files): @@ -1192,3 +1192,4 @@ class PageScrollSetter(object): raise TypeError('on_off必须为bool。') b = 'smooth' if on_off else 'auto' self._scroll._driver.run_js(f'document.documentElement.style.setProperty("scroll-behavior","{b}");') + self._scroll._wait_complete = on_off diff --git a/DrissionPage/chromium_page.py b/DrissionPage/chromium_page.py index 3b19484..c4bfea2 100644 --- a/DrissionPage/chromium_page.py +++ b/DrissionPage/chromium_page.py @@ -402,6 +402,11 @@ class ChromiumTabRect(object): def __init__(self, page): self._page = page + @property + def window_state(self): + """返回窗口状态:normal、fullscreen、maximized、 minimized""" + return self._get_browser_rect()['windowState'] + @property def browser_location(self): """返回浏览器在屏幕上的坐标,左上角为(0, 0)""" diff --git a/DrissionPage/chromium_page.pyi b/DrissionPage/chromium_page.pyi index aa44e5b..338fbaa 100644 --- a/DrissionPage/chromium_page.pyi +++ b/DrissionPage/chromium_page.pyi @@ -116,6 +116,9 @@ class ChromiumTabRect(object): def __init__(self, page: ChromiumPage): self._page: ChromiumPage = ... + @property + def window_state(self) -> str: ... + @property def browser_location(self) -> Tuple[int, int]: ... diff --git a/DrissionPage/commons/browser.py b/DrissionPage/commons/browser.py index 8ea4e13..f35dfd3 100644 --- a/DrissionPage/commons/browser.py +++ b/DrissionPage/commons/browser.py @@ -99,7 +99,7 @@ def set_prefs(opt): prefs = opt.preferences del_list = opt._prefs_to_del else: - prefs = opt.experimental_options.get('prefs', None) + prefs = opt.experimental_options.get('prefs', []) del_list = [] if not opt.user_data_path: diff --git a/DrissionPage/commons/locator.py b/DrissionPage/commons/locator.py index 1ffeb2a..d3288cf 100644 --- a/DrissionPage/commons/locator.py +++ b/DrissionPage/commons/locator.py @@ -36,19 +36,19 @@ def get_loc(loc, translate_css=False): def str_to_loc(loc): """处理元素查找语句 - 查找方式:属性、tag name及属性、文本、xpath、css selector、id、class - @表示属性,.表示class,#表示id,=表示精确匹配,:表示模糊匹配,无控制字符串时默认搜索该字符串 + :param loc: 查找语法字符串 + :return: 匹配符元组 """ loc_by = 'xpath' if loc.startswith('.'): - if loc.startswith(('.=', '.:',)): + if loc.startswith(('.=', '.:', '.^', '.$')): loc = loc.replace('.', '@class', 1) else: loc = loc.replace('.', '@class=', 1) elif loc.startswith('#'): - if loc.startswith(('#=', '#:',)): + if loc.startswith(('#=', '#:', '#^', '#$')): loc = loc.replace('#', '@id', 1) else: loc = loc.replace('#', '@id=', 1) @@ -56,7 +56,7 @@ def str_to_loc(loc): elif loc.startswith(('t:', 't=')): loc = f'tag:{loc[2:]}' - elif loc.startswith(('tx:', 'tx=')): + elif loc.startswith(('tx:', 'tx=', 'tx^', 'tx$')): loc = f'text{loc[2:]}' # ------------------------------------------------------------------ @@ -87,9 +87,17 @@ def str_to_loc(loc): # 根据文本查找 elif loc.startswith('text='): loc_str = f'//*[text()={_make_search_str(loc[5:])}]' + elif loc.startswith('text:') and loc != 'text:': loc_str = f'//*/text()[contains(., {_make_search_str(loc[5:])})]/..' + elif loc.startswith('text^') and loc != 'text^': + loc_str = f'//*/text()[starts-with(., {_make_search_str(loc[5:])})]/..' + + elif loc.startswith('text$') and loc != 'text$': + loc_str = f'//*/text()[substring(., string-length(.) - string-length({_make_search_str(loc[5:])}) +1) = ' \ + f'{_make_search_str(loc[5:])}]/..' + # 用xpath查找 elif loc.startswith(('xpath:', 'xpath=')) and loc not in ('xpath:', 'xpath='): loc_str = loc[6:] @@ -100,7 +108,7 @@ def str_to_loc(loc): elif loc.startswith(('css:', 'css=')) and loc not in ('css:', 'css='): loc_by = 'css selector' loc_str = loc[4:] - elif loc.startswith(('c:', 'c=')) and loc not in ('c:', 'c='): + elif loc.startswith(('c:', 'cx=')) and loc not in ('c:', 'cx='): loc_by = 'css selector' loc_str = loc[2:] @@ -126,24 +134,44 @@ def _make_single_xpath_str(tag: str, text: str) -> str: arg_str = 'not(@*)' else: - r = split(r'([:=])', text, maxsplit=1) + r = split(r'([:=$^])', text, maxsplit=1) len_r = len(r) len_r0 = len(r[0]) if len_r != 3 and len_r0 > 1: arg_str = 'normalize-space(text())' if r[0] in ('@text()', '@tx()') else f'{r[0]}' elif len_r == 3 and len_r0 > 1: - if r[1] == '=': # 精确查找 + symbol = r[1] + if symbol == '=': # 精确查找 arg = '.' if r[0] in ('@text()', '@tx()') else r[0] arg_str = f'{arg}={_make_search_str(r[2])}' - else: # 模糊查找 + elif symbol == '^': # 开头开头 + if r[0] in ('@text()', '@tx()'): + txt_str = f'/text()[starts-with(., {_make_search_str(r[2])})]/..' + arg_str = '' + else: + arg_str = f"starts-with({r[0]},{_make_search_str(r[2])})" + + elif symbol == '$': # 匹配结尾 + if r[0] in ('@text()', '@tx()'): + txt_str = f'/text()[substring(., string-length(.) - string-length({_make_search_str(r[2])}) +1) ' \ + f'= {_make_search_str(r[2])}]/..' + arg_str = '' + else: + arg_str = f'substring({r[0]}, string-length({r[0]}) - string-length({_make_search_str(r[2])}) +1)' \ + f' = {_make_search_str(r[2])}' + + elif symbol == ':': # 模糊查找 if r[0] in ('@text()', '@tx()'): txt_str = f'/text()[contains(., {_make_search_str(r[2])})]/..' arg_str = '' else: arg_str = f"contains({r[0]},{_make_search_str(r[2])})" + else: + raise ValueError(f'符号不正确:{symbol}') + if arg_str: arg_list.append(arg_str) arg_str = ' and '.join(arg_list) @@ -161,7 +189,7 @@ def _make_multi_xpath_str(tag: str, text: str, _and: bool = True) -> str: args = text.split('@@') if _and else text.split('@|') for arg in args[1:]: - r = split(r'([:=])', arg, maxsplit=1) + r = split(r'([:=$^])', arg, maxsplit=1) arg_str = '' len_r = len(r) @@ -176,11 +204,23 @@ def _make_multi_xpath_str(tag: str, text: str, _and: bool = True) -> str: elif len_r == 3: # 属性名和内容都有 arg = '.' if r[0] in ('text()', 'tx()') else f'@{r[0]}' - if r[1] == '=': + symbol = r[1] + if symbol == '=': arg_str = f'{arg}={_make_search_str(r[2])}' - else: + + elif symbol == ':': arg_str = f'contains({arg},{_make_search_str(r[2])})' + elif symbol == '^': + arg_str = f'starts-with({arg},{_make_search_str(r[2])})' + + elif symbol == '$': + arg_str = f'substring({arg}, string-length({arg}) - string-length({_make_search_str(r[2])}) +1) ' \ + f'= {_make_search_str(r[2])}' + + else: + raise ValueError(f'符号不正确:{symbol}') + if arg_str and ignore: arg_str = f'not({arg_str})' diff --git a/DrissionPage/configs/driver_options.py b/DrissionPage/configs/driver_options.py index dd45009..ce30964 100644 --- a/DrissionPage/configs/driver_options.py +++ b/DrissionPage/configs/driver_options.py @@ -36,6 +36,7 @@ class DriverOptions(Options): self._experimental_options = options_dict.get('experimental_options', {}) self._debugger_address = options_dict.get('debugger_address', None) self.page_load_strategy = options_dict.get('page_load_strategy', 'normal') + self.system_user_path = options_dict.get('system_user_path', False) for arg in self._arguments: if arg.startswith('--user-data-dir='): @@ -50,6 +51,7 @@ class DriverOptions(Options): self.ini_path = None self.timeouts = {'implicit': 10, 'pageLoad': 30, 'script': 30} self._debugger_address = '127.0.0.1:9222' + self.system_user_path = False @property def driver_path(self):