From 6255a096ba1b6e98752673ec5819bf0635b56f41 Mon Sep 17 00:00:00 2001 From: g1879 Date: Wed, 24 Jul 2024 11:53:11 +0800 Subject: [PATCH 1/5] =?UTF-8?q?ele.click()=E5=8F=96=E6=B6=88=E7=A7=BB?= =?UTF-8?q?=E5=8A=A8=E8=BD=A8=E8=BF=B9=EF=BC=9Bwait.new=5Ftab()=E7=9A=84cu?= =?UTF-8?q?rr=5Ftab=E5=8F=82=E6=95=B0=E5=8F=AF=E6=8E=A5=E6=94=B6Tab?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1=EF=BC=9B=E4=BC=98=E5=8C=96=E7=AD=89=E5=BE=85?= =?UTF-8?q?=EF=BC=9B=E4=BF=AE=E5=A4=8D=E8=AE=BE=E7=BD=AE=E5=90=8C=E6=97=B6?= =?UTF-8?q?ua=E5=92=8C=E6=97=A0=E5=A4=B4=E6=97=B6=E5=87=BA=E7=8E=B0?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/_base/browser.py | 2 +- DrissionPage/_configs/chromium_options.py | 1 + DrissionPage/_configs/chromium_options.pyi | 1 + DrissionPage/_functions/browser.py | 2 ++ DrissionPage/_functions/elements.py | 2 +- DrissionPage/_functions/web.py | 4 ++-- DrissionPage/_functions/web.pyi | 2 +- DrissionPage/_units/clicker.py | 14 ++++---------- DrissionPage/_units/clicker.pyi | 3 +-- DrissionPage/_units/waiter.py | 17 +++++++++++------ DrissionPage/_units/waiter.pyi | 5 ++++- 11 files changed, 29 insertions(+), 24 deletions(-) diff --git a/DrissionPage/_base/browser.py b/DrissionPage/_base/browser.py index b01aec3..a22810e 100644 --- a/DrissionPage/_base/browser.py +++ b/DrissionPage/_base/browser.py @@ -81,7 +81,7 @@ class Chromium(object): self.address = self._chromium_options.address self._driver = BrowserDriver(self.id, 'browser', self.address, self) - if self.is_headless != self._chromium_options.is_headless or ( + if (not self._chromium_options._ua_set and self.is_headless != self._chromium_options.is_headless) or ( self._is_exists and self._chromium_options._new_env): self.quit(3, True) connect_browser(self._chromium_options) diff --git a/DrissionPage/_configs/chromium_options.py b/DrissionPage/_configs/chromium_options.py index bec890e..35e14c2 100644 --- a/DrissionPage/_configs/chromium_options.py +++ b/DrissionPage/_configs/chromium_options.py @@ -22,6 +22,7 @@ class ChromiumOptions(object): self._prefs_to_del = [] self.clear_file_flags = False self._is_headless = False + self._ua_set = False if read_file is False: ini_path = False diff --git a/DrissionPage/_configs/chromium_options.pyi b/DrissionPage/_configs/chromium_options.pyi index 10ba9a8..06875a1 100644 --- a/DrissionPage/_configs/chromium_options.pyi +++ b/DrissionPage/_configs/chromium_options.pyi @@ -34,6 +34,7 @@ class ChromiumOptions(object): _retry_times: int = ... _retry_interval: float = ... _is_headless: bool = ... + _ua_set: bool = ... def __init__(self, read_file: [bool, None] = True, ini_path: Union[str, Path] = None): ... diff --git a/DrissionPage/_functions/browser.py b/DrissionPage/_functions/browser.py index 9acae5d..9958bf0 100644 --- a/DrissionPage/_functions/browser.py +++ b/DrissionPage/_functions/browser.py @@ -81,6 +81,8 @@ def get_launch_args(opt): user_path = f'--user-data-dir={Path(i[16:]).absolute()}' result.add(user_path) continue + elif i.startswith('--user-agent='): + opt._ua_set = True result.add(i) if not user_path and not opt.system_user_path: diff --git a/DrissionPage/_functions/elements.py b/DrissionPage/_functions/elements.py index cccb8da..c91abfe 100644 --- a/DrissionPage/_functions/elements.py +++ b/DrissionPage/_functions/elements.py @@ -393,7 +393,7 @@ def get_eles(locators, owner, any_one=False, first_ele=True, timeout=10): for loc in locators: if res[loc] is not False: continue - ele = owner.ele(loc, timeout=0) if first_ele else owner.eles(loc, timeout=0) + ele = owner._ele(loc, timeout=0, raise_err=False, index=1 if first_ele else None) if ele: res[loc] = ele if any_one: diff --git a/DrissionPage/_functions/web.py b/DrissionPage/_functions/web.py index f86605e..ccbc84c 100644 --- a/DrissionPage/_functions/web.py +++ b/DrissionPage/_functions/web.py @@ -136,7 +136,7 @@ def offset_scroll(ele, offset_x, offset_y): :param ele: 元素对象 :param offset_x: 偏移量x :param offset_y: 偏移量y - :return: 绝对坐标和相对坐标 + :return: 相对坐标 """ loc_x, loc_y = ele.rect.location cp_x, cp_y = ele.rect.click_point @@ -150,7 +150,7 @@ def offset_scroll(ele, offset_x, offset_y): ccp_x, ccp_y = ele.rect.viewport_click_point cx = cl_x + offset_x if offset_x else ccp_x cy = cl_y + offset_y if offset_y else ccp_y - return lx, ly, cx, cy + return cx, cy def make_absolute_link(link, baseURI=None): diff --git a/DrissionPage/_functions/web.pyi b/DrissionPage/_functions/web.pyi index c7a40db..d09dfac 100644 --- a/DrissionPage/_functions/web.pyi +++ b/DrissionPage/_functions/web.pyi @@ -24,7 +24,7 @@ def format_html(text: str) -> str: ... def location_in_viewport(page: ChromiumBase, loc_x: float, loc_y: float) -> bool: ... -def offset_scroll(ele: ChromiumElement, offset_x: float, offset_y: float) -> Tuple[int, int, int, int]: ... +def offset_scroll(ele: ChromiumElement, offset_x: float, offset_y: float) -> Tuple[int, int]: ... def make_absolute_link(link: str, baseURI: str = None) -> str: ... diff --git a/DrissionPage/_units/clicker.py b/DrissionPage/_units/clicker.py index cb889ee..e020278 100644 --- a/DrissionPage/_units/clicker.py +++ b/DrissionPage/_units/clicker.py @@ -91,16 +91,13 @@ class Clicker(object): includeUserAgentShadowDOM=True, ignorePointerEventsNone=True) if r['backendNodeId'] != self._ele._backend_id: vx, vy = self._ele.rect.viewport_midpoint - lx, ly = self._ele.rect._get_page_coord(vx, vy) else: vx, vy = self._ele.rect.viewport_click_point - lx, ly = self._ele.rect._get_page_coord(vx, vy) except CDPError: vx, vy = self._ele.rect.viewport_midpoint - lx, ly = self._ele.rect._get_page_coord(vx, vy) - self._click(lx, ly, vx, vy) + self._click(vx, vy) return self._ele if by_js is not False: @@ -113,7 +110,7 @@ class Clicker(object): def right(self): """右键单击""" self._ele.owner.scroll.to_see(self._ele) - return self._click(*self._ele.rect.click_point, *self._ele.rect.viewport_click_point, button='right') + return self._click(*self._ele.rect.viewport_click_point, button='right') def middle(self, get_tab=True): """中键单击,默认返回新出现的tab对象 @@ -122,7 +119,7 @@ class Clicker(object): """ self._ele.owner.scroll.to_see(self._ele) curr_tid = self._ele.tab.browser.tab_ids[0] - self._click(*self._ele.rect.click_point, *self._ele.rect.viewport_click_point, button='middle') + self._click(*self._ele.rect.viewport_click_point, button='middle') if get_tab: tid = self._ele.tab.browser.wait.new_tab(curr_tab=curr_tid) if not tid: @@ -226,17 +223,14 @@ class Clicker(object): self.left(by_js=by_js) return True if self._ele.tab.wait.title_change(text=text, exclude=exclude, timeout=timeout) else False - def _click(self, loc_x, loc_y, view_x, view_y, button='left', count=1): + def _click(self, view_x, view_y, button='left', count=1): """实施点击 - :param loc_x: 绝对x坐标 - :param loc_y: 绝对y坐标 :param view_x: 视口x坐标 :param view_y: 视口y坐标 :param button: 'left' 'right' 'middle' 'back' 'forward' :param count: 点击次数 :return: None """ - self._ele.owner.actions.move_to((loc_x, loc_y), duration=.05) self._ele.owner._run_cdp('Input.dispatchMouseEvent', type='mousePressed', x=view_x, y=view_y, button=button, clickCount=count, _ignore=AlertExistsError) self._ele.owner._run_cdp('Input.dispatchMouseEvent', type='mouseReleased', x=view_x, diff --git a/DrissionPage/_units/clicker.pyi b/DrissionPage/_units/clicker.pyi index 1511100..325b2ac 100644 --- a/DrissionPage/_units/clicker.pyi +++ b/DrissionPage/_units/clicker.pyi @@ -53,8 +53,7 @@ class Clicker(object): def for_title_change(self, text: str = None, exclude: bool = False, by_js: bool = False, timeout: float = None) -> bool: ... - def _click(self, loc_x: float, - loc_y: float, + def _click(self, view_x: float, view_y: float, button: str = 'left', diff --git a/DrissionPage/_units/waiter.py b/DrissionPage/_units/waiter.py index d988c01..101dd01 100644 --- a/DrissionPage/_units/waiter.py +++ b/DrissionPage/_units/waiter.py @@ -35,16 +35,19 @@ class BrowserWaiter(OriginWaiter): def new_tab(self, timeout=None, curr_tab=None, raise_err=None): """等待新标签页出现 :param timeout: 超时时间(秒),为None则使用页面对象timeout属性 - :param curr_tab: 指定当前最新的tab id,用于判断新tab出现,为None自动获取 + :param curr_tab: 指定当前最新的tab对象或tab id,用于判断新tab出现,为None自动获取 :param raise_err: 等待失败时是否报错,为None时根据Settings设置 :return: 等到新标签页返回其id,否则返回False """ - curr_tid = curr_tab if curr_tab else self._owner.tab_ids[0] + if not curr_tab: + curr_tab = self._owner.tab_ids[0] + elif hasattr(curr_tab, '_type'): + curr_tab = curr_tab.tab_id timeout = timeout if timeout is not None else self._owner.timeout end_time = perf_counter() + timeout while perf_counter() < end_time: latest_tid = self._owner.tab_ids[0] - if curr_tid != latest_tid: + if curr_tab != latest_tid: return latest_tid sleep(.01) @@ -193,7 +196,7 @@ class BaseWaiter(OriginWaiter): by = ('id', 'xpath', 'link text', 'partial link text', 'name', 'tag name', 'class name', 'css selector') locators = ((get_loc(locators)[1],) if (isinstance(locators, str) or isinstance(locators, tuple) and locators[0] in by and len(locators) == 2) - else [get_loc(l)[1] for l in locators]) + else [get_loc(x)[1] for x in locators]) method = any if any_one else all timeout = self._owner.timeout if timeout is None else timeout @@ -321,7 +324,8 @@ class BaseWaiter(OriginWaiter): :param raise_err: 等待失败时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ - timeout = timeout if timeout is not None else self._owner.timeout + if timeout is None: + timeout = self._owner.timeout timeout = .1 if timeout <= 0 else timeout end_time = perf_counter() + timeout while perf_counter() < end_time: @@ -527,7 +531,8 @@ class ElementWaiter(OriginWaiter): :param raise_err: 等待失败时是否报错,为None时根据Settings设置 :return: 成功返回元素对象,失败返回False """ - timeout = timeout if timeout is not None else self._timeout + if timeout is None: + timeout = self._timeout t1 = perf_counter() r = self._wait_state('is_clickable', True, timeout, raise_err, err_text='等待元素可点击失败(等{}秒)。') r = self.stop_moving(timeout=timeout - perf_counter() + t1) if wait_moved and r else r diff --git a/DrissionPage/_units/waiter.pyi b/DrissionPage/_units/waiter.pyi index 9e3a70f..2e36810 100644 --- a/DrissionPage/_units/waiter.pyi +++ b/DrissionPage/_units/waiter.pyi @@ -34,7 +34,10 @@ class BrowserWaiter(OriginWaiter): def download_begin(self, timeout: float = None, cancel_it: bool = False) -> DownloadMission: ... - def new_tab(self, timeout: float = None, curr_tab: str = None, raise_err: bool = None) -> Union[str, bool]: ... + def new_tab(self, + timeout: float = None, + curr_tab: Union[str, ChromiumTab] = None, + raise_err: bool = None) -> Union[str, bool]: ... def all_downloads_done(self, timeout: float = None, cancel_if_timeout: bool = True) -> bool: ... From 0b40011a00f6ab6abef4cfb634f5b3802717acbb Mon Sep 17 00:00:00 2001 From: g1879 Date: Wed, 24 Jul 2024 15:48:21 +0800 Subject: [PATCH 2/5] 4.1.0.0b11 --- DrissionPage/_units/waiter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DrissionPage/_units/waiter.py b/DrissionPage/_units/waiter.py index 101dd01..002bede 100644 --- a/DrissionPage/_units/waiter.py +++ b/DrissionPage/_units/waiter.py @@ -133,7 +133,7 @@ class BaseWaiter(OriginWaiter): end_time = perf_counter() + timeout ele = self._owner._ele(loc_or_ele, raise_err=False, timeout=timeout) timeout = end_time - perf_counter() - if timeout <= 0: + if timeout <= 0 or not ele: if raise_err is True or Settings.raise_when_wait_failed is True: raise WaitTimeoutError(f'等待元素显示失败(等待{timeout}秒)。') else: From 0812f5233f8d9f6ace0f228067b0893ce30c2106 Mon Sep 17 00:00:00 2001 From: g1879 Date: Wed, 24 Jul 2024 17:43:01 +0800 Subject: [PATCH 3/5] =?UTF-8?q?4.1.0.0b12=E4=BF=AE=E5=A4=8DChromiumPage?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/__init__.py | 2 +- DrissionPage/_units/setter.py | 36 ++++++++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/DrissionPage/__init__.py b/DrissionPage/__init__.py index 2617ea8..cd53866 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.0b11' +__version__ = '4.1.0.0b12' diff --git a/DrissionPage/_units/setter.py b/DrissionPage/_units/setter.py index 972ab75..eea1870 100644 --- a/DrissionPage/_units/setter.py +++ b/DrissionPage/_units/setter.py @@ -428,6 +428,34 @@ class TabSetter(ChromiumBaseSetter): class ChromiumPageSetter(TabSetter): + def NoneElement_value(self, value=None, on_off=True): + """设置空元素是否返回设定值 + :param value: 返回的设定值 + :param on_off: 是否启用 + :return: None + """ + super().NoneElement_value(value, on_off) + self._owner.browser._none_ele_return_value = on_off + self._owner.browser._none_ele_value = value + + def retry_times(self, times): + """设置连接失败重连次数""" + super().retry_times(times) + self._owner.browser.retry_times = times + + def retry_interval(self, interval): + """设置连接失败重连间隔""" + super().retry_interval(interval) + self._owner.browser.retry_interval = interval + + def download_path(self, path): + """设置下载路径 + :param path: 下载路径 + :return: None + """ + super().download_path(path) + self._owner.browser._download_path = self._owner._download_path + def auto_handle_alert(self, on_off=True, accept=True, all_tabs=False): """设置是否启用自动处理弹窗 :param on_off: bool表示开或关 @@ -625,18 +653,20 @@ class LoadMode(object): if value.lower() not in ('normal', 'eager', 'none'): raise ValueError("只能选择 'normal', 'eager', 'none'。") self._owner._load_mode = value + if self._owner._type in ('ChromiumPage', 'MixPage'): + self._owner.browser._load_mode = value def normal(self): """设置页面加载策略为normal""" - self._owner._load_mode = 'normal' + self.__call__('normal') def eager(self): """设置页面加载策略为eager""" - self._owner._load_mode = 'eager' + self.__call__('eager') def none(self): """设置页面加载策略为none""" - self._owner._load_mode = 'none' + self.__call__('none') class PageScrollSetter(object): From 955c72d61c178d9271f84593bca589c6745dff2d Mon Sep 17 00:00:00 2001 From: g1879 Date: Fri, 26 Jul 2024 19:07:30 +0800 Subject: [PATCH 4/5] =?UTF-8?q?4.1.0.0b13Frame=E5=AF=B9=E8=B1=A1=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E5=8D=95=E4=BE=8B=EF=BC=9Bparent()=E5=A2=9E=E5=8A=A0t?= =?UTF-8?q?imeout=E5=8F=82=E6=95=B0=EF=BC=9Bsr=E5=86=85=E6=89=BE=E5=85=83?= =?UTF-8?q?=E7=B4=A0=E5=85=83=E7=B4=A0=E5=8A=A0=E4=B8=8Atimeout=E5=8F=82?= =?UTF-8?q?=E6=95=B0=EF=BC=9B=E4=BF=AE=E5=A4=8D=E4=B8=A4=E4=B8=AA=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/__init__.py | 2 +- DrissionPage/_base/base.py | 5 +- DrissionPage/_base/base.pyi | 8 ++- DrissionPage/_elements/chromium_element.py | 72 +++++++++++++-------- DrissionPage/_elements/chromium_element.pyi | 21 +++--- DrissionPage/_functions/elements.py | 1 + DrissionPage/_functions/tools.py | 2 + DrissionPage/_functions/web.py | 8 ++- DrissionPage/_pages/chromium_frame.py | 24 ++++++- DrissionPage/_pages/chromium_frame.pyi | 4 +- DrissionPage/_units/setter.py | 1 + 11 files changed, 102 insertions(+), 46 deletions(-) diff --git a/DrissionPage/__init__.py b/DrissionPage/__init__.py index cd53866..a8db515 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.0b12' +__version__ = '4.1.0.0b13' diff --git a/DrissionPage/_base/base.py b/DrissionPage/_base/base.py index 8fa9a7c..32fb08e 100644 --- a/DrissionPage/_base/base.py +++ b/DrissionPage/_base/base.py @@ -136,10 +136,11 @@ class DrissionElement(BaseElement): for x in self.eles('xpath:./text() | *')] return [format_html(x.strip(' ').rstrip('\n')) for x in texts if x and sub('[\r\n\t ]', '', x) != ''] - def parent(self, level_or_loc=1, index=1): + def parent(self, level_or_loc=1, index=1, timeout=None): """返回上面某一级父元素,可指定层数或用查询语法定位 :param level_or_loc: 第几级父元素,1开始,或定位符 :param index: 当level_or_loc传入定位符,使用此参数选择第几个结果,1开始 + :param timeout: 时间(秒) :return: 上级元素对象 """ if isinstance(level_or_loc, int): @@ -154,7 +155,7 @@ class DrissionElement(BaseElement): else: raise TypeError('level_or_loc参数只能是tuple、int或str。') - return self._ele(loc, timeout=0, relative=True, raise_err=False, method='parent()') + return self._ele(loc, timeout=timeout, relative=True, raise_err=False, method='parent()') def child(self, locator='', index=1, timeout=None, ele_only=True): """返回直接子元素元素或节点组成的列表,可用查询语法筛选 diff --git a/DrissionPage/_base/base.pyi b/DrissionPage/_base/base.pyi index 39cd123..05eb3e1 100644 --- a/DrissionPage/_base/base.pyi +++ b/DrissionPage/_base/base.pyi @@ -13,9 +13,10 @@ from DownloadKit import DownloadKit from .._elements.none_element import NoneElement from .._elements.session_element import SessionElement from .._functions.elements import SessionElementsList +from .._pages.chromium_frame import ChromiumFrame from .._pages.chromium_page import ChromiumPage -from .._pages.session_page import SessionPage from .._pages.mix_page import MixPage +from .._pages.session_page import SessionPage class BaseParser(object): @@ -82,6 +83,8 @@ class BaseElement(BaseParser): def nexts(self): ... + def get_frame(self, loc_or_ind, timeout=None) -> ChromiumFrame: ... + class DrissionElement(BaseElement): @@ -103,7 +106,8 @@ class DrissionElement(BaseElement): def parent(self, level_or_loc: Union[tuple, str, int] = 1, - index: int = 1) -> Union[DrissionElement, None]: ... + index: int = 1, + timeout: float = None) -> Union[DrissionElement, None]: ... def child(self, locator: Union[Tuple[str, str], str, int] = '', diff --git a/DrissionPage/_elements/chromium_element.py b/DrissionPage/_elements/chromium_element.py index 539a028..810d1df 100644 --- a/DrissionPage/_elements/chromium_element.py +++ b/DrissionPage/_elements/chromium_element.py @@ -232,13 +232,14 @@ class ChromiumElement(DrissionElement): if (is_checked and uncheck) or (not is_checked and not uncheck): self.click() - def parent(self, level_or_loc=1, index=1): + def parent(self, level_or_loc=1, index=1, timeout=0): """返回上面某一级父元素,可指定层数或用查询语法定位 :param level_or_loc: 第几级父元素,1开始,或定位符 :param index: 当level_or_loc传入定位符,使用此参数选择第几个结果,1开始 + :param timeout: 查找超时时间(秒) :return: 上级元素对象 """ - return super().parent(level_or_loc, index) + return super().parent(level_or_loc, index, timeout=timeout) def child(self, locator='', index=1, timeout=None, ele_only=True): """返回当前元素的一个符合条件的直接子元素,可用查询语法筛选,可指定返回筛选结果的第几个 @@ -1041,10 +1042,11 @@ class ShadowRoot(BaseElement): Thread(target=run_js, args=(self, script, as_expr, self.owner.timeouts.script if timeout is None else timeout, args)).start() - def parent(self, level_or_loc=1, index=1): + def parent(self, level_or_loc=1, index=1, timeout=0): """返回上面某一级父元素,可指定层数或用查询语法定位 :param level_or_loc: 第几级父元素,或定位符 :param index: 当level_or_loc传入定位符,使用此参数选择第几个结果 + :param timeout: 查找超时时间(秒) :return: ChromiumElement对象 """ if isinstance(level_or_loc, int): @@ -1061,12 +1063,13 @@ class ShadowRoot(BaseElement): else: raise TypeError('level_or_loc参数只能是tuple、int或str。') - return self.parent_ele._ele(loc, timeout=0, relative=True, raise_err=False, method='parent()') + return self.parent_ele._ele(loc, timeout=timeout, relative=True, raise_err=False, method='parent()') - def child(self, locator='', index=1): + def child(self, locator='', index=1, timeout=None): """返回直接子元素元素或节点组成的列表,可用查询语法筛选 :param locator: 用于筛选的查询语法 :param index: 第几个查询结果,1开始 + :param timeout: 查找超时时间(秒) :return: 直接子元素或节点文本组成的列表 """ if not locator: @@ -1078,14 +1081,16 @@ class ShadowRoot(BaseElement): loc = loc[1].lstrip('./') loc = f'xpath:./{loc}' - ele = self._ele(loc, index=index, relative=True) + ele = self._ele(loc, index=index, relative=True, timeout=timeout) - return ele if ele else NoneElement(self.owner, 'child()', {'locator': locator, 'index': index}) + return ele if ele else NoneElement(self.owner, 'child()', + {'locator': locator, 'index': index, 'timeout': timeout}) - def next(self, locator='', index=1): + def next(self, locator='', index=1, timeout=None): """返回当前元素后面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个 :param locator: 用于筛选的查询语法 :param index: 第几个查询结果,1开始 + :param timeout: 查找超时时间(秒) :return: ChromiumElement对象 """ loc = get_loc(locator, True) @@ -1094,15 +1099,17 @@ class ShadowRoot(BaseElement): loc = loc[1].lstrip('./') xpath = f'xpath:./{loc}' - ele = self.parent_ele._ele(xpath, index=index, relative=True) + ele = self.parent_ele._ele(xpath, index=index, relative=True, timeout=timeout) - return ele if ele else NoneElement(self.owner, 'next()', {'locator': locator, 'index': index}) + return ele if ele else NoneElement(self.owner, 'next()', + {'locator': locator, 'index': index, 'timeout': timeout}) - def before(self, locator='', index=1): + def before(self, locator='', index=1, timeout=None): """返回文档中当前元素前面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个 查找范围不限同级元素,而是整个DOM文档 :param locator: 用于筛选的查询语法 :param index: 前面第几个查询结果,1开始 + :param timeout: 查找超时时间(秒) :return: 本元素前面的某个元素或节点 """ loc = get_loc(locator, True) @@ -1111,23 +1118,27 @@ class ShadowRoot(BaseElement): loc = loc[1].lstrip('./') xpath = f'xpath:./preceding::{loc}' - ele = self.parent_ele._ele(xpath, index=index, relative=True) + ele = self.parent_ele._ele(xpath, index=index, relative=True, timeout=timeout) - return ele if ele else NoneElement(self.owner, 'before()', {'locator': locator, 'index': index}) + return ele if ele else NoneElement(self.owner, 'before()', + {'locator': locator, 'index': index, 'timeout': timeout}) - def after(self, locator='', index=1): + def after(self, locator='', index=1, timeout=None): """返回文档中此当前元素后面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个 查找范围不限同级元素,而是整个DOM文档 :param locator: 用于筛选的查询语法 :param index: 后面第几个查询结果,1开始 + :param timeout: 查找超时时间(秒) :return: 本元素后面的某个元素或节点 """ - nodes = self.afters(locator=locator) - return nodes[index - 1] if nodes else NoneElement(self.owner, 'after()', {'locator': locator, 'index': index}) + nodes = self.afters(locator=locator, timeout=timeout) + return nodes[index - 1] if nodes else NoneElement(self.owner, 'after()', + {'locator': locator, 'index': index, 'timeout': timeout}) - def children(self, locator=''): + def children(self, locator='', timeout=None): """返回当前元素符合条件的直接子元素或节点组成的列表,可用查询语法筛选 :param locator: 用于筛选的查询语法 + :param timeout: 查找超时时间(秒) :return: 直接子元素或节点文本组成的列表 """ if not locator: @@ -1139,11 +1150,12 @@ class ShadowRoot(BaseElement): loc = loc[1].lstrip('./') loc = f'xpath:./{loc}' - return self._ele(loc, index=None, relative=True) + return self._ele(loc, index=None, relative=True, timeout=timeout) - def nexts(self, locator=''): + def nexts(self, locator='', timeout=None): """返回当前元素后面符合条件的同级元素或节点组成的列表,可用查询语法筛选 :param locator: 用于筛选的查询语法 + :param timeout: 查找超时时间(秒) :return: ChromiumElement对象组成的列表 """ loc = get_loc(locator, True) @@ -1152,12 +1164,13 @@ class ShadowRoot(BaseElement): loc = loc[1].lstrip('./') xpath = f'xpath:./{loc}' - return self.parent_ele._ele(xpath, index=None, relative=True) + return self.parent_ele._ele(xpath, index=None, relative=True, timeout=timeout) - def befores(self, locator=''): + def befores(self, locator='', timeout=None): """返回文档中当前元素前面符合条件的元素或节点组成的列表,可用查询语法筛选 查找范围不限同级元素,而是整个DOM文档 :param locator: 用于筛选的查询语法 + :param timeout: 查找超时时间(秒) :return: 本元素前面的元素或节点组成的列表 """ loc = get_loc(locator, True) @@ -1166,18 +1179,19 @@ class ShadowRoot(BaseElement): loc = loc[1].lstrip('./') xpath = f'xpath:./preceding::{loc}' - return self.parent_ele._ele(xpath, index=None, relative=True) + return self.parent_ele._ele(xpath, index=None, relative=True, timeout=timeout) - def afters(self, locator=''): + def afters(self, locator='', timeout=None): """返回文档中当前元素后面符合条件的元素或节点组成的列表,可用查询语法筛选 查找范围不限同级元素,而是整个DOM文档 :param locator: 用于筛选的查询语法 + :param timeout: 查找超时时间(秒) :return: 本元素后面的元素或节点组成的列表 """ eles1 = self.nexts(locator) loc = get_loc(locator, True)[1].lstrip('./') xpath = f'xpath:./following::{loc}' - return eles1 + self.parent_ele._ele(xpath, index=None, relative=True) + return eles1 + self.parent_ele._ele(xpath, index=None, relative=True, timeout=timeout) def ele(self, locator, index=1, timeout=None): """返回当前元素下级符合条件的一个元素 @@ -1251,7 +1265,10 @@ class ShadowRoot(BaseElement): 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)'): + if c in ('html:nth-child(1)', 'html:nth-child(1)>body:nth-child(1)', + 'html:nth-child(1)>body:nth-child(1)>shadow_root:nth-child(1)'): + continue + elif 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: @@ -1265,7 +1282,8 @@ class ShadowRoot(BaseElement): else: node_ids = [self.owner._run_cdp('DOM.querySelector', nodeId=self._node_id, selector=i)['nodeId'] for i in css] - if 0 in node_ids: + node_ids = [i for i in node_ids if i] + if not node_ids: return None r = make_chromium_eles(self.owner, _ids=node_ids, index=index, is_obj_id=False) return None if r is False else r @@ -1476,6 +1494,8 @@ def make_chromium_eles(page, _ids, index=1, is_obj_id=True, ele_only=False): else: # 获取全部 nodes = ChromiumElementsList(page=page) for obj_id in _ids: + # if obj_id == 0: + # continue tmp = get_node_func(page, obj_id, ele_only) if tmp is False: return False diff --git a/DrissionPage/_elements/chromium_element.pyi b/DrissionPage/_elements/chromium_element.pyi index c04e13d..1af2011 100644 --- a/DrissionPage/_elements/chromium_element.pyi +++ b/DrissionPage/_elements/chromium_element.pyi @@ -101,7 +101,8 @@ class ChromiumElement(DrissionElement): def parent(self, level_or_loc: Union[tuple, str, int] = 1, - index: int = 1) -> ChromiumElement: ... + index: int = 1, + timeout: float = 0) -> ChromiumElement: ... def child(self, locator: Union[Tuple[str, str], str, int] = '', @@ -309,31 +310,31 @@ class ShadowRoot(BaseElement): def run_async_js(self, script: str, *args, as_expr: bool = False, timeout: float = None) -> None: ... - def parent(self, level_or_loc: Union[str, int] = 1, index: int = 1) -> ChromiumElement: ... + def parent(self, level_or_loc: Union[str, int] = 1, index: int = 1, timeout: float = 0) -> ChromiumElement: ... def child(self, locator: Union[Tuple[str, str], str] = '', - index: int = 1) -> ChromiumElement: ... + index: int = 1, timeout:float=None) -> ChromiumElement: ... def next(self, locator: Union[Tuple[str, str], str] = '', - index: int = 1) -> ChromiumElement: ... + index: int = 1, timeout:float=None) -> ChromiumElement: ... def before(self, locator: Union[Tuple[str, str], str] = '', - index: int = 1) -> ChromiumElement: ... + index: int = 1, timeout:float=None) -> ChromiumElement: ... def after(self, locator: Union[Tuple[str, str], str] = '', - index: int = 1) -> ChromiumElement: ... + index: int = 1, timeout:float=None) -> ChromiumElement: ... - def children(self, locator: Union[Tuple[str, str], str] = '') -> List[ChromiumElement]: ... + def children(self, locator: Union[Tuple[str, str], str] = '', timeout:float=None) -> List[ChromiumElement]: ... - def nexts(self, locator: Union[Tuple[str, str], str] = '') -> List[ChromiumElement]: ... + def nexts(self, locator: Union[Tuple[str, str], str] = '', timeout:float=None) -> List[ChromiumElement]: ... - def befores(self, locator: Union[Tuple[str, str], str] = '') -> List[ChromiumElement]: ... + def befores(self, locator: Union[Tuple[str, str], str] = '', timeout:float=None) -> List[ChromiumElement]: ... - def afters(self, locator: Union[Tuple[str, str], str] = '') -> List[ChromiumElement]: ... + def afters(self, locator: Union[Tuple[str, str], str] = '', timeout:float=None) -> List[ChromiumElement]: ... def ele(self, locator: Union[Tuple[str, str], str], diff --git a/DrissionPage/_functions/elements.py b/DrissionPage/_functions/elements.py index c91abfe..2a459b4 100644 --- a/DrissionPage/_functions/elements.py +++ b/DrissionPage/_functions/elements.py @@ -170,6 +170,7 @@ class SessionFilter(SessionFilterOne): """ self._list = _text_all(self._list, SessionElementsList(page=self._list._page), text=text, fuzzy=fuzzy, contain=contain) + return self def _get_attr(self, name, value, method, equal=True): """返回通过某个方法可获得某个值的元素 diff --git a/DrissionPage/_functions/tools.py b/DrissionPage/_functions/tools.py index 29ffb9e..ed97953 100644 --- a/DrissionPage/_functions/tools.py +++ b/DrissionPage/_functions/tools.py @@ -226,6 +226,8 @@ def raise_error(result, ignore=None, user=False): r = StorageError() elif error == 'Sanitizing cookie failed': r = CookieFormatError(f'cookie格式不正确:{result["args"]}') + elif error == 'Invalid header name': + r = ValueError(f'header名不正确。\n参数:{result["args"]["headers"]}') elif error == 'Given expression does not evaluate to a function': r = JavaScriptError(f'传入的js无法解析成函数:\n{result["args"]["functionDeclaration"]}') elif error.endswith("' wasn't found"): diff --git a/DrissionPage/_functions/web.py b/DrissionPage/_functions/web.py index ccbc84c..98cd105 100644 --- a/DrissionPage/_functions/web.py +++ b/DrissionPage/_functions/web.py @@ -372,11 +372,15 @@ def format_headers(txt): """ if isinstance(txt, (dict, CaseInsensitiveDict)): for k, v in txt.items(): - txt[k] = str(v) + if k in (':method', ':scheme', ':authority', ':path'): + txt.pop(k) + else: + txt[k] = str(v) return txt headers = {} for header in txt.split('\n'): if header: name, value = header.split(': ', maxsplit=1) - headers[name] = value + if name not in (':method', ':scheme', ':authority', ':path'): + headers[name] = value return headers diff --git a/DrissionPage/_pages/chromium_frame.py b/DrissionPage/_pages/chromium_frame.py index 2f30aff..62264bd 100644 --- a/DrissionPage/_pages/chromium_frame.py +++ b/DrissionPage/_pages/chromium_frame.py @@ -10,6 +10,7 @@ from re import search, findall, DOTALL from time import sleep, perf_counter from .._elements.chromium_element import ChromiumElement +from .._functions.settings import Settings from .._pages.chromium_base import ChromiumBase from .._units.listener import FrameListener from .._units.rect import FrameRect @@ -21,6 +22,24 @@ from ..errors import ContextLostError, ElementLostError, PageDisconnectedError, class ChromiumFrame(ChromiumBase): + _Frames = {} + + def __new__(cls, owner, ele, info=None): + """ + :param owner: frame所在的页面对象 + :param ele: frame所在元素 + :param info: frame所在元素信息 + """ + node = info['node'] if info else owner._run_cdp('DOM.describeNode', backendNodeId=ele._backend_id)['node'] + if Settings.singleton_tab_obj and node['frameId'] in cls._Frames: + r = cls._Frames[node['frameId']] + while not hasattr(r, '_frame_id'): + sleep(.1) + return r + r = object.__new__(cls) + cls._Frames[node['frameId']] = r + return r + def __init__(self, owner, ele, info=None): """ :param owner: frame所在的页面对象 @@ -33,7 +52,7 @@ class ChromiumFrame(ChromiumBase): self._frame_ele = ele self._reloading = False - node = info['node'] if not info else owner._run_cdp('DOM.describeNode', backendNodeId=ele._backend_id)['node'] + node = info['node'] if info else owner._run_cdp('DOM.describeNode', backendNodeId=ele._backend_id)['node'] self._frame_id = node['frameId'] if self._is_inner_frame(): self._is_diff_domain = False @@ -385,10 +404,11 @@ class ChromiumFrame(ChromiumBase): else: return self.doc_ele._run_js(script, *args, as_expr=as_expr, timeout=timeout) - def parent(self, level_or_loc=1, index=1): + def parent(self, level_or_loc=1, index=1, timeout=0): """返回上面某一级父元素,可指定层数或用查询语法定位 :param level_or_loc: 第几级父元素,1开始,或定位符 :param index: 当level_or_loc传入定位符,使用此参数选择第几个结果,1开始 + :param timeout: 查找超时时间(秒) :return: 上级元素对象 """ return self.frame_ele.parent(level_or_loc, index) diff --git a/DrissionPage/_pages/chromium_frame.pyi b/DrissionPage/_pages/chromium_frame.pyi index 54e3594..76b9bce 100644 --- a/DrissionPage/_pages/chromium_frame.pyi +++ b/DrissionPage/_pages/chromium_frame.pyi @@ -21,6 +21,7 @@ from .._units.waiter import FrameWaiter class ChromiumFrame(ChromiumBase): + _Frames: dict = ... def __init__(self, owner: Union[ChromiumTab, ChromiumFrame], @@ -156,7 +157,8 @@ class ChromiumFrame(ChromiumBase): def parent(self, level_or_loc: Union[Tuple[str, str], str, int] = 1, - index: int = 1) -> ChromiumElement: ... + index: int = 1, + timeout: float = 0) -> ChromiumElement: ... def prev(self, locator: Union[Tuple[str, str], str, int] = '', diff --git a/DrissionPage/_units/setter.py b/DrissionPage/_units/setter.py index eea1870..1c46abd 100644 --- a/DrissionPage/_units/setter.py +++ b/DrissionPage/_units/setter.py @@ -770,6 +770,7 @@ class WindowSetter(object): return self._owner._run_cdp('Browser.getWindowForTarget') except: sleep(.1) + raise RuntimeError('获取窗口信息失败。') def _perform(self, bounds): """执行改变窗口大小操作 From e7ab81092b5d4b9423fdcf0e65d975d7608ca8fa Mon Sep 17 00:00:00 2001 From: g1879 Date: Fri, 26 Jul 2024 19:44:26 +0800 Subject: [PATCH 5/5] =?UTF-8?q?4.1.0.0b14=E4=BF=AE=E5=A4=8D=E5=B0=8F?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/__init__.py | 2 +- DrissionPage/_pages/chromium_frame.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/DrissionPage/__init__.py b/DrissionPage/__init__.py index a8db515..1774967 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.0b13' +__version__ = '4.1.0.0b14' diff --git a/DrissionPage/_pages/chromium_frame.py b/DrissionPage/_pages/chromium_frame.py index 62264bd..f97dea3 100644 --- a/DrissionPage/_pages/chromium_frame.py +++ b/DrissionPage/_pages/chromium_frame.py @@ -194,6 +194,7 @@ class ChromiumFrame(ChromiumBase): def _onFrameDetached(self, **kwargs): """同域变异域""" self.browser._frames.pop(kwargs['frameId'], None) + ChromiumFrame._Frames.pop(kwargs['frameId'], None) if kwargs['frameId'] == self._frame_id: self._reload() @@ -411,7 +412,7 @@ class ChromiumFrame(ChromiumBase): :param timeout: 查找超时时间(秒) :return: 上级元素对象 """ - return self.frame_ele.parent(level_or_loc, index) + return self.frame_ele.parent(level_or_loc, index, timeout=timeout) def prev(self, locator='', index=1, timeout=0, ele_only=True): """返回当前元素前面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个