解决较高版本Chrome适配问题;不支持低于selenium4.1

This commit is contained in:
g1879 2022-05-13 18:19:57 +08:00
parent 34b76d9446
commit 6876d6a21c
10 changed files with 244 additions and 255 deletions

View File

@ -483,6 +483,9 @@ class DriverOptions(Options):
self.timeouts['pageLoad'] *= 1000 self.timeouts['pageLoad'] *= 1000
self.timeouts['script'] *= 1000 self.timeouts['script'] *= 1000
if '--no-sandbox' not in self._arguments:
self._arguments.append('--no-sandbox')
@property @property
def driver_path(self) -> str: def driver_path(self) -> str:
"""chromedriver文件路径""" """chromedriver文件路径"""

View File

@ -118,6 +118,11 @@ class Drission(object):
# -----------创建WebDriver对象----------- # -----------创建WebDriver对象-----------
self._driver = _create_driver(chrome_path, driver_path, self.driver_options) self._driver = _create_driver(chrome_path, driver_path, self.driver_options)
# -----------解决接管新版浏览器不能定位到正确的标签页的问题-----------
active_tab = self._driver.window_handles[0]
if active_tab != self._driver.current_window_handle:
self._driver.switch_to.window(active_tab)
# 反反爬设置 # 反反爬设置
try: try:
self._driver.execute_script('Object.defineProperty(navigator,"webdriver",{get:() => undefined,});') self._driver.execute_script('Object.defineProperty(navigator,"webdriver",{get:() => undefined,});')
@ -448,7 +453,7 @@ def _create_chrome(chrome_path: str, port: str, args: list, proxy: dict) -> tupl
# ----------创建浏览器进程---------- # ----------创建浏览器进程----------
try: try:
debugger = Popen(f'{chrome_path} --remote-debugging-port={port} {args}', shell=False) debugger = Popen(f'"{chrome_path}" --remote-debugging-port={port} {args}', shell=False)
if chrome_path == 'chrome.exe': if chrome_path == 'chrome.exe':
from .common import get_exe_path_from_port from .common import get_exe_path_from_port

View File

@ -764,10 +764,7 @@ class DriverElement(DrissionElement):
:param loc: 筛选条件可用selenium的(By, str)也可用本库定位语法 :param loc: 筛选条件可用selenium的(By, str)也可用本库定位语法
:return: DriverElement对象 :return: DriverElement对象
""" """
try: from selenium.webdriver.support.relative_locator import RelativeBy
from selenium.webdriver.support.relative_locator import RelativeBy
except ImportError:
raise ImportError('该方法只支持selenium4及以上版本。')
if isinstance(loc, str): if isinstance(loc, str):
loc = str_to_loc(loc) loc = str_to_loc(loc)

View File

@ -334,15 +334,9 @@ class DriverPage(BasePage):
:param url: 新标签页跳转到的网址 :param url: 新标签页跳转到的网址
:return: None :return: None
""" """
try: # selenium4新增功能须配合新版浏览器 self.driver.switch_to.new_window('tab')
self.driver.switch_to.new_window('tab') if url:
if url: self.get(url)
self.get(url)
except Exception:
self.to_tab(-1)
self.run_script(f'window.open("{url}");')
self.to_tab(-1)
def close_tabs(self, num_or_handles: Union[int, str, list, tuple] = None) -> None: def close_tabs(self, num_or_handles: Union[int, str, list, tuple] = None) -> None:
"""关闭传入的标签页,默认关闭当前页。可传入多个 \n """关闭传入的标签页,默认关闭当前页。可传入多个 \n

View File

@ -1,219 +1,220 @@
selenium 缺乏对浏览器下载文件的有效管理,难以进行检测下载状态、重命名、失败管理。 selenium 缺乏对浏览器下载文件的有效管理,难以进行检测下载状态、重命名、失败管理。
使用 requests 下载文件能较好实现以上功能,但代码较为繁琐。 使用 requests 下载文件能较好实现以上功能,但代码较为繁琐。
因此 DrissionPage 提供了高效可靠的下载工具,整合了两者优点,可从 selenium 获取登录信息,用 requests 进行下载。弥补了 selenium 的不足,使下载简洁高效。 因此 DrissionPage 提供了高效可靠的下载工具,整合了两者优点,可从 selenium 获取登录信息,用 requests 进行下载。弥补了 selenium 的不足,使下载简洁高效。
?> 为了增强灵活性,该工具现在独立打包成一个库,叫 DownloadKit详细用法见[DownloadKit](https://gitee.com/g1879/DownloadKit) ?> 为了增强灵活性,该工具现在独立打包成一个库,叫 DownloadKit详细用法见[DownloadKit](https://gitee.com/g1879/DownloadKit)
# 功能 # 功能
- 支持多线程同时下载多个文件 - 支持多线程同时下载多个文件
- 大文件自动分块使用多线程下载 - 大文件自动分块使用多线程下载
- 自动任务调度,简易的任务添加方式 - 自动任务调度,简易的任务添加方式
- 支持 d 模式下用 requests 下载文件 - 支持 d 模式下用 requests 下载文件
- 自动创建目标路径 - 自动创建目标路径
- 自动去除路径中的非法字符 - 自动去除路径中的非法字符
- 下载时支持文件重命名 - 下载时支持文件重命名
- 自动处理文件名冲突 - 自动处理文件名冲突
- 显示下载进度 - 显示下载进度
- 支持 post 方式 - 支持 post 方式
- 支持自定义连接参数 - 支持自定义连接参数
- 任务失败自动重试 - 任务失败自动重试
# 单线程下载方式 # 单线程下载方式
`MixPage`对象的`download`属性是一个`DownloadKit`对象,为尽量与旧版兼容,该属性可直接调用。如`page.download(url, save_path)` `MixPage`对象的`download`属性是一个`DownloadKit`对象,为尽量与旧版兼容,该属性可直接调用。如`page.download(url, save_path)`
?> 使用这个方式时效果与旧版一致,下载一个文件时会阻塞程序,因此更加建议用后文讲述的多线程方法。 ?> 使用这个方式时效果与旧版一致,下载一个文件时会阻塞程序,因此更加建议用后文讲述的多线程方法。
参数: 参数:
- file_ur文件 url - file_ur文件 url
- goal_path存放路径填写到文件夹不填写文件名 - goal_path存放路径填写到文件夹不填写文件名
- rename重命名文件可不写扩展名不输入则用网络文件原名 - rename重命名文件可不写扩展名不输入则用网络文件原名
- file_exists若存在同名文件可选择`'rename'`,`'overwrite'`,`'skip'`方式处理,若选择重命名,会在文件名后面添加序号 - file_exists若存在同名文件可选择`'rename'`,`'overwrite'`,`'skip'`方式处理,若选择重命名,会在文件名后面添加序号
- post_datapost 方式的数据,这个参数不为`None`时自动转成 post 方式 - post_datapost 方式的数据,这个参数不为`None`时自动转成 post 方式
- show_msg是否显示下载信息和进度 - show_msg是否显示下载信息和进度
- **kwargs连接参数与 requests 的一致 - **kwargs连接参数与 requests 的一致
返回:下载是否成功和状态信息的元组。 返回:下载是否成功和状态信息的元组。
```python ```python
from DrissionPage import MixPage from DrissionPage import MixPage
page = MixPage() page = MixPage()
# 文件 url # 文件 url
url = 'https://www.baidu.com/img/flexible/logo/pc/result.png' url = 'https://www.baidu.com/img/flexible/logo/pc/result.png'
# 存放路径 # 存放路径
save_path = r'C:\download' save_path = r'C:\download'
# 重命名为img.png存在重名时自动在文件名末尾加上序号显示下载进度 # 重命名为img.png存在重名时自动在文件名末尾加上序号显示下载进度
res = page.download(url, save_path, 'img', 'rename', show_msg=True) res = page.download(url, save_path, 'img', 'rename', show_msg=True)
# 打印结果 # 打印结果
print(res) print(res)
``` ```
显示: 显示:
```shell ```shell
urlhttps://www.baidu.com/img/flexible/logo/pc/result.png urlhttps://www.baidu.com/img/flexible/logo/pc/result.png
文件名img.png 文件名img.png
目标路径C:\download 目标路径C:\download
100% 下载完成 C:\download\img.png 100% 下载完成 C:\download\img.png
(True, 'C:\\download\\img.png') (True, 'C:\\download\\img.png')
``` ```
# 多线程并发下载方式 # 多线程并发下载方式
你可以往`DownloadKit`对象添加个数不限的下载任务,它会自动调配线程去完成这些任务。 你可以往`DownloadKit`对象添加个数不限的下载任务,它会自动调配线程去完成这些任务。
默认为 10 个线程。 默认为 10 个线程。
## 添加任务 ## 添加任务
使用`add()`方法添加任务 使用`add()`方法添加任务
参数: 参数:
- file_ur文件 url - file_ur文件 url
- goal_path存放路径填写到文件夹不填写文件名 - goal_path存放路径填写到文件夹不填写文件名
- session可指定使用的`Session`对象,默认使用`MixPage`内置的`Session`对象 - session可指定使用的`Session`对象,默认使用`MixPage`内置的`Session`对象
- rename重命名文件可不写扩展名不输入则用网络文件原名 - rename重命名文件可不写扩展名不输入则用网络文件原名
- file_exists若存在同名文件可选择`'rename'`,`'overwrite'`,`'skip'`方式处理,若选择重命名,会在文件名后面添加序号 - file_exists若存在同名文件可选择`'rename'`,`'overwrite'`,`'skip'`方式处理,若选择重命名,会在文件名后面添加序号
- post_datapost 方式的数据,这个参数不为`None`时自动转成 post 方式 - post_datapost 方式的数据,这个参数不为`None`时自动转成 post 方式
- **kwargs连接参数与 requests 的一致 - **kwargs连接参数与 requests 的一致
返回:任务对象,可通过任务对象查看任务状态和结果 返回:任务对象,可通过任务对象查看任务状态和结果
```python ```python
from DrissionPage import MixPage from DrissionPage import MixPage
page = MixPage('s') page = MixPage('s')
# 文件 url # 文件 url
url = 'https://www.baidu.com/img/flexible/logo/pc/result.png' url = 'https://www.baidu.com/img/flexible/logo/pc/result.png'
# 存放路径 # 存放路径
save_path = r'C:\download' save_path = r'C:\download'
# 返回一个任务对象 # 返回一个任务对象
mission = page.download.add(url, save_path) mission = page.download.add(url, save_path)
# 通过任务对象查看状态 # 通过任务对象查看状态
print(mission.rate, mission.info) print(mission.rate, mission.info)
``` ```
输出: 输出:
```shell ```shell
90% '下载中' 90% '下载中'
``` ```
## 实时查看所有线程进度 ## 实时查看所有线程进度
多线程方式不会实时显示下载进度,可用`show()`方法把下载进度打印出来。 多线程方式不会实时显示下载进度,可用`show()`方法把下载进度打印出来。
!> **注意:** <br> 若使用 pyCharm 运行,须在运行配置里勾选“模拟输出控制台中的终端”才能正常显示输出。 !> **注意:** <br> 若使用 pyCharm 运行,须在运行配置里勾选“模拟输出控制台中的终端”才能正常显示输出。
参数: 参数:
- asyn是否异步进行 - asyn是否异步进行
- keep任务全部完成后是否保持显示 - keep任务全部完成后是否保持显示
返回None 返回None
```python ```python
url = 'https://example.com/file/abc.zip' url = 'https://example.com/file/abc.zip'
mission = page.download.add(url, r'.\files') mission = page.download.add(url, r'.\files')
page.download.show() page.download.show()
``` ```
输出: 输出:
```shell ```shell
等待任务数0 等待任务数0
线程097.41% D:\files\abc.zip 线程097.41% D:\files\abc.zip
线程1空闲 线程1空闲
线程2空闲 线程2空闲
..... .....
``` ```
?> **Tips** <br> `keep`参数为`True`时,即使所有任务都已结束仍会一直打印进度,可以按`enter`结束。 ?> **Tips** <br> `keep`参数为`True`时,即使所有任务都已结束仍会一直打印进度,可以按`enter`结束。
## 等待任务结束 ## 等待任务结束
有时须要等待任务结束,以便获取结果,可用`wait()`方法。 当传入任务时,等待该任务结束并返回结果。不传入参数时等待所有任务结束,与`show()`方法一致。 有时须要等待任务结束,以便获取结果,可用`wait()`方法。 当传入任务时,等待该任务结束并返回结果。不传入参数时等待所有任务结束,与`show()`方法一致。
参数: 参数:
- mission任务对象或任务`id`,为`None`时等待所有任务结束 - mission任务对象或任务`id`,为`None`时等待所有任务结束
- show是否显示进度 - show是否显示进度
返回: 返回:
- 指定任务时,返回任务结果和信息组成的两位 tuple。`True`表示成功,`False`表示失败,`None`表示跳过。 - 指定任务时,返回任务结果和信息组成的两位 tuple。`True`表示成功,`False`表示失败,`None`表示跳过。
- 不指定任务时,返回`None` - 不指定任务时,返回`None`
!> **注意:** <br> 若使用 pyCharm 运行,须在运行配置里勾选“模拟输出控制台中的终端”才能正常显示输出。 !> **注意:** <br> 若使用 pyCharm 运行,须在运行配置里勾选“模拟输出控制台中的终端”才能正常显示输出。
```python ```python
url = 'https://www.baidu.com/img/PCfb_5bf082d29588c07f842ccde3f97243ea.png' url = 'https://www.baidu.com/img/PCfb_5bf082d29588c07f842ccde3f97243ea.png'
mission = page.download.add(url, save_path) mission = page.download.add(url, save_path)
page.download.wait(mission) page.download.wait(mission)
``` ```
输出: 输出:
```shell ```shell
urlhttps://www.baidu.com/img/PCfb_5bf082d29588c07f842ccde3f97243ea.png urlhttps://www.baidu.com/img/PCfb_5bf082d29588c07f842ccde3f97243ea.png
文件名PCfb_5bf082d29588c07f842ccde3f97243ea_4.png 文件名PCfb_5bf082d29588c07f842ccde3f97243ea_4.png
目标路径D:\files 目标路径D:\files
100% 下载完成 D:\files\PCfb_5bf082d29588c07f842ccde3f97243ea_4.png 100% 下载完成 D:\files\PCfb_5bf082d29588c07f842ccde3f97243ea_4.png
``` ```
## 获取某个任务结果 ## 获取某个任务结果
`add()`方法返回的`Mission`对象,可查看该任务执行情况。 `add()`方法返回的`Mission`对象,可查看该任务执行情况。
`Mission`对象属性: `Mission`对象属性:
- id任务 id - id任务 id
- file_name要保存的文件名 - file_name要保存的文件名
- path要保存的路径 - path要保存的路径
- data任务数据 - data任务数据
- state任务状态`'waiting'``'running'``'done'`三种 - state任务状态`'waiting'``'running'``'done'`三种
- rate任务进度以百分比显示 - rate任务进度以百分比显示
- info任务信息成功会返回文件绝对路径失败会显示原因 - info任务信息成功会返回文件绝对路径失败会显示原因
- result任务结果`True`表示成功,`False`表示失败,`None`表示跳过 - result任务结果`True`表示成功,`False`表示失败,`None`表示跳过
```python ```python
mission = page.download.add(url) mission = page.download.add(url)
print(mission.state) print(mission.state)
``` ```
输出: 输出:
```python ```python
running running
``` ```
## 下载设置 ## 下载设置
可使用以下属性进行配置: 可使用以下属性进行配置:
```python ```python
# 设置线程数,只能在没有任务在运行的时候进行 # 设置线程数,只能在没有任务在运行的时候进行
page.download.roads = 20 page.download.roads = 20
# 大文件分块大小,默认 20MB page.downloadd.block_size = '50M' # 大文件分块大小,默认 20MB
page.downloadd.block_size = '50M'
# 设置保存路径,设置后每个任务会使用这个路径,也可添加任务时单独设置
page.download.goal_path = r'D:\tmp' # 设置保存路径,设置后每个任务会使用这个路径,也可添加任务时单独设置
page.download.goal_path = r'D:\tmp'
# 设置重试次数,初始为继承 page 的 retry_times 属性
page.download.retry = 5 # 设置重试次数,初始为继承 page 的 retry_times 属性
page.download.retry = 5
# 设置失败重试间隔,初始为继承 page 的 retry_interval 属性
page.download.interval = 2 # 设置失败重试间隔,初始为继承 page 的 retry_interval 属性
page.download.interval = 2
# 设置存在文件名冲突时的处理方式,可选 'skip', 'overwrite', 'rename'
page.download.file_exists = 'skip' # 设置存在文件名冲突时的处理方式,可选 'skip', 'overwrite', 'rename'
``` page.download.file_exists = 'skip'
```
?> **Tips**<br> 重试次数和间隔在初始化时继承页面对象的`retry_times``retry_interval`属性,可用上面例子的方法对下载的重试次数和间隔进行设置,设置后不会影响页面对象的设置。
?> **Tips**<br> 重试次数和间隔在初始化时继承页面对象的`retry_times``retry_interval`属性,可用上面例子的方法对下载的重试次数和间隔进行设置,设置后不会影响页面对象的设置。

View File

@ -53,8 +53,6 @@ html = ele.html
""" """
``` ```
## inner_html ## inner_html
此属性返回元素的`innerHTML`文本。 此属性返回元素的`innerHTML`文本。
@ -68,8 +66,6 @@ Hello World!
""" """
``` ```
## tag ## tag
此属性返回元素的标签名。 此属性返回元素的标签名。
@ -79,8 +75,6 @@ tag = ele.tag
# 返回div # 返回div
``` ```
## text ## text
此属性返回元素内所有文本组合成的字符串。 此属性返回元素内所有文本组合成的字符串。
@ -94,8 +88,6 @@ Hello World!
""" """
``` ```
## raw_text ## raw_text
此属性返回元素内原始文本。 此属性返回元素内原始文本。
@ -105,13 +97,11 @@ text = ele.raw_text
"""返回(注意保留了元素间的空格和换行): """返回(注意保留了元素间的空格和换行):
Hello World! Hello World!
行元素 行元素
""" """
``` ```
## texts() ## texts()
此方法返回元素内所有直接子节点的文本,包括元素和文本节点。 它有一个参数`text_node_only`,为`True`时则只获取只返回文本节点。这个方法适用于获取文本节点和元素节点混排的情况。 此方法返回元素内所有直接子节点的文本,包括元素和文本节点。 它有一个参数`text_node_only`,为`True`时则只获取只返回文本节点。这个方法适用于获取文本节点和元素节点混排的情况。
@ -376,4 +366,3 @@ displayed = ele.is_displayed()
## is_valid() ## is_valid()
`DriverElement`一致。 `DriverElement`一致。

View File

@ -149,11 +149,14 @@ print(page.timeouts)
此属性以列表形式返回当前浏览器所有标签页的 handle。 此属性以列表形式返回当前浏览器所有标签页的 handle。
!> **注意:**
以下情况会导致获取到的顺序与视觉效果不一致1、自动化过程中手动点击标签页2、浏览器被接管时已打开一个以上标签页。
## current_tab_index ## current_tab_index
此属性返回当前标签页的序号。 此属性返回当前标签页的序号。
!> **注意:** <br>自动化过程中若手动点击标签页,会使被点击标签页的 handle 排到首位,从而导致排序与视觉效果不一致 !> **注意:** <br>以下情况会导致获取到的排序号与视觉效果不一致1、自动化过程中手动点击标签页2、浏览器被接管时已打开一个以上标签页
## current_tab_handle ## current_tab_handle
@ -176,4 +179,3 @@ print(page.timeouts)
- path: 下载文件夹路径,默认从配置信息读取 - path: 下载文件夹路径,默认从配置信息读取
返回:下载中的文件列表 返回:下载中的文件列表

View File

@ -17,8 +17,6 @@
!> **注意:** <br>s 模式转 d 模式时,若浏览器当前网址域名和 s 模式不一样,必定会跳转。 !> **注意:** <br>s 模式转 d 模式时,若浏览器当前网址域名和 s 模式不一样,必定会跳转。
参数: 参数:
- mode目标模式字符串`'s'``'d'`,默认转换到另一种 - mode目标模式字符串`'s'``'d'`,默认转换到另一种
@ -299,7 +297,7 @@ page.to_frame(iframe)
返回None 返回None
!> **注意:** <br>自动化过程中若手动点击标签页,会使被点击标签页的 handle 排到首位,从而导致排序与视觉效果不一致 !> **注意:** <br>以下情况会导致获取到的排序号与视觉效果不一致1、自动化过程中手动点击标签页2、浏览器被接管时已打开一个以上标签页
```python ```python
# 跳转到第一个标签页 # 跳转到第一个标签页
@ -327,7 +325,7 @@ page.create_tab('http://www.baidu.com')
此方法用于关闭指定的标签页,标签页可以是序号或 handle 值,可关闭多个。默认关闭当前的。 此方法用于关闭指定的标签页,标签页可以是序号或 handle 值,可关闭多个。默认关闭当前的。
!> **注意:** <br>当程序使用的是接管的浏览器,获取到的 handle 顺序和视觉效果不一致,不能按序号关闭 !> **注意:** <br>以下情况会导致获取到的排序号与视觉效果不一致不能按序号关闭1、自动化过程中手动点击标签页2、浏览器被接管时已打开一个以上标签页
参数: 参数:
@ -347,7 +345,7 @@ page.close_tabs((0, 2))
此方法用于关闭指定标签页以外的标签页,标签页可以是序号或 handle 值,可保留多个。默认保留当前的。 此方法用于关闭指定标签页以外的标签页,标签页可以是序号或 handle 值,可保留多个。默认保留当前的。
!> **注意:** <br>当程序使用的是接管的浏览器,获取到的 handle 顺序和视觉效果不一致,不能按序号关闭 !> **注意:** <br>以下情况会导致获取到的排序号与视觉效果不一致不能按序号关闭1、自动化过程中手动点击标签页2、浏览器被接管时已打开一个以上标签页
参数: 参数:

View File

@ -1,4 +1,4 @@
selenium selenium>=4.1
requests requests
tldextract tldextract
lxml lxml

View File

@ -6,7 +6,7 @@ with open("README.md", "r", encoding='utf-8') as fh:
setup( setup(
name="DrissionPage", name="DrissionPage",
version="2.5.9", version="2.6.0",
author="g1879", author="g1879",
author_email="g1879@qq.com", author_email="g1879@qq.com",
description="A module that integrates selenium and requests session, encapsulates common page operations.", description="A module that integrates selenium and requests session, encapsulates common page operations.",
@ -18,7 +18,7 @@ setup(
include_package_data=True, include_package_data=True,
packages=find_packages(), packages=find_packages(),
install_requires=[ install_requires=[
"selenium", "selenium>=4.1",
"lxml", "lxml",
"tldextract", "tldextract",
"requests", "requests",