From 366e03027eda9c7d7e4074d8452ad3634224e44a Mon Sep 17 00:00:00 2001 From: g1879 Date: Sun, 20 Mar 2022 23:26:44 +0800 Subject: [PATCH] 2.5.7 --- DrissionPage/config.py | 47 +++++++++- DrissionPage/drission.py | 7 +- DrissionPage/driver_element.py | 58 ++++++++---- DrissionPage/driver_page.py | 5 +- docs/使用方法/元素操作.md | 56 +++++------ docs/使用方法/创建页面对象.md | 109 +++++++++++++++++++++- docs/使用方法/启动配置/Session启动配置.md | 3 - docs/入门指南/快速上手.md | 5 +- docs/版本历史.md | 15 ++- setup.py | 2 +- 10 files changed, 242 insertions(+), 65 deletions(-) diff --git a/DrissionPage/config.py b/DrissionPage/config.py index e5ba3d6..db07e88 100644 --- a/DrissionPage/config.py +++ b/DrissionPage/config.py @@ -8,7 +8,7 @@ from configparser import RawConfigParser, NoSectionError, NoOptionError from http.cookiejar import Cookie from pathlib import Path -from typing import Any, Union +from typing import Any, Union, List from requests.cookies import RequestsCookieJar from selenium.webdriver.chrome.options import Options @@ -493,6 +493,51 @@ class DriverOptions(Options): """浏览器启动文件路径""" return self.binary_location + # -------------重写父类方法,实现链式操作------------- + def add_argument(self, argument) -> 'DriverOptions': + """添加一个配置项 \n + :param argument: 配置项内容 + :return: 当前对象 + """ + super().add_argument(argument) + return self + + def set_capability(self, name, value) -> 'DriverOptions': + """设置一个capability \n + :param name: capability名称 + :param value: capability值 + :return: 当前对象 + """ + super().set_capability(name, value) + return self + + def add_extension(self, extension: str) -> 'DriverOptions': + """添加插件 \n + :param extension: crx文件路径 + :return: 当前对象 + """ + super().add_extension(extension) + return self + + def add_encoded_extension(self, extension: str) -> 'DriverOptions': + """将带有扩展数据的 Base64 编码字符串添加到将用于将其提取到 ChromeDriver 的列表中 \n + :param extension: 带有扩展数据的 Base64 编码字符串 + :return: 当前对象 + """ + super().add_encoded_extension(extension) + return self + + def add_experimental_option(self, name: str, value: Union[str, int, dict, List[str]]) -> 'DriverOptions': + """添加一个实验选项到浏览器 \n + :param name: 选项名称 + :param value: 选项值 + :return: 当前对象 + """ + super().add_experimental_option(name, value) + return self + + # -------------重写父类方法结束------------- + def save(self, path: str = None) -> str: """保存设置到文件 \n :param path: ini文件的路径, None 保存到当前读取的配置文件,传入 'default' 保存到默认ini文件 diff --git a/DrissionPage/drission.py b/DrissionPage/drission.py index 07d16db..14c4ec0 100644 --- a/DrissionPage/drission.py +++ b/DrissionPage/drission.py @@ -13,6 +13,7 @@ from selenium import webdriver from selenium.common.exceptions import SessionNotCreatedException, WebDriverException from selenium.webdriver.chrome.options import Options from selenium.webdriver.chrome.webdriver import WebDriver +from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver from tldextract import extract from .config import _session_options_to_dict, SessionOptions, DriverOptions, _cookies_to_tuple @@ -22,7 +23,7 @@ class Drission(object): """Drission类用于管理WebDriver对象和Session对象,是驱动器的角色""" def __init__(self, - driver_or_options: Union[WebDriver, Options, DriverOptions, bool] = None, + driver_or_options: Union[RemoteWebDriver, Options, DriverOptions, bool] = None, session_or_options: Union[Session, dict, SessionOptions, bool] = None, ini_path: str = None, proxy: dict = None): @@ -65,7 +66,7 @@ class Drission(object): elif driver_or_options is False: self._driver_options = DriverOptions(read_file=False) - elif isinstance(driver_or_options, WebDriver): + elif isinstance(driver_or_options, RemoteWebDriver): self._driver = driver_or_options elif isinstance(driver_or_options, (Options, DriverOptions)): @@ -383,7 +384,7 @@ class Drission(object): self.close_session() -def user_agent_to_session(driver: WebDriver, session: Session) -> None: +def user_agent_to_session(driver: RemoteWebDriver, session: Session) -> None: """把driver的user-agent复制到session \n :param driver: 来源driver对象 :param session: 目标session对象 diff --git a/DrissionPage/driver_element.py b/DrissionPage/driver_element.py index 8b6be1e..c312fa6 100644 --- a/DrissionPage/driver_element.py +++ b/DrissionPage/driver_element.py @@ -6,11 +6,13 @@ """ from os import sep from pathlib import Path -from time import time, perf_counter +from time import time, perf_counter, sleep from typing import Union, List, Any, Tuple -from selenium.common.exceptions import TimeoutException, JavascriptException, InvalidElementStateException -from selenium.webdriver.chrome.webdriver import WebDriver +from selenium.common.exceptions import TimeoutException, JavascriptException, InvalidElementStateException, \ + NoSuchElementException +# from selenium.webdriver.chrome.webdriver import WebDriver +from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver from selenium.webdriver.remote.webelement import WebElement from selenium.webdriver.support import expected_conditions as ec from selenium.webdriver.support.wait import WebDriverWait @@ -872,7 +874,7 @@ class ElementsByXpath(object): self.single = single self.timeout = timeout - def __call__(self, ele_or_driver: Union[WebDriver, WebElement]) \ + def __call__(self, ele_or_driver: Union[RemoteWebDriver, WebElement]) \ -> Union[str, DriverElement, None, List[str or DriverElement]]: def get_nodes(node=None, xpath_txt=None, type_txt='7'): @@ -921,7 +923,7 @@ class ElementsByXpath(object): """ return driver.execute_script(js, node, xpath_txt) - if isinstance(ele_or_driver, WebDriver): + if isinstance(ele_or_driver, RemoteWebDriver): driver, the_node = ele_or_driver, 'document' else: driver, the_node = ele_or_driver.parent, ele_or_driver @@ -969,12 +971,14 @@ class Select(object): self.inner_ele = ele self.select_ele = Select(ele.inner_ele) - def __call__(self, text_or_index: Union[str, int, list, tuple]) -> bool: + def __call__(self, text_or_index: Union[str, int, list, tuple], timeout=None) -> bool: """选定下拉列表中子元素 \n :param text_or_index: 根据文本、值选或序号择选项,若允许多选,传入list或tuple可多选 + :param timeout: 超时时间,不输入默认实用页面超时时间 :return: None """ - return self.select(text_or_index) + timeout = timeout if timeout is not None else self.inner_ele.page.timeout + return self.select(text_or_index, timeout=timeout) @property def is_multi(self) -> bool: @@ -1005,35 +1009,43 @@ class Select(object): """清除所有已选项""" self.select_ele.deselect_all() - def select(self, text_or_index: Union[str, int, list, tuple]) -> bool: + def select(self, text_or_index: Union[str, int, list, tuple], timeout=None) -> bool: """选定下拉列表中子元素 \n :param text_or_index: 根据文本、值选或序号择选项,若允许多选,传入list或tuple可多选 + :param timeout: 超时时间,不输入默认实用页面超时时间 :return: 是否选择成功 """ i = 'index' if isinstance(text_or_index, int) else 'text' - return self._select(text_or_index, i, False) + timeout = timeout if timeout is not None else self.inner_ele.page.timeout + return self._select(text_or_index, i, False, timeout) - def select_by_value(self, value: Union[str, list, tuple]) -> bool: + def select_by_value(self, value: Union[str, list, tuple], timeout=None) -> bool: """此方法用于根据value值选择项。当元素是多选列表时,可以接收list或tuple \n :param value: value属性值,传入list或tuple可选择多项 + :param timeout: 超时时间,不输入默认实用页面超时时间 :return: None """ - return self._select(value, 'value', False) + timeout = timeout if timeout is not None else self.inner_ele.page.timeout + return self._select(value, 'value', False, timeout) - def deselect(self, text_or_index: Union[str, int, list, tuple]) -> bool: + def deselect(self, text_or_index: Union[str, int, list, tuple], timeout=None) -> bool: """取消选定下拉列表中子元素 \n :param text_or_index: 根据文本或序号取消择选项,若允许多选,传入list或tuple可取消多项 + :param timeout: 超时时间,不输入默认实用页面超时时间 :return: None """ i = 'index' if isinstance(text_or_index, int) else 'text' - return self._select(text_or_index, i, True) + timeout = timeout if timeout is not None else self.inner_ele.page.timeout + return self._select(text_or_index, i, True, timeout) - def deselect_by_value(self, value: Union[str, list, tuple]) -> bool: + def deselect_by_value(self, value: Union[str, list, tuple], timeout=None) -> bool: """此方法用于根据value值取消选择项。当元素是多选列表时,可以接收list或tuple \n :param value: value属性值,传入list或tuple可取消多项 + :param timeout: 超时时间,不输入默认实用页面超时时间 :return: None """ - return self._select(value, 'value', True) + timeout = timeout if timeout is not None else self.inner_ele.page.timeout + return self._select(value, 'value', True, timeout) def invert(self) -> None: """反选""" @@ -1046,7 +1058,8 @@ class Select(object): def _select(self, text_value_index: Union[str, int, list, tuple] = None, para_type: str = 'text', - deselect: bool = False) -> bool: + deselect: bool = False, + timeout=None) -> bool: """选定或取消选定下拉列表中子元素 \n :param text_value_index: 根据文本、值选或序号择选项,若允许多选,传入list或tuple可多选 :param para_type: 参数类型,可选 'text'、'value'、'index' @@ -1056,7 +1069,7 @@ class Select(object): if not self.is_multi and isinstance(text_value_index, (list, tuple)): raise TypeError('单选下拉列表不能传入list和tuple') - if isinstance(text_value_index, (str, int)): + def do_select(): try: if para_type == 'text': if deselect: @@ -1075,11 +1088,20 @@ class Select(object): self.select_ele.select_by_index(int(text_value_index)) else: raise ValueError('para_type参数只能传入"text"、"value"或"index"。') + return True - except Exception: + except NoSuchElementException: return False + if isinstance(text_value_index, (str, int)): + t1 = perf_counter() + ok = do_select() + while not ok and perf_counter() - t1 < timeout: + sleep(.2) + ok = do_select() + return ok + elif isinstance(text_value_index, (list, tuple)): return self._select_multi(text_value_index, para_type, deselect) diff --git a/DrissionPage/driver_page.py b/DrissionPage/driver_page.py index e0d0d2f..6110a1a 100644 --- a/DrissionPage/driver_page.py +++ b/DrissionPage/driver_page.py @@ -13,6 +13,7 @@ from urllib.parse import quote from selenium.common.exceptions import NoAlertPresentException from selenium.webdriver.chrome.webdriver import WebDriver +from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver from selenium.webdriver.remote.webelement import WebElement from selenium.webdriver.support.wait import WebDriverWait @@ -25,7 +26,7 @@ from .session_element import make_session_ele, SessionElement class DriverPage(BasePage): """DriverPage封装了页面操作的常用功能,使用selenium来获取、解析、操作网页""" - def __init__(self, driver: WebDriver, timeout: float = 10) -> None: + def __init__(self, driver: RemoteWebDriver, timeout: float = 10) -> None: """初始化函数,接收一个WebDriver对象,用来操作网页""" super().__init__(timeout) self._driver = driver @@ -302,7 +303,7 @@ class DriverPage(BasePage): """等待元素从dom删除、显示、隐藏 \n :param loc_or_ele: 可以是元素、查询字符串、loc元组 :param timeout: 等待超时时间 - :return: 等待是否成功 + :return: 用于等待的ElementWaiter对象 """ return ElementWaiter(self, loc_or_ele, timeout) diff --git a/docs/使用方法/元素操作.md b/docs/使用方法/元素操作.md index 329a0ad..5ba1afc 100644 --- a/docs/使用方法/元素操作.md +++ b/docs/使用方法/元素操作.md @@ -30,8 +30,6 @@ ele.click(timeout = 10) # 不断重试点击,直到遮罩层消失,或到 ele.click(by_js=True) # 无视遮罩层,直接用 js 点击下方元素 ``` - - ## click_at() 此方法用于带偏移量点击元素,偏移量相对于元素左上角坐标。不传入`x`或`y`值时点击元素中点。可选择是否用 js 方式点击,但不会进行重试。 @@ -57,8 +55,6 @@ ele.click_at(x=50) ele.click_at() ``` - - ## r_click() 此方法实现右键单击元素。 @@ -71,8 +67,6 @@ ele.click_at() ele.r_click() ``` - - ## r_click_at() 此方法用于带偏移量右键点击元素,用法和`click_at()`相似,但没有`by_js`参数。 @@ -89,8 +83,6 @@ ele.r_click() ele.r_click_at(50, 50) ``` - - ## input() 此方法用于向元素输入文本或组合键,也可用于输入文件路径到`input`元素(文件间用`\n`间隔)。可选择输入前是否清空元素。 @@ -166,11 +158,11 @@ ele.run_script('arguments[0].click()') 方法: -| 方法 | 参数说明 | 功能 | -| :-------: | :------: | :-----------------: | -| display() | 无 | 等待元素从 DOM 显示 | -| hidden() | 无 | 等待元素从 DOM 隐藏 | -| delete() | 无 | 等待元素从 DOM 删除 | +| 方法 | 参数说明 | 功能 | +|:---------:|:----:|:------------:| +| display() | 无 | 等待元素从 DOM 显示 | +| hidden() | 无 | 等待元素从 DOM 隐藏 | +| delete() | 无 | 等待元素从 DOM 删除 | 这些方法返回布尔值,代表是否等待成功。 @@ -186,8 +178,6 @@ ele2 = ele1.ele('#div1') ele1.wait_ele(ele2).hidden() ``` - - ## screenshot() 此方法用于对元素进行截图。 @@ -206,8 +196,6 @@ ele1.wait_ele(ele2).hidden() path = ele.screenshot(r'D:\tmp', 'img_name') ``` - - ## set_prop() 此方法用于设置元素`property`属性。 @@ -309,18 +297,18 @@ ele1.drag_to((50, 50)) 此属性用于以某种方式滚动元素中的滚动条。 调用此属性返回一个`Scroll`对象,调用该对象方法实现各种方式的滚动。 -| 方法 | 参数说明 | 功能 | -| :---------------: | :----------: | :------------------------------: | -| to_top() | 无 | 滚动到顶端,水平位置不变 | -| to_bottom() | 无 | 滚动到底端,水平位置不变 | -| to_half() | 无 | 滚动到垂直中间位置,水平位置不变 | -| to_rightmost() | 无 | 滚动到最右边,垂直位置不变 | -| to_leftmost() | 无 | 滚动到最左边,垂直位置不变 | -| to_location(x, y) | 滚动条坐标值 | 滚动到指定位置 | -| up(pixel) | 滚动的像素 | 向上滚动若干像素,水平位置不变 | -| down(pixel) | 滚动的像素 | 向下滚动若干像素,水平位置不变 | -| right(pixel) | 滚动的像素 | 向左滚动若干像素,垂直位置不变 | -| left(pixel) | 滚动的像素 | 向右滚动若干像素,垂直位置不变 | +| 方法 | 参数说明 | 功能 | +|:-----------------:|:------:|:----------------:| +| to_top() | 无 | 滚动到顶端,水平位置不变 | +| to_bottom() | 无 | 滚动到底端,水平位置不变 | +| to_half() | 无 | 滚动到垂直中间位置,水平位置不变 | +| to_rightmost() | 无 | 滚动到最右边,垂直位置不变 | +| to_leftmost() | 无 | 滚动到最左边,垂直位置不变 | +| to_location(x, y) | 滚动条坐标值 | 滚动到指定位置 | +| up(pixel) | 滚动的像素 | 向上滚动若干像素,水平位置不变 | +| down(pixel) | 滚动的像素 | 向下滚动若干像素,水平位置不变 | +| right(pixel) | 滚动的像素 | 向左滚动若干像素,垂直位置不变 | +| left(pixel) | 滚动的像素 | 向右滚动若干像素,垂直位置不变 | ```python # 滚动到底部 @@ -367,6 +355,8 @@ ele.hover() ele.select ``` +?> **Tips:**
网页操作中经常遇到动态变化的表单,这时需要等待列表项加载。`Select`类内置等待功能,默认为页面等待时间。 + ## 属性 ### is_multi @@ -413,12 +403,13 @@ Select 类的`__call__()`方法直接调用这个方法,因此可以直接`ele 参数: - text_or_index:根据文本或序号择选项,若允许多选,传入`list`或`tuple`可多选 +- timeout:等待列表项出现的时间 返回:是否选择成功 ```python -# 根据文本选择下拉列表项 -ele.select('text') +# 根据文本选择下拉列表项,等待1秒 +ele.select('text', timeout=1) # 选择第一个下拉列表项 ele.select(0) @@ -438,6 +429,7 @@ ele.select(('text1', 2)) 参数: - value:`value`属性值,若允许多选,传入`list`或`tuple`可多选 +- timeout:等待列表项出现的时间 返回:是否选择成功 @@ -458,6 +450,7 @@ ele.select.select_by_value(('value1', 2)) 参数: - text_or_index:根据文本、值选或序号择选项,若允许多选,传入`list`或`tuple`可多选 +- timeout:等待列表项出现的时间 返回:是否取消选择成功 @@ -485,6 +478,7 @@ ele.select.deselect(('text1', 2)) 参数: - value:`value`属性值,若允许多选,传入`list`或`tuple`可取消多项 +- timeout:等待列表项出现的时间 返回:是否取消选择成功 diff --git a/docs/使用方法/创建页面对象.md b/docs/使用方法/创建页面对象.md index e9af9f5..bad169c 100644 --- a/docs/使用方法/创建页面对象.md +++ b/docs/使用方法/创建页面对象.md @@ -16,8 +16,7 @@ - driver_options:浏览器设置,没传入`drission`参数时会用这个设置新建`Drission`对象中的`WebDriver`对象,传入`False`则不创建 - session_options:requests 设置,没传入`drission`参数时会用这个设置新建`Drission`对象中的`Session`对象,传入`False`则不创建 -!> **注意:**
-有传入`drission`参数时,`driver_options`和`session_options`参数无效 +!> **注意:**
有传入`drission`参数时,`driver_options`和`session_options`参数无效 # 直接创建 @@ -159,3 +158,109 @@ d = Drission(driver_or_options=do, session_or_options=so) page = MixPage(drission=d) ``` +# 接管手动打开的浏览器 + +如果须要手动打开浏览器,手动操作后再接管,可以这样做: + +- 右键点击浏览器图标,选择属性 + +- 在“目标”路径后面加上` --remote-debugging-port=端口号`(注意最前面有个空格) + +- 点击确定 + +- 在程序中的浏览器配置中指定接管该端口浏览器,如下: + +``` +# 文件快捷方式的目标路径设置 +D:\chrome.exe --remote-debugging-port=9222 +``` + +```python +from DrissionPage.configs import DriverOptions +from DrissionPage import MixPage + + +do = DriverOptions().set_paths(local_port=9222) +page = MixPage(driver_options=do) +``` + +?>**Tips:**
接管使用 bat 文件打开的浏览器也是一样做法做法。
接管浏览器时只有`local_port`、`debugger_address`、`driver_path`参数是有效的。 + +# 多 Chrome 浏览器共存 + +如果想要同时操作多个 Chrome 浏览器,或者自己在使用 Chrome 上网,同时控制另外几个跑自动化,就须要给这些被程序控制的浏览器设置单独的端口和用户文件夹,否则会造成冲突。具体用`DriverOptions`对象进行设置,示例如下: + +```python +from DrissionPage.confing import DriverOptions + +do1 = DriverOptions().set_paths(local_port=9111, user_path=r'D:\data1') +do2 = DriverOptions().set_paths(local_port=9222, user_path=r'D:\data2') + +page1 = MixPage(driver_options=do1) +page2 = MixPage(driver_options=do2) + +page1.get('https://www.baidu.com') +page2.get('http://www.163.com') +``` + +如果要接管多个手动打开的浏览器,每个浏览器后面的参数都要添加`--remote-debugging-port`和`--user-data-dir`参数。示例如下: + +``` +# 浏览器1目标设置 +D:\chrome.exe --remote-debugging-port=9111 --user-data-dir=D:\data1 + +# 浏览器2目标设置 +D:\chrome.exe --remote-debugging-port=9222 --user-data-dir=D:\data2 +``` + +```python +from DrissionPage.confing import DriverOptions + +do1 = DriverOptions().set_paths(local_port=9111) +do2 = DriverOptions().set_paths(local_port=9222) + +page1 = MixPage(driver_options=do1) +page2 = MixPage(driver_options=do2) +``` + +?> **Tips:**
使用 bat 文件打开浏览器再接管操作是一样的。 + +# 一些技巧 + +事实上,本库默认启动浏览器的方式是先通过程序在 9222(或用户指定的)端口运行一个浏览器进程,然后通过程序接管。这种做法有很多好处: + +## 可重复使用的浏览器对象 + +当程序运行完毕,浏览器不会主动关闭,下次再运行的时候可以直接在当前状态下继续运行。于是无须每次打开新的浏览器对象,无须从最开始步骤重新运行整个程序,一些前置条件(如登录)也无须每次运行,大大提高开发效率。 + +## 简便的调试方式 + +通过重复使用浏览器对象,用户可以把浏览器页面调整到某个状态再用程序接管,对调试某个特定问题效率极高。比如有些通过很多步骤才能到达的页面,如果每次重新运行会花费大量时间,而将页面固定再由程序接管,测试各种细节非常方便快捷。 + +## 一行代码传递登录状态到 requests + +本库的一个特点是打通了浏览器和`requests`之间的登录状态,我们可以手动登录浏览器,再用程序接管,然后切换到 s 模式,这时 s 模式的`Session`对象即已活动浏览器的登录信息,无须用`requests` +处理登录过程,极大地简化了开发复杂程度。示例: + +```python +# 假设已在 9222 端口打开了一个浏览器,并已登录某网站 +from DrissionPage import MixPage + +page = MixPage() # 用 d 模式创建页面对象,默认接管 9222 端口 +page.change_mode() # 切换到 s 模式,即使用 requests 进行操作 +page.get('某url...') # 即可已登录状态访问 +``` + +使用其它端口号: + +```python +from DrissionPage.configs import DriverOptions +from DrissionPage import MixPage + +do = DriverOptions().set_paths(local_port=9333) +page = MixPage(driver_option=do) +page.change_mode() +page.get('某url...') +``` + + diff --git a/docs/使用方法/启动配置/Session启动配置.md b/docs/使用方法/启动配置/Session启动配置.md index d2ac7c5..e9169cb 100644 --- a/docs/使用方法/启动配置/Session启动配置.md +++ b/docs/使用方法/启动配置/Session启动配置.md @@ -13,8 +13,6 @@ - read_file:是否从默认 ini 文件中读取配置信息 - ini_path:ini 文件路径,为`None`则读取默认 ini 文件 - - ## headers 该属性返回`headers`设置信息,可传入字典赋值。 @@ -162,4 +160,3 @@ so.set_a_header('Connection', 'keep-alive') # 以该配置创建页面对象 page = MixPage(mode='s', session_options=so) ``` - diff --git a/docs/入门指南/快速上手.md b/docs/入门指南/快速上手.md index f595459..6ba06d8 100644 --- a/docs/入门指南/快速上手.md +++ b/docs/入门指南/快速上手.md @@ -21,7 +21,8 @@ from DrissionPage import MixPage s 模式是无须初始化的,导入后就可以直接使用。 d 模式因为使用浏览器,须要配置浏览器和对应的 driver。 -!> **注意:**
这里介绍的方法只支持 Windows 系统,使用其它系统请查看 [使用其它系统或浏览器](使用方法\使用其它系统或浏览器.md) 章节。 +!> **注意:**
这里介绍的方法只适用于 Windows 系统和 Chrome 浏览器,使用其它系统或浏览器请查看 [使用其它系统或浏览器](使用方法\使用其它系统或浏览器.md) 章节。
如果您有已打开的 Chrome +浏览器,请先关闭,否则会造成冲突。后面在 [创建页面对象](使用方法\使创建页面对象.md) 章节再介绍多 Chrome 浏览器共存的方法。 ## 自动配置方式 @@ -99,7 +100,7 @@ for row in rows: cols = row.eles('tag:td') # 生成当前行数据列表(跳过其中没用的几列) data = [td.text for k, td in enumerate(cols) if k not in (2, 4, 6)] - + print(data) # 打印行数据 ``` diff --git a/docs/版本历史.md b/docs/版本历史.md index 0cad757..d1aaeb5 100644 --- a/docs/版本历史.md +++ b/docs/版本历史.md @@ -1,4 +1,16 @@ -# v2.5.3 +# v2.5.7 + +- 列表元素`select()`、`deselect()`等方法添加`timeout`参数,可等待列表元素加载 + +- 优化了对消息提示框的处理 + +- `drag()`和`drag_to()`不再检测是否拖拽成功,改成返回None + +- `DriverOptions`对象从父类继承的方法也支持链式操作 + +- 其它优化和问题修复 + +# v2.5.5 - `DriverPage`添加`run_cdp()`方法 - `get()`和`post()`方法删除`go_anyway`参数 @@ -260,4 +272,3 @@ # v0.8.4 - 基本完成 - diff --git a/setup.py b/setup.py index c92ccd5..81734a5 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ with open("README.md", "r", encoding='utf-8') as fh: setup( name="DrissionPage", - version="2.5.6", + version="2.5.7", author="g1879", author_email="g1879@qq.com", description="A module that integrates selenium and requests session, encapsulates common page operations.",