From 17ae5e83b11dec7333d053d01a22a0fa78ca17f4 Mon Sep 17 00:00:00 2001 From: g1879 Date: Thu, 19 Jan 2023 11:26:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9F=BA=E6=9C=AC=E5=AE=8C=E6=88=90download?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=EF=BC=8C=E5=BE=85=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/chromium_base.py | 11 +-- DrissionPage/chromium_base.pyi | 2 + DrissionPage/chromium_element.py | 119 +++++++++++++++---------------- DrissionPage/chromium_page.py | 14 ++-- DrissionPage/chromium_page.pyi | 5 +- DrissionPage/easy_set.py | 31 ++++---- DrissionPage/functions/web.py | 8 +-- DrissionPage/mix_page.py | 2 +- DrissionPage/web_page.py | 60 +++++++++------- DrissionPage/web_page.pyi | 4 +- 10 files changed, 140 insertions(+), 116 deletions(-) diff --git a/DrissionPage/chromium_base.py b/DrissionPage/chromium_base.py index 52fc3c0..9dd19d3 100644 --- a/DrissionPage/chromium_base.py +++ b/DrissionPage/chromium_base.py @@ -29,10 +29,6 @@ class ChromiumBase(BasePage): self._root_id = None self._debug = False self._debug_recorder = None - self._control_session = Session() - self._control_session.keep_alive = False - self._first_run = True - self._is_reading = False # 用于避免不同线程重复读取document self._timeouts = None self._page_load_strategy = None @@ -47,6 +43,7 @@ class ChromiumBase(BasePage): :param tab_id: 要控制的标签页id,不指定默认为激活的 :return: None """ + self._chromium_init() self.address = addr_driver_opts if not tab_id: json = self._control_session.get(f'http://{self.address}/json').json() @@ -56,6 +53,12 @@ class ChromiumBase(BasePage): self._get_document() self._first_run = False + def _chromium_init(self): + self._control_session = Session() + self._control_session.keep_alive = False + self._first_run = True + self._is_reading = False + def _set_options(self): """用于设置浏览器运行参数""" self._timeouts = Timeout(self) diff --git a/DrissionPage/chromium_base.pyi b/DrissionPage/chromium_base.pyi index 865583a..fe41e72 100644 --- a/DrissionPage/chromium_base.pyi +++ b/DrissionPage/chromium_base.pyi @@ -41,6 +41,8 @@ class ChromiumBase(BasePage): addr_driver_opts: Union[str, ChromiumDriver, DriverOptions] = None, tab_id: str = None) -> None: ... + def _chromium_init(self): ... + def _init_page(self, tab_id: str = None) -> None: ... def _get_document(self) -> None: ... diff --git a/DrissionPage/chromium_element.py b/DrissionPage/chromium_element.py index fc8fc8d..311409b 100644 --- a/DrissionPage/chromium_element.py +++ b/DrissionPage/chromium_element.py @@ -21,7 +21,7 @@ class ChromiumElement(DrissionElement): """ChromePage页面对象中的元素对象""" def __init__(self, page, node_id=None, obj_id=None, backend_id=None): - """初始化,node_id和obj_id必须至少传入一个 \n + """初始化,node_id和obj_id必须至少传入一个 :param page: 元素所在ChromePage页面对象 :param node_id: cdp中的node id :param obj_id: js中的object id @@ -56,8 +56,8 @@ class ChromiumElement(DrissionElement): return f'' def __call__(self, loc_or_str, timeout=None): - """在内部查找元素 \n - 例:ele2 = ele1('@id=ele_id') \n + """在内部查找元素 + 例:ele2 = ele1('@id=ele_id') :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 超时时间 :return: ChromiumElement对象或属性、文本 @@ -86,8 +86,7 @@ class ChromiumElement(DrissionElement): def attrs(self): """返回元素所有attribute属性""" attrs = self.page.run_cdp('DOM.getAttributes', nodeId=self._node_id, not_change=True)['attributes'] - attrs_len = len(attrs) - return {attrs[i]: attrs[i + 1] for i in range(0, attrs_len, 2)} + return {attrs[i]: attrs[i + 1] for i in range(0, len(attrs), 2)} @property def text(self): @@ -197,14 +196,14 @@ class ChromiumElement(DrissionElement): return self._scroll def parent(self, level_or_loc=1): - """返回上面某一级父元素,可指定层数或用查询语法定位 \n + """返回上面某一级父元素,可指定层数或用查询语法定位 :param level_or_loc: 第几级父元素,或定位符 :return: 上级元素对象 """ return super().parent(level_or_loc) def prev(self, filter_loc='', index=1, timeout=0): - """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n + """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 :param filter_loc: 用于筛选元素的查询语法 :param index: 前面第几个查询结果元素 :param timeout: 查找元素的超时时间 @@ -213,7 +212,7 @@ class ChromiumElement(DrissionElement): return super().prev(index, filter_loc, timeout) def next(self, filter_loc='', index=1, timeout=0): - """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n + """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 :param filter_loc: 用于筛选元素的查询语法 :param index: 后面第几个查询结果元素 :param timeout: 查找元素的超时时间 @@ -222,7 +221,7 @@ class ChromiumElement(DrissionElement): return super().next(index, filter_loc, timeout) def before(self, filter_loc='', index=1, timeout=None): - """返回当前元素前面的一个元素,可指定筛选条件和第几个。查找范围不限兄弟元素,而是整个DOM文档 \n + """返回当前元素前面的一个元素,可指定筛选条件和第几个。查找范围不限兄弟元素,而是整个DOM文档 :param filter_loc: 用于筛选元素的查询语法 :param index: 前面第几个查询结果元素 :param timeout: 查找元素的超时时间 @@ -231,7 +230,7 @@ class ChromiumElement(DrissionElement): return super().before(index, filter_loc, timeout) def after(self, filter_loc='', index=1, timeout=None): - """返回当前元素后面的一个元素,可指定筛选条件和第几个。查找范围不限兄弟元素,而是整个DOM文档 \n + """返回当前元素后面的一个元素,可指定筛选条件和第几个。查找范围不限兄弟元素,而是整个DOM文档 :param filter_loc: 用于筛选元素的查询语法 :param index: 后面第几个查询结果元素 :param timeout: 查找元素的超时时间 @@ -240,7 +239,7 @@ class ChromiumElement(DrissionElement): return super().after(index, filter_loc, timeout) def prevs(self, filter_loc='', timeout=0): - """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n + """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 :param filter_loc: 用于筛选元素的查询语法 :param timeout: 查找元素的超时时间 :return: 兄弟元素或节点文本组成的列表 @@ -248,7 +247,7 @@ class ChromiumElement(DrissionElement): return super().prevs(filter_loc, timeout) def nexts(self, filter_loc='', timeout=0): - """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n + """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 :param filter_loc: 用于筛选元素的查询语法 :param timeout: 查找元素的超时时间 :return: 兄弟元素或节点文本组成的列表 @@ -256,7 +255,7 @@ class ChromiumElement(DrissionElement): return super().nexts(filter_loc, timeout) def befores(self, filter_loc='', timeout=None): - """返回当前元素后面符合条件的全部兄弟元素或节点组成的列表,可用查询语法筛选。查找范围不限兄弟元素,而是整个DOM文档 \n + """返回当前元素后面符合条件的全部兄弟元素或节点组成的列表,可用查询语法筛选。查找范围不限兄弟元素,而是整个DOM文档 :param filter_loc: 用于筛选元素的查询语法 :param timeout: 查找元素的超时时间 :return: 本元素前面的元素或节点组成的列表 @@ -264,7 +263,7 @@ class ChromiumElement(DrissionElement): return super().befores(filter_loc, timeout) def afters(self, filter_loc='', timeout=None): - """返回当前元素后面符合条件的全部兄弟元素或节点组成的列表,可用查询语法筛选。查找范围不限兄弟元素,而是整个DOM文档 \n + """返回当前元素后面符合条件的全部兄弟元素或节点组成的列表,可用查询语法筛选。查找范围不限兄弟元素,而是整个DOM文档 :param filter_loc: 用于筛选元素的查询语法 :param timeout: 查找元素的超时时间 :return: 本元素前面的元素或节点组成的列表 @@ -272,7 +271,7 @@ class ChromiumElement(DrissionElement): return super().afters(filter_loc, timeout) def wait_ele(self, loc_or_ele, timeout=None): - """返回用于等待子元素到达某个状态的等待器对象 \n + """返回用于等待子元素到达某个状态的等待器对象 :param loc_or_ele: 可以是元素、查询字符串、loc元组 :param timeout: 等待超时时间 :return: 用于等待的ElementWaiter对象 @@ -323,7 +322,7 @@ class ChromiumElement(DrissionElement): return location_in_viewport(self.page, x, y) if x else False def attr(self, attr): - """返回attribute属性值 \n + """返回attribute属性值 :param attr: 属性名 :return: 属性值文本,没有该属性返回None """ @@ -354,7 +353,7 @@ class ChromiumElement(DrissionElement): return attrs.get(attr, None) def set_attr(self, attr, value): - """设置元素attribute属性 \n + """设置元素attribute属性 :param attr: 属性名 :param value: 属性值 :return: None @@ -362,14 +361,14 @@ class ChromiumElement(DrissionElement): self.run_js(f'this.setAttribute(arguments[0], arguments[1]);', False, attr, str(value)) def remove_attr(self, attr): - """删除元素attribute属性 \n + """删除元素attribute属性 :param attr: 属性名 :return: None """ self.run_js(f'this.removeAttribute("{attr}");') def prop(self, prop): - """获取property属性值 \n + """获取property属性值 :param prop: 属性名 :return: 属性值文本 """ @@ -382,7 +381,7 @@ class ChromiumElement(DrissionElement): return format_html(i['value']['value']) def set_prop(self, prop, value): - """设置元素property属性 \n + """设置元素property属性 :param prop: 属性名 :param value: 属性值 :return: None @@ -391,14 +390,14 @@ class ChromiumElement(DrissionElement): self.run_js(f'this.{prop}="{value}";') def set_innerHTML(self, html): - """设置元素innerHTML \n + """设置元素innerHTML :param html: html文本 :return: None """ self.set_prop('innerHTML', html) def run_js(self, script, as_expr=False, *args): - """运行javascript代码 \n + """运行javascript代码 :param script: js文本 :param as_expr: 是否作为表达式运行,为True时args无效 :param args: 参数,按顺序在js文本中对应argument[0]、argument[1]... @@ -407,7 +406,7 @@ class ChromiumElement(DrissionElement): return run_js(self, script, as_expr, self.page.timeouts.script, args, True) def run_async_js(self, script, as_expr=False, *args): - """以异步方式执行js代码 \n + """以异步方式执行js代码 :param script: js文本 :param as_expr: 是否作为表达式运行,为True时args无效 :param args: 参数,按顺序在js文本中对应argument[0]、argument[2]... @@ -417,7 +416,7 @@ class ChromiumElement(DrissionElement): Thread(target=run_js, args=(self, script, as_expr, self.page.timeouts.script, args, True)).start() def ele(self, loc_or_str, timeout=None): - """返回当前元素下级符合条件的第一个元素、属性或节点文本 \n + """返回当前元素下级符合条件的第一个元素、属性或节点文本 :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致 :return: ChromiumElement对象或属性、文本 @@ -425,7 +424,7 @@ class ChromiumElement(DrissionElement): return self._ele(loc_or_str, timeout) def eles(self, loc_or_str, timeout=None): - """返回当前元素下级所有符合条件的子元素、属性或节点文本 \n + """返回当前元素下级所有符合条件的子元素、属性或节点文本 :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致 :return: ChromiumElement对象或属性、文本组成的列表 @@ -433,7 +432,7 @@ class ChromiumElement(DrissionElement): return self._ele(loc_or_str, timeout=timeout, single=False) def s_ele(self, loc_or_str=None): - """查找第一个符合条件的元素以SessionElement形式返回,处理复杂页面时效率很高 \n + """查找第一个符合条件的元素以SessionElement形式返回,处理复杂页面时效率很高 :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :return: SessionElement对象或属性、文本 """ @@ -442,7 +441,7 @@ class ChromiumElement(DrissionElement): return make_session_ele(self, loc_or_str) def s_eles(self, loc_or_str=None): - """查找所有符合条件的元素以SessionElement列表形式返回 \n + """查找所有符合条件的元素以SessionElement列表形式返回 :param loc_or_str: 定位符 :return: SessionElement或属性、文本组成的列表 """ @@ -451,7 +450,7 @@ class ChromiumElement(DrissionElement): return make_session_ele(self, loc_or_str, single=False) def _ele(self, loc_or_str, timeout=None, single=True, relative=False): - """返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个 \n + """返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个 :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 查找元素超时时间 :param single: True则返回第一个,False则返回全部 @@ -460,7 +459,7 @@ class ChromiumElement(DrissionElement): return find_in_chromium_ele(self, loc_or_str, single, timeout, relative=relative) def style(self, style, pseudo_ele=''): - """返回元素样式属性值,可获取伪元素属性值 \n + """返回元素样式属性值,可获取伪元素属性值 :param style: 样式属性名称 :param pseudo_ele: 伪元素名称(如有) :return: 样式属性的值 @@ -500,7 +499,7 @@ class ChromiumElement(DrissionElement): return data def save(self, path=None, rename=None): - """保存图片或其它有src属性的元素的资源 \n + """保存图片或其它有src属性的元素的资源 :param path: 文件保存路径,为None时保存到当前文件夹 :param rename: 文件名称,为None时从资源url获取 :return: None @@ -518,7 +517,7 @@ class ChromiumElement(DrissionElement): f.write(data) def get_screenshot(self, path=None, as_bytes=None): - """对当前元素截图 \n + """对当前元素截图 :param path: 完整路径,后缀可选 'jpg','jpeg','png','webp' :param as_bytes: 是否已字节形式返回图片,可选 'jpg','jpeg','png','webp',生效时path参数无效 :return: 图片完整路径或字节文本 @@ -539,7 +538,7 @@ class ChromiumElement(DrissionElement): left_top=left_top, right_bottom=right_bottom) def input(self, vals, clear=True): - """输入文本或组合键,也可用于输入文件路径到input元素(文件间用\n间隔) \n + """输入文本或组合键,也可用于输入文件路径到input元素(文件间用\n间隔) :param vals: 文本值或按键组合 :param clear: 输入前是否清空文本框 :return: None @@ -581,7 +580,7 @@ class ChromiumElement(DrissionElement): self.page.run_cdp('DOM.setFileInputFiles', files=files, nodeId=self._node_id, not_change=True) def clear(self, by_js=False): - """清空元素文本 \n + """清空元素文本 :param by_js: 是否用js方式清空 :return: None """ @@ -592,8 +591,8 @@ class ChromiumElement(DrissionElement): self.input(('\ue009', 'a', '\ue017'), clear=False) def click(self, by_js=None, retry=False, timeout=.2, wait_loading=0): - """点击元素 \n - 如果遇到遮挡,会重新尝试点击直到超时,若都失败就改用js点击 \n + """点击元素 + 如果遇到遮挡,会重新尝试点击直到超时,若都失败就改用js点击 :param by_js: 是否用js点击,为True时直接用js点击,为False时重试失败也不会改用js :param retry: 遇到其它元素遮挡时,是否重试 :param timeout: 尝试点击的超时时间,不指定则使用父页面的超时时间,retry为True时才生效 @@ -643,7 +642,7 @@ class ChromiumElement(DrissionElement): return False def click_at(self, offset_x=None, offset_y=None, button='left'): - """带偏移量点击本元素,相对于左上角坐标。不传入x或y值时点击元素左上角可接受点击的点 \n + """带偏移量点击本元素,相对于左上角坐标。不传入x或y值时点击元素左上角可接受点击的点 :param offset_x: 相对元素左上角坐标的x轴偏移量 :param offset_y: 相对元素左上角坐标的y轴偏移量 :param button: 左键还是右键 @@ -660,7 +659,7 @@ class ChromiumElement(DrissionElement): self._click(x, y, 'right') def r_click_at(self, offset_x=None, offset_y=None): - """带偏移量右键单击本元素,相对于左上角坐标。不传入x或y值时点击元素中点 \n + """带偏移量右键单击本元素,相对于左上角坐标。不传入x或y值时点击元素中点 :param offset_x: 相对元素左上角坐标的x轴偏移量 :param offset_y: 相对元素左上角坐标的y轴偏移量 :return: None @@ -674,7 +673,7 @@ class ChromiumElement(DrissionElement): self._click(x, y, 'middle') def _click(self, client_x, client_y, button='left'): - """实施点击 \n + """实施点击 :param client_x: 视口中的x坐标 :param client_y: 视口中的y坐标 :param button: 'left' 或 'right' @@ -686,7 +685,7 @@ class ChromiumElement(DrissionElement): self.page.driver.Input.dispatchMouseEvent(type='mouseReleased', x=client_x, y=client_y, button=button) def hover(self, offset_x=None, offset_y=None): - """鼠标悬停,可接受偏移量,偏移量相对于元素左上角坐标。不传入x或y值时悬停在元素中点 \n + """鼠标悬停,可接受偏移量,偏移量相对于元素左上角坐标。不传入x或y值时悬停在元素中点 :param offset_x: 相对元素左上角坐标的x轴偏移量 :param offset_y: 相对元素左上角坐标的y轴偏移量 :return: None @@ -696,7 +695,7 @@ class ChromiumElement(DrissionElement): self.page.driver.Input.dispatchMouseEvent(type='mouseMoved', x=x, y=y) def drag(self, offset_x=0, offset_y=0, speed=40, shake=True): - """拖拽当前元素到相对位置 \n + """拖拽当前元素到相对位置 :param offset_x: x变化值 :param offset_y: y变化值 :param speed: 拖动的速度,传入0即瞬间到达 @@ -709,7 +708,7 @@ class ChromiumElement(DrissionElement): self.drag_to((offset_x, offset_y), speed, shake) def drag_to(self, ele_or_loc, speed=40, shake=True): - """拖拽当前元素,目标为另一个元素或坐标元组 \n + """拖拽当前元素,目标为另一个元素或坐标元组 :param ele_or_loc: 另一个元素或坐标元组,坐标为元素中点的坐标 :param speed: 拖动的速度,传入0即瞬间到达 :param shake: 是否随机抖动 @@ -747,7 +746,7 @@ class ChromiumElement(DrissionElement): actions.release() def _get_obj_id(self, node_id=None, backend_id=None): - """根据传入node id获取js中的object id \n + """根据传入node id获取js中的object id :param node_id: cdp中的node id :param backend_id: backend id :return: js中的object id @@ -758,7 +757,7 @@ class ChromiumElement(DrissionElement): return self.page.run_cdp('DOM.resolveNode', backendNodeId=backend_id, not_change=True)['object']['objectId'] def _get_node_id(self, obj_id=None, backend_id=None): - """根据传入object id获取cdp中的node id \n + """根据传入object id获取cdp中的node id :param obj_id: js中的object id :param backend_id: backend id :return: cdp中的node id @@ -816,7 +815,7 @@ class ChromiumElement(DrissionElement): return f':root{t}' if mode == 'css' else t def _get_client_rect(self, quad): - """按照类型返回窗口坐标 \n + """按照类型返回窗口坐标 :param quad: 方框类型,margin border padding :return: 四个角坐标,大小为0时返回None """ @@ -857,8 +856,8 @@ class ChromiumShadowRootElement(BaseElement): return f'' def __call__(self, loc_or_str, timeout=None): - """在内部查找元素 \n - 例:ele2 = ele1('@id=ele_id') \n + """在内部查找元素 + 例:ele2 = ele1('@id=ele_id') :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 超时时间 :return: DriverElement对象或属性、文本 @@ -910,7 +909,7 @@ class ChromiumShadowRootElement(BaseElement): return self.run_js('return this.innerHTML;') def run_js(self, script, as_expr=False, *args): - """运行javascript代码 \n + """运行javascript代码 :param script: js文本 :param as_expr: 是否作为表达式运行,为True时args无效 :param args: 参数,按顺序在js文本中对应argument[0]、argument[1]... @@ -919,7 +918,7 @@ class ChromiumShadowRootElement(BaseElement): return run_js(self, script, as_expr, self.page.timeouts.script, args) def run_async_js(self, script, as_expr=False, *args): - """以异步方式执行js代码 \n + """以异步方式执行js代码 :param script: js文本 :param as_expr: 是否作为表达式运行,为True时args无效 :param args: 参数,按顺序在js文本中对应argument[0]、argument[1]... @@ -929,7 +928,7 @@ class ChromiumShadowRootElement(BaseElement): Thread(target=run_js, args=(self, script, as_expr, self.page.timeouts.script, args)).start() def parent(self, level_or_loc=1): - """返回上面某一级父元素,可指定层数或用查询语法定位 \n + """返回上面某一级父元素,可指定层数或用查询语法定位 :param level_or_loc: 第几级父元素,或定位符 :return: ChromiumElement对象 """ @@ -950,7 +949,7 @@ class ChromiumShadowRootElement(BaseElement): return self.parent_ele._ele(loc, timeout=0, relative=True) def next(self, filter_loc='', index=1): - """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n + """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 :param filter_loc: 用于筛选元素的查询语法 :param index: 第几个查询结果元素 :return: ChromiumElement对象 @@ -959,7 +958,7 @@ class ChromiumShadowRootElement(BaseElement): return nodes[index - 1] if nodes else None def before(self, filter_loc='', index=1): - """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n + """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 :param filter_loc: 用于筛选元素的查询语法 :param index: 前面第几个查询结果元素 :return: 本元素前面的某个元素或节点 @@ -968,7 +967,7 @@ class ChromiumShadowRootElement(BaseElement): return nodes[index - 1] if nodes else None def after(self, filter_loc='', index=1): - """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n + """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 :param filter_loc: 用于筛选元素的查询语法 :param index: 后面第几个查询结果元素 :return: 本元素后面的某个元素或节点 @@ -977,7 +976,7 @@ class ChromiumShadowRootElement(BaseElement): return nodes[index - 1] if nodes else None def nexts(self, filter_loc=''): - """返回后面所有兄弟元素或节点组成的列表 \n + """返回后面所有兄弟元素或节点组成的列表 :param filter_loc: 用于筛选元素的查询语法 :return: ChromiumElement对象组成的列表 """ @@ -990,7 +989,7 @@ class ChromiumShadowRootElement(BaseElement): return self.parent_ele._ele(xpath, timeout=0.1, single=False, relative=True) def befores(self, filter_loc=''): - """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n + """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 :param filter_loc: 用于筛选元素的查询语法 :return: 本元素前面的元素或节点组成的列表 """ @@ -1003,7 +1002,7 @@ class ChromiumShadowRootElement(BaseElement): return self.parent_ele._ele(xpath, timeout=0.1, single=False, relative=True) def afters(self, filter_loc=''): - """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n + """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 :param filter_loc: 用于筛选元素的查询语法 :return: 本元素后面的元素或节点组成的列表 """ @@ -1013,7 +1012,7 @@ class ChromiumShadowRootElement(BaseElement): return eles1 + self.parent_ele._ele(xpath, timeout=0.1, single=False, relative=True) def ele(self, loc_or_str, timeout=None): - """返回当前元素下级符合条件的第一个元素 \n + """返回当前元素下级符合条件的第一个元素 :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致 :return: ChromiumElement对象 @@ -1021,7 +1020,7 @@ class ChromiumShadowRootElement(BaseElement): return self._ele(loc_or_str, timeout) def eles(self, loc_or_str, timeout=None): - """返回当前元素下级所有符合条件的子元素 \n + """返回当前元素下级所有符合条件的子元素 :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致 :return: ChromiumElement对象组成的列表 @@ -1029,21 +1028,21 @@ class ChromiumShadowRootElement(BaseElement): return self._ele(loc_or_str, timeout=timeout, single=False) def s_ele(self, loc_or_str=None): - """查找第一个符合条件的元素以SessionElement形式返回,处理复杂页面时效率很高 \n + """查找第一个符合条件的元素以SessionElement形式返回,处理复杂页面时效率很高 :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :return: SessionElement对象或属性、文本 """ return make_session_ele(self, loc_or_str) def s_eles(self, loc_or_str): - """查找所有符合条件的元素以SessionElement列表形式返回,处理复杂页面时效率很高 \n + """查找所有符合条件的元素以SessionElement列表形式返回,处理复杂页面时效率很高 :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :return: SessionElement对象 """ return make_session_ele(self, loc_or_str, single=False) def _ele(self, loc_or_str, timeout=None, single=True, relative=False): - """返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个 \n + """返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个 :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 查找元素超时时间 :param single: True则返回第一个,False则返回全部 diff --git a/DrissionPage/chromium_page.py b/DrissionPage/chromium_page.py index dde5672..50d4d20 100644 --- a/DrissionPage/chromium_page.py +++ b/DrissionPage/chromium_page.py @@ -34,6 +34,7 @@ class ChromiumPage(ChromiumBase): :return: None """ # 接管或启动浏览器 + self._chromium_init() if addr_driver_opts is None or isinstance(addr_driver_opts, (ChromiumOptions, DriverOptions)): self._driver_options = addr_driver_opts or ChromiumOptions() # 从ini文件读取 self.address = self._driver_options.debugger_address @@ -62,9 +63,9 @@ class ChromiumPage(ChromiumBase): else: raise TypeError('只能接收ChromiumDriver或ChromiumOptions类型参数。') - self._init_page(tab_id) self._set_options() self._set_chromium_options() + self._init_page(tab_id) self._get_document() self._first_run = False @@ -75,11 +76,9 @@ class ChromiumPage(ChromiumBase): script=self._driver_options.timeouts['script'], implicit=self._driver_options.timeouts['implicit']) self._page_load_strategy = self._driver_options.page_load_strategy - self.download_set.save_path(self._download_path) def _set_chromium_options(self): """设置浏览器专有的配置""" - self._main_tab = self.tab_id self._alert = Alert() self._window_setter = None @@ -91,6 +90,13 @@ class ChromiumPage(ChromiumBase): super()._init_page(tab_id) self._tab_obj.Page.javascriptDialogOpening = self._on_alert_open self._tab_obj.Page.javascriptDialogClosed = self._on_alert_close + self._main_tab = self.tab_id + try: + self.run_cdp('Browser.setDownloadBehavior', behavior='allow', + downloadPath=self._driver_options.download_path, not_change=True) + except: + self.run_cdp('Page.setDownloadBehavior', behavior='allow', + downloadPath=self._driver_options.download_path, not_change=True) @property def tabs_count(self): @@ -368,7 +374,7 @@ class ChromiumDownloadSetter(DownloadSetter): """用于设置下载参数的类""" def save_path(self, path): - """设置下载路径 \n + """设置下载路径 :param path: 下载路径 :return: None """ diff --git a/DrissionPage/chromium_page.pyi b/DrissionPage/chromium_page.pyi index 85a55e4..f28e21d 100644 --- a/DrissionPage/chromium_page.pyi +++ b/DrissionPage/chromium_page.pyi @@ -16,7 +16,7 @@ from .configs.driver_options import DriverOptions class ChromiumPage(ChromiumBase): def __init__(self, - addr_driver_opts: Union[str, ChromiumDriver, DriverOptions] = None, + addr_driver_opts: Union[str, ChromiumOptions, DriverOptions] = None, tab_id: str = None, timeout: float = None): self._driver_options: [ChromiumDriver, DriverOptions] = ... @@ -51,9 +51,6 @@ class ChromiumPage(ChromiumBase): @property def set_window(self) -> WindowSetter: ... - @property - def download_list(self) -> list: ... - @property def download_set(self) -> ChromiumDownloadSetter: ... diff --git a/DrissionPage/easy_set.py b/DrissionPage/easy_set.py index a133447..bfbe211 100644 --- a/DrissionPage/easy_set.py +++ b/DrissionPage/easy_set.py @@ -27,6 +27,7 @@ def show_settings(ini_path=None): def set_paths(driver_path=None, + chrome_path=None, browser_path=None, local_port=None, debugger_address=None, @@ -35,8 +36,9 @@ def set_paths(driver_path=None, cache_path=None, ini_path=None, check_version=False): - """快捷的路径设置函数 \n + """快捷的路径设置函数 :param driver_path: chromedriver.exe路径 + :param chrome_path: 浏览器可执行文件路径 :param browser_path: 浏览器可执行文件路径 :param local_port: 本地端口号 :param debugger_address: 调试浏览器地址,例:127.0.0.1:9222 @@ -55,6 +57,9 @@ def set_paths(driver_path=None, if driver_path is not None: om.set_item('paths', 'chromedriver_path', format_path(driver_path)) + if chrome_path is not None: + om.set_item('chrome_options', 'binary_location', format_path(browser_path)) + if browser_path is not None: om.set_item('chrome_options', 'binary_location', format_path(browser_path)) @@ -80,7 +85,7 @@ def set_paths(driver_path=None, def set_argument(arg, value=None, ini_path=None): - """设置浏览器配置argument属性 \n + """设置浏览器配置argument属性 :param arg: 属性名 :param value: 属性值,有值的属性传入值,没有的传入None :param ini_path: 要修改的ini文件路径 @@ -92,7 +97,7 @@ def set_argument(arg, value=None, ini_path=None): def set_headless(on_off=True, ini_path=None): - """设置是否隐藏浏览器界面 \n + """设置是否隐藏浏览器界面 :param on_off: 开或关 :param ini_path: 要修改的ini文件路径 :return: None @@ -102,7 +107,7 @@ def set_headless(on_off=True, ini_path=None): def set_no_imgs(on_off=True, ini_path=None): - """设置是否禁止加载图片 \n + """设置是否禁止加载图片 :param on_off: 开或关 :param ini_path: 要修改的ini文件路径 :return: None @@ -112,7 +117,7 @@ def set_no_imgs(on_off=True, ini_path=None): def set_no_js(on_off=True, ini_path=None): - """设置是否禁用js \n + """设置是否禁用js :param on_off: 开或关 :param ini_path: 要修改的ini文件路径 :return: None @@ -122,7 +127,7 @@ def set_no_js(on_off=True, ini_path=None): def set_mute(on_off=True, ini_path=None): - """设置是否静音 \n + """设置是否静音 :param on_off: 开或关 :param ini_path: 要修改的ini文件路径 :return: None @@ -132,7 +137,7 @@ def set_mute(on_off=True, ini_path=None): def set_user_agent(user_agent, ini_path=None): - """设置user agent \n + """设置user agent :param user_agent: user agent文本 :param ini_path: 要修改的ini文件路径 :return: None @@ -141,7 +146,7 @@ def set_user_agent(user_agent, ini_path=None): def set_proxy(proxy, ini_path=None): - """设置代理 \n + """设置代理 :param proxy: 代理网址和端口 :param ini_path: 要修改的ini文件路径 :return: None @@ -150,7 +155,7 @@ def set_proxy(proxy, ini_path=None): def check_driver_version(driver_path=None, chrome_path=None): - """检查传入的chrome和chromedriver是否匹配 \n + """检查传入的chrome和chromedriver是否匹配 :param driver_path: chromedriver.exe路径 :param chrome_path: chrome.exe路径 :return: 是否匹配 @@ -185,7 +190,7 @@ def get_match_driver(ini_path='default', chrome_path=None, show_msg=True, check_version=True): - """自动识别chrome版本并下载匹配的driver \n + """自动识别chrome版本并下载匹配的driver :param ini_path: 要读取和修改的ini文件路径 :param save_path: chromedriver保存路径 :param chrome_path: 指定chrome.exe位置 @@ -237,7 +242,7 @@ def get_chrome_path(ini_path=None, from_ini=True, from_regedit=True, from_system_path=True): - """从ini文件或系统变量中获取chrome.exe的路径 \n + """从ini文件或系统变量中获取chrome.exe的路径 :param ini_path: ini文件路径 :param show_msg: 是否打印信息 :param from_ini: 是否从ini文件获取 @@ -310,7 +315,7 @@ def get_chrome_path(ini_path=None, def _get_chrome_version(path: str) -> Union[str, None]: - """根据文件路径获取版本号 \n + """根据文件路径获取版本号 :param path: chrome.exe文件路径 :return: 版本号 """ @@ -327,7 +332,7 @@ def _get_chrome_version(path: str) -> Union[str, None]: def _download_driver(version: str, save_path: str = None, show_msg: bool = True) -> Union[str, None]: - """根据传入的版本号到镜像网站查找,下载最相近的 \n + """根据传入的版本号到镜像网站查找,下载最相近的 :param version: 本地版本号 :return: 保存地址 """ diff --git a/DrissionPage/functions/web.py b/DrissionPage/functions/web.py index 95a2907..09b12f1 100644 --- a/DrissionPage/functions/web.py +++ b/DrissionPage/functions/web.py @@ -79,7 +79,7 @@ def get_ele_txt(e): def format_html(text): - """处理html编码字符 \n + """处理html编码字符 :param text: html文本 :return: 格式化后的html文本 """ @@ -106,7 +106,7 @@ def location_in_viewport(page, loc_x, loc_y): def offset_scroll(ele, offset_x, offset_y): - """接收元素及偏移坐标,把坐标滚动到页面中间,返回该点在视口中的坐标 \n + """接收元素及偏移坐标,把坐标滚动到页面中间,返回该点在视口中的坐标 有偏移量时以元素左上角坐标为基准,没有时以_click_point为基准 :param ele: 元素对象 :param offset_x: 偏移量x @@ -164,7 +164,7 @@ def is_js_func(func): def cookie_to_dict(cookie): - """把Cookie对象转为dict格式 \n + """把Cookie对象转为dict格式 :param cookie: Cookie对象 :return: cookie字典 """ @@ -199,7 +199,7 @@ def cookie_to_dict(cookie): def cookies_to_tuple(cookies): - """把cookies转为tuple格式 \n + """把cookies转为tuple格式 :param cookies: cookies信息,可为CookieJar, list, tuple, str, dict :return: 返回tuple形式的cookies """ diff --git a/DrissionPage/mix_page.py b/DrissionPage/mix_page.py index 9196efa..cd2c574 100644 --- a/DrissionPage/mix_page.py +++ b/DrissionPage/mix_page.py @@ -39,7 +39,7 @@ class MixPage(SessionPage, DriverPage, BasePage): if self._mode == 'd': try: timeouts = self.drission.driver_options.timeouts - t = timeout if timeout is not None else timeouts['implicit'] + t = timeout if isinstance(timeout, (int, float)) else timeouts['implicit'] self.set_timeouts(t, timeouts['pageLoad'], timeouts['script']) except Exception: diff --git a/DrissionPage/web_page.py b/DrissionPage/web_page.py index 5450fc9..dd9e3a7 100644 --- a/DrissionPage/web_page.py +++ b/DrissionPage/web_page.py @@ -38,24 +38,23 @@ class WebPage(SessionPage, ChromiumPage, BasePage): self._debug = False self._debug_recorder = None + self.address = None self._session = None self._tab_obj = None - self._is_loading = False - self._driver_options = None self._session_options = None - - self._set_both_options(driver_or_options, session_or_options) self._setting_tab_id = tab_id self._response = None self._download_kit = None self._download_set = None + self._set_both_options(driver_or_options, session_or_options) + if self._mode == 'd': self._to_d_mode() - t = timeout if timeout is not None else self.timeouts.implicit + t = timeout if isinstance(timeout, (int, float)) else self.timeouts.implicit super(ChromiumBase, self).__init__(t) # 调用Base的__init__() def _set_both_options(self, dr_opt, se_opt): @@ -64,10 +63,11 @@ class WebPage(SessionPage, ChromiumPage, BasePage): :param se_opt: Session、SessionOptions对象或配置信息,为None则从ini读取,为False用默认信息创建 :return: None """ + # 浏览器配置 if isinstance(dr_opt, ChromiumDriver): self._connect_browser(dr_opt) self._has_driver = True - self._driver_options = None + # self._driver_options = None dr_opt = False else: @@ -81,12 +81,13 @@ class WebPage(SessionPage, ChromiumPage, BasePage): self._driver_options = dr_opt else: - raise TypeError('driver_or_options参数只能接收ChromiumDriver, ChromiumOptionsOptions、None或False。') + raise TypeError('driver_or_options参数只能接收ChromiumDriver, ChromiumOptions、None或False。') + # Session配置 if isinstance(se_opt, Session): self._session = se_opt self._has_session = True - self._session_options = None + self._session_options = SessionOptions(read_file=False) se_opt = False else: @@ -102,13 +103,18 @@ class WebPage(SessionPage, ChromiumPage, BasePage): else: raise TypeError('session_or_options参数只能接收Session, SessionOptions、None或False。') + # 通用配置 self._timeouts = Timeout(self) + self._page_load_strategy = self._driver_options.page_load_strategy + self._download_path = None if se_opt is not False: self.set_timeouts(implicit=self._session_options.timeout) + self._download_path = self._session_options.download_path if dr_opt is not False: t = self._driver_options.timeouts self.set_timeouts(t['implicit'], t['pageLoad'], t['script']) + self._download_path = self._driver_options.download_path def _set_options(self): """覆盖父类同名方法""" @@ -232,7 +238,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): return self._download_set def get(self, url, show_errmsg=False, retry=None, interval=None, timeout=None, **kwargs): - """跳转到一个url \n + """跳转到一个url \n :param url: 目标url :param show_errmsg: 是否显示和抛出异常 :param retry: 重试次数 @@ -249,7 +255,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): return super().get(url, show_errmsg, retry, interval, timeout, **kwargs) def ele(self, loc_or_ele, timeout=None): - """返回第一个符合条件的元素、属性或节点文本 \n + """返回第一个符合条件的元素、属性或节点文本 \n :param loc_or_ele: 元素的定位信息,可以是元素对象,loc元组,或查询字符串 :param timeout: 查找元素超时时间,默认与页面等待时间一致 :return: 元素对象或属性、文本节点文本 @@ -260,7 +266,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): return super(SessionPage, self).ele(loc_or_ele, timeout=timeout) def eles(self, loc_or_str, timeout=None): - """返回页面中所有符合条件的元素、属性或节点文本 \n + """返回页面中所有符合条件的元素、属性或节点文本 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 查找元素超时时间,默认与页面等待时间一致 :return: 元素对象或属性、文本组成的列表 @@ -291,9 +297,9 @@ class WebPage(SessionPage, ChromiumPage, BasePage): return super(SessionPage, self).s_eles(loc_or_str) def change_mode(self, mode=None, go=True, copy_cookies=True): - """切换模式,接收's'或'd',除此以外的字符串会切换为 d 模式 \n - 如copy_cookies为True,切换时会把当前模式的cookies复制到目标模式 \n - 切换后,如果go是True,调用相应的get函数使访问的页面同步 \n + """切换模式,接收's'或'd',除此以外的字符串会切换为 d 模式 \n + 如copy_cookies为True,切换时会把当前模式的cookies复制到目标模式 \n + 切换后,如果go是True,调用相应的get函数使访问的页面同步 \n :param mode: 模式字符串 :param go: 是否跳转到原模式的url :param copy_cookies: 是否复制cookies到目标模式 @@ -333,7 +339,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): self.get(url) def cookies_to_session(self, copy_user_agent=True): - """把driver对象的cookies复制到session对象 \n + """把driver对象的cookies复制到session对象 \n :param copy_user_agent: 是否复制ua信息 :return: None """ @@ -357,7 +363,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): self.set_cookies(cookies, set_driver=True) def get_cookies(self, as_dict=False, all_domains=False): - """返回cookies \n + """返回cookies \n :param as_dict: 是否以字典方式返回 :param all_domains: 是否返回所有域的cookies :return: cookies信息 @@ -368,6 +374,10 @@ class WebPage(SessionPage, ChromiumPage, BasePage): return self._get_driver_cookies(as_dict) def _get_driver_cookies(self, as_dict=False): + """获取浏览器cookies \n + :param as_dict: 以dict形式返回 + :return: cookies信息 + """ cookies = self._tab_obj.Network.getCookies()['cookies'] if as_dict: return {cookie['name']: cookie['value'] for cookie in cookies} @@ -399,7 +409,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): super().set_cookies(cookies) def set_headers(self, headers: dict) -> None: - """设置固定发送的headers \n + """设置固定发送的headers \n :param headers: dict格式的headers数据 :return: None """ @@ -429,7 +439,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): # ----------------重写SessionPage的函数----------------------- def post(self, url: str, data=None, show_errmsg=False, retry=None, interval=None, **kwargs): - """用post方式跳转到url,会切换到s模式 \n + """用post方式跳转到url,会切换到s模式 \n :param url: 目标url :param data: post方式时提交的数据 :param show_errmsg: 是否显示和抛出异常 @@ -482,11 +492,10 @@ class WebPageDownloadSetter(ChromiumDownloadSetter): self._behavior = 'allow' def save_path(self, path): - """设置下载路径 \n + """设置下载路径 :param path: 下载路径 :return: None """ - # todo: 设置时判断模式,初始设置 path = path or '' path = Path(path).absolute() path.mkdir(parents=True, exist_ok=True) @@ -506,14 +515,15 @@ class WebPageDownloadSetter(ChromiumDownloadSetter): raise RuntimeError('浏览器未连接。') self._page.driver.Page.downloadWillBegin = None self._page.driver.Browser.downloadWillBegin = None - self._page.driver.Browser.setDownloadBehavior(behavior='allow') + self._page.driver.Browser.setDownloadBehavior(behavior='allow', downloadPath=self._page.download_path) self._behavior = 'allow' def use_DownloadKit(self): """设置使用DownloadKit下载文件""" - self._page.driver.Page.downloadWillBegin = self._download_by_DownloadKit - self._page.driver.Browser.downloadWillBegin = self._download_by_DownloadKit - self._page.driver.Browser.setDownloadBehavior(behavior='deny') + if self._page._has_driver: + self._page.driver.Page.downloadWillBegin = self._download_by_DownloadKit + self._page.driver.Browser.downloadWillBegin = self._download_by_DownloadKit + self._page.driver.Browser.setDownloadBehavior(behavior='deny') self._behavior = 'deny' def _download_by_DownloadKit(self, **kwargs): @@ -521,5 +531,5 @@ class WebPageDownloadSetter(ChromiumDownloadSetter): self._page.run_cdp('Browser.cancelDownload', guid=gid, not_change=True) url = kwargs['url'] name = kwargs['suggestedFilename'] - print(f'开始下载:{url}') + print(f'下载:{url}') self._page.download.add(url, goal_path=self._page.download_path, rename=name) diff --git a/DrissionPage/web_page.pyi b/DrissionPage/web_page.pyi index 9175260..d75e35c 100644 --- a/DrissionPage/web_page.pyi +++ b/DrissionPage/web_page.pyi @@ -31,11 +31,13 @@ class WebPage(SessionPage, ChromiumPage, BasePage): self._mode: str = ... self._has_driver: bool = ... self._has_session: bool = ... + self.address: str = ... self._session_options: Union[SessionOptions, None] = ... - self._driver_options: Union[DriverOptions, None] = ... + self._driver_options: Union[ChromiumOptions, DriverOptions, None] = ... self._setting_tab_id: str = ... self._download_kit: DownloadKit = ... self._download_set: WebPageDownloadSetter = ... + self._download_path: str = ... def __call__(self, loc_or_str: Union[Tuple[str, str], str, ChromiumElement, SessionElement],