diff --git a/DrissionPage/__init__.py b/DrissionPage/__init__.py index 4a88739..2617ea8 100644 --- a/DrissionPage/__init__.py +++ b/DrissionPage/__init__.py @@ -14,4 +14,4 @@ from ._pages.chromium_page import ChromiumPage from ._pages.mix_page import MixPage from ._pages.mix_page import MixPage as WebPage -__version__ = '4.1.0.0b10' +__version__ = '4.1.0.0b11' diff --git a/DrissionPage/_base/driver.py b/DrissionPage/_base/driver.py index bf2916b..c95485a 100644 --- a/DrissionPage/_base/driver.py +++ b/DrissionPage/_base/driver.py @@ -7,7 +7,7 @@ """ from json import dumps, loads, JSONDecodeError from queue import Queue, Empty -from threading import Thread, Event +from threading import Thread from time import perf_counter, sleep from requests import Session @@ -44,7 +44,7 @@ class Driver(object): self._handle_event_th.daemon = True self._handle_immediate_event_th = None - self._stopped = Event() + self.is_running = False self.event_handlers = {} self.immediate_event_handlers = {} @@ -87,7 +87,7 @@ class Driver(object): self.method_results.pop(ws_id, None) return {'error': {'message': 'connection disconnected'}, 'type': 'connection_error'} - while not self._stopped.is_set(): + while self.is_running: try: result = self.method_results[ws_id].get(timeout=.2) self.method_results.pop(ws_id, None) @@ -108,7 +108,7 @@ class Driver(object): def _recv_loop(self): """接收浏览器信息的守护线程方法""" - while not self._stopped.is_set(): + while self.is_running: try: # self._ws.settimeout(1) msg_json = self._ws.recv() @@ -146,7 +146,7 @@ class Driver(object): def _handle_event_loop(self): """当接收到浏览器信息,执行已绑定的方法""" - while not self._stopped.is_set(): + while self.is_running: try: event = self.event_queue.get(timeout=1) except Empty: @@ -184,7 +184,7 @@ class Driver(object): :param kwargs: cdp参数 :return: 执行结果 """ - if self._stopped.is_set(): + if not self.is_running: return {'error': 'connection disconnected', 'type': 'connection_error'} timeout = kwargs.pop('_timeout', Settings.cdp_timeout) @@ -198,7 +198,7 @@ class Driver(object): def start(self): """启动连接""" - self._stopped.clear() + self.is_running = True try: self._ws = create_connection(self._websocket_url, enable_multithread=True, suppress_origin=True) except WebSocketBadStatusException as e: @@ -221,10 +221,10 @@ class Driver(object): def _stop(self): """中断连接""" - if self._stopped.is_set(): + if not self.is_running: return False - self._stopped.set() + self.is_running = False if self._ws: self._ws.close() self._ws = None diff --git a/DrissionPage/_base/driver.pyi b/DrissionPage/_base/driver.pyi index b3e09f0..be5f7c1 100644 --- a/DrissionPage/_base/driver.pyi +++ b/DrissionPage/_base/driver.pyi @@ -6,7 +6,7 @@ @License : BSD 3-Clause. """ from queue import Queue -from threading import Thread, Event +from threading import Thread from typing import Union, Callable, Dict, Optional from requests import Response, Session @@ -35,7 +35,8 @@ class Driver(object): _recv_th: Thread _handle_event_th: Thread _handle_immediate_event_th: Optional[Thread] - _stopped: Event + # _stopped: Event + is_running: bool event_handlers: dict immediate_event_handlers: dict method_results: dict diff --git a/DrissionPage/_elements/chromium_element.py b/DrissionPage/_elements/chromium_element.py index 5409f46..539a028 100644 --- a/DrissionPage/_elements/chromium_element.py +++ b/DrissionPage/_elements/chromium_element.py @@ -903,7 +903,11 @@ class ChromiumElement(DrissionElement): txt5 = '''return path;''' elif mode == 'css': - txt1 = '' + txt1 = ''' + let i = el.getAttribute("id"); + if (i){path = '>' + el.tagName.toLowerCase() + "#" + i + path; + break;} + ''' txt3 = '' txt4 = '''path = '>' + el.tagName.toLowerCase() + ":nth-child(" + nth + ")" + path;''' txt5 = '''return path.substr(1);''' @@ -913,6 +917,7 @@ class ChromiumElement(DrissionElement): js = '''function(){ function e(el) { + //return el; if (!(el instanceof Element)) return; let path = ''; while (el.nodeType === Node.ELEMENT_NODE) { @@ -1243,7 +1248,12 @@ class ShadowRoot(BaseElement): if not eles: return None - css = [i.css_path[61:] for i in eles] + css = [] + for i in eles: + c = i.css_path + if c.startswith('html:nth-child(1)>body:nth-child(1)>shadow_root:nth-child(1)'): + c = c[61:] + css.append(c) if index is not None: try: node_id = self.owner._run_cdp('DOM.querySelector', nodeId=self._node_id, diff --git a/DrissionPage/_elements/session_element.py b/DrissionPage/_elements/session_element.py index d746dd5..29257c8 100644 --- a/DrissionPage/_elements/session_element.py +++ b/DrissionPage/_elements/session_element.py @@ -276,6 +276,10 @@ class SessionElement(DrissionElement): while ele: if mode == 'css': + id_ = ele.attr('id') + if id_: + path_str = f'>{ele.tag}#{id_}{path_str}' + break brothers = len(ele.eles(f'xpath:./preceding-sibling::*')) path_str = f'>{ele.tag}:nth-child({brothers + 1}){path_str}' else: diff --git a/DrissionPage/_pages/chromium_base.py b/DrissionPage/_pages/chromium_base.py index 229ff0f..9ae334b 100644 --- a/DrissionPage/_pages/chromium_base.py +++ b/DrissionPage/_pages/chromium_base.py @@ -374,7 +374,7 @@ class ChromiumBase(BasePage): @property def _target_id(self): """返回当前标签页id""" - return self.driver.id if not self.driver._stopped.is_set() else '' + return self.driver.id if self.driver.is_running else '' @property def active_ele(self): diff --git a/DrissionPage/_units/listener.py b/DrissionPage/_units/listener.py index 504dc54..0166e15 100644 --- a/DrissionPage/_units/listener.py +++ b/DrissionPage/_units/listener.py @@ -127,13 +127,13 @@ class Listener(object): if not self.listening: raise RuntimeError('监听未启动或已暂停。') if not timeout: - while self._caught.qsize() < count: + while self._driver.is_running and self._caught.qsize() < count: sleep(.03) fail = False else: end = perf_counter() + timeout - while True: + while self._driver.is_running: if perf_counter() > end: fail = True break @@ -167,8 +167,8 @@ class Listener(object): raise RuntimeError('监听未启动或已暂停。') caught = 0 end = perf_counter() + timeout if timeout else None - while True: - if (timeout and perf_counter() > end) or self._driver._stopped.is_set(): + while self._driver.is_running: + if (timeout and perf_counter() > end) or not self._driver.is_running: return if self._caught.qsize() >= gap: yield self._caught.get_nowait() if gap == 1 else [self._caught.get_nowait() for _ in range(gap)] diff --git a/DrissionPage/_units/waiter.py b/DrissionPage/_units/waiter.py index e8afbd1..d988c01 100644 --- a/DrissionPage/_units/waiter.py +++ b/DrissionPage/_units/waiter.py @@ -285,18 +285,26 @@ class BaseWaiter(OriginWaiter): :param raise_err: 等待失败时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ + + def do(): + if arg == 'url': + v = self._owner.url + elif arg == 'title': + v = self._owner.title + else: + raise ValueError + if (not exclude and text in v) or (exclude and text not in v): + return True + + if do(): + return True + if timeout is None: timeout = self._owner.timeout end_time = perf_counter() + timeout while perf_counter() < end_time: - if arg == 'url': - val = self._owner.url - elif arg == 'title': - val = self._owner.title - else: - raise ValueError - if (not exclude and text in val) or (exclude and text not in val): + if do(): return True sleep(.05) @@ -313,19 +321,18 @@ class BaseWaiter(OriginWaiter): :param raise_err: 等待失败时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ - if timeout != 0: - if timeout is None or timeout is True: - timeout = self._owner.timeout - end_time = perf_counter() + timeout - while perf_counter() < end_time: - if self._owner._is_loading == start: - return True - sleep(gap) + timeout = timeout if timeout is not None else self._owner.timeout + timeout = .1 if timeout <= 0 else timeout + end_time = perf_counter() + timeout + while perf_counter() < end_time: + if self._owner._is_loading == start: + return True + sleep(gap) - if raise_err is True or Settings.raise_when_wait_failed is True: - raise WaitTimeoutError(f'等待页面加载失败(等待{timeout}秒)。') - else: - return False + if raise_err is True or Settings.raise_when_wait_failed is True: + raise WaitTimeoutError(f'等待页面加载失败(等待{timeout}秒)。') + else: + return False class TabWaiter(BaseWaiter): @@ -461,6 +468,9 @@ class ElementWaiter(OriginWaiter): :param raise_err: 等待失败时是否报错,为None时根据Settings设置 :return: 成功返回元素对象,失败返回False """ + if not self._ele.states.is_enabled or not self._ele.states.is_alive: + return self._ele + if timeout is None: timeout = self._timeout end_time = perf_counter() + timeout @@ -483,6 +493,9 @@ class ElementWaiter(OriginWaiter): """ if timeout is None: timeout = self._timeout + if timeout <= 0: + timeout = .1 + end_time = perf_counter() + timeout while perf_counter() < end_time: try: @@ -514,9 +527,10 @@ class ElementWaiter(OriginWaiter): :param raise_err: 等待失败时是否报错,为None时根据Settings设置 :return: 成功返回元素对象,失败返回False """ + timeout = timeout if timeout is not None else self._timeout t1 = perf_counter() r = self._wait_state('is_clickable', True, timeout, raise_err, err_text='等待元素可点击失败(等{}秒)。') - r = self.stop_moving(timeout=perf_counter() - t1) if wait_moved and r else r + r = self.stop_moving(timeout=timeout - perf_counter() + t1) if wait_moved and r else r if raise_err and not r: raise WaitTimeoutError(f'等待元素可点击失败(等{timeout}秒)。') return r