2.5.8 优化s模式创建连接的逻辑;ini文件添加运行弹窗参赛

This commit is contained in:
g1879 2022-04-15 15:02:03 +08:00
parent 0786ae6c56
commit 72c143e77d
7 changed files with 259 additions and 255 deletions

View File

@ -7,6 +7,7 @@
from abc import abstractmethod from abc import abstractmethod
from re import sub from re import sub
from typing import Union, Tuple, List from typing import Union, Tuple, List
from urllib.parse import quote
from lxml.html import HtmlElement from lxml.html import HtmlElement
from selenium.webdriver.remote.webelement import WebElement from selenium.webdriver.remote.webelement import WebElement
@ -330,6 +331,18 @@ class BasePage(BaseParser):
"""返回当前访问的url有效性""" """返回当前访问的url有效性"""
return self._url_available return self._url_available
def _before_connect(self, url: str, retry: int, interval: float) -> tuple:
"""连接前的准备 \n
:param url: 要访问的url
:param retry: 重试次数
:param interval: 重试间隔
:return: 重试次数和间隔组成的tuple
"""
self._url = quote(url, safe='/:&?=%;#@+!')
retry = retry if retry is not None else self.retry_times
interval = interval if interval is not None else self.retry_interval
return retry, interval
# ----------------以下属性或方法由后代实现---------------- # ----------------以下属性或方法由后代实现----------------
@property @property
def url(self): def url(self):

View File

@ -5,7 +5,7 @@ tmp_path =
[chrome_options] [chrome_options]
debugger_address = 127.0.0.1:9222 debugger_address = 127.0.0.1:9222
binary_location = binary_location =
arguments = ['--no-sandbox', '--disable-gpu', '--ignore-certificate-errors', '--disable-infobars'] arguments = ['--no-sandbox', '--disable-gpu', '--ignore-certificate-errors', '--disable-infobars', '--disable-popup-blocking']
extensions = [] extensions = []
experimental_options = {'prefs': {'profile.default_content_settings.popups': 0, 'profile.default_content_setting_values': {'notifications': 2}, 'plugins.plugins_list': [{'enabled': False, 'name': 'Chrome PDF Viewer'}]}, 'useAutomationExtension': False, 'excludeSwitches': ['enable-automation']} experimental_options = {'prefs': {'profile.default_content_settings.popups': 0, 'profile.default_content_setting_values': {'notifications': 2}, 'plugins.plugins_list': [{'enabled': False, 'name': 'Chrome PDF Viewer'}]}, 'useAutomationExtension': False, 'excludeSwitches': ['enable-automation']}
timeouts = {'implicit': 10.0, 'pageLoad': 30.0, 'script': 30.0} timeouts = {'implicit': 10.0, 'pageLoad': 30.0, 'script': 30.0}

View File

@ -9,7 +9,6 @@ from os import sep
from pathlib import Path from pathlib import Path
from time import sleep, perf_counter from time import sleep, perf_counter
from typing import Union, List, Any, Tuple from typing import Union, List, Any, Tuple
from urllib.parse import quote
from selenium.common.exceptions import NoAlertPresentException from selenium.common.exceptions import NoAlertPresentException
from selenium.webdriver.chrome.webdriver import WebDriver from selenium.webdriver.chrome.webdriver import WebDriver
@ -75,15 +74,8 @@ class DriverPage(BasePage):
:param interval: 重试间隔 :param interval: 重试间隔
:return: 目标url是否可用返回None表示不确定 :return: 目标url是否可用返回None表示不确定
""" """
to_url = quote(url, safe='/:&?=%;#@+!') retry, interval = self._before_connect(url, retry, interval)
retry = retry if retry is not None else self.retry_times self._url_available = self._d_connect(self._url, times=retry, interval=interval, show_errmsg=show_errmsg)
interval = interval if interval is not None else self.retry_interval
if not url:
raise ValueError('没有传入url。')
self._url = to_url
self._url_available = self._try_to_connect(to_url, times=retry, interval=interval, show_errmsg=show_errmsg)
try: try:
self._driver.execute_script('Object.defineProperty(navigator,"webdriver",{get:() => undefined,});') self._driver.execute_script('Object.defineProperty(navigator,"webdriver",{get:() => undefined,});')
@ -173,11 +165,11 @@ class DriverPage(BasePage):
self._timeout = second self._timeout = second
self._wait_object = None self._wait_object = None
def _try_to_connect(self, def _d_connect(self,
to_url: str, to_url: str,
times: int = 0, times: int = 0,
interval: float = 1, interval: float = 1,
show_errmsg: bool = False) -> Union[bool, None]: show_errmsg: bool = False) -> Union[bool, None]:
"""尝试连接,重试若干次 \n """尝试连接,重试若干次 \n
:param to_url: 要访问的url :param to_url: 要访问的url
:param times: 重试次数 :param times: 重试次数

View File

@ -315,7 +315,7 @@ class MixPage(SessionPage, DriverPage, BasePage):
# 使用requests访问url并判断可用性 # 使用requests访问url并判断可用性
if by_requests: if by_requests:
self.cookies_to_session() self.cookies_to_session()
r = self._make_response(self.url)[0] r = self._make_response(self.url, retry=0)[0]
return r.ok if r else False return r.ok if r else False
def close_driver(self) -> None: def close_driver(self) -> None:

View File

@ -7,7 +7,7 @@
from re import search from re import search
from time import sleep from time import sleep
from typing import Union, List, Tuple from typing import Union, List, Tuple
from urllib.parse import urlparse, quote from urllib.parse import urlparse
from requests import Session, Response from requests import Session, Response
from requests.structures import CaseInsensitiveDict from requests.structures import CaseInsensitiveDict
@ -70,7 +70,7 @@ class SessionPage(BasePage):
:param kwargs: 连接参数 :param kwargs: 连接参数
:return: url是否可用 :return: url是否可用
""" """
return self._connect(url, 'get', None, show_errmsg, retry, interval, **kwargs) return self._s_connect(url, 'get', None, show_errmsg, retry, interval, **kwargs)
def ele(self, def ele(self,
loc_or_ele: Union[Tuple[str, str], str, SessionElement], loc_or_ele: Union[Tuple[str, str], str, SessionElement],
@ -173,16 +173,16 @@ class SessionPage(BasePage):
:param kwargs: 连接参数 :param kwargs: 连接参数
:return: url是否可用 :return: url是否可用
""" """
return self._connect(url, 'post', data, show_errmsg, retry, interval, **kwargs) return self._s_connect(url, 'post', data, show_errmsg, retry, interval, **kwargs)
def _connect(self, def _s_connect(self,
url: str, url: str,
mode: str, mode: str,
data: Union[dict, str] = None, data: Union[dict, str] = None,
show_errmsg: bool = False, show_errmsg: bool = False,
retry: int = None, retry: int = None,
interval: float = None, interval: float = None,
**kwargs) -> bool: **kwargs) -> bool:
"""执行get或post连接 \n """执行get或post连接 \n
:param url: 目标url :param url: 目标url
:param mode: 'get' 'post' :param mode: 'get' 'post'
@ -193,15 +193,8 @@ class SessionPage(BasePage):
:param kwargs: 连接参数 :param kwargs: 连接参数
:return: url是否可用 :return: url是否可用
""" """
to_url = quote(url, safe='/:&?=%;#@+!') retry, interval = self._before_connect(url, retry, interval)
retry = retry if retry is not None else self.retry_times self._response, info = self._make_response(self._url, mode, data, retry, interval, show_errmsg, **kwargs)
interval = interval if interval is not None else self.retry_interval
if not url:
raise ValueError('没有传入url。')
self._url = to_url
self._response, info = self._make_response(to_url, mode, data, retry, interval, show_errmsg, **kwargs)
if self._response is None: if self._response is None:
self._url_available = False self._url_available = False
@ -267,7 +260,7 @@ class SessionPage(BasePage):
except Exception as e: except Exception as e:
err = e err = e
# if r and (r.content != b'' or r.status_code in (403, 404)): # if r and r.status_code in (403, 404):
# break # break
if i < retry: if i < retry:

View File

@ -1,217 +1,219 @@
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)
# 功能 # 功能
- 支持多线程同时下载多个文件 - 支持多线程同时下载多个文件
- 自动管理下载列表,只要用`add()`方法添加任务,该工具会自动在多个线程中调度任务,无须等待 - 大文件自动分块使用多线程下载
- 支持 d 模式下用 requests 下载文件 - 自动任务调度,简易的任务添加方式
- 可指定下载路径,若路径不存在会自动创建文件夹 - 支持 d 模式下用 requests 下载文件
- 重命名文件,可不填写扩展名,程序自动补充 - 自动创建目标路径
- 存在同名文件时,可选择重命名、覆盖、跳过等处理方式 - 自动去除路径中的非法字符
- 显示下载进度 - 下载时支持文件重命名
- 支持 post 方式 - 自动处理文件名冲突
- 支持自定义连接参数 - 显示下载进度
- 任务失败自动重试 - 支持 post 方式
- 支持自定义连接参数
# 单线程下载方式 - 任务失败自动重试
`MixPage`对象的`download`属性是一个`DownloadKit`对象,为尽量与旧版兼容,该属性可直接调用。如`page.download(url, path)` # 单线程下载方式
?> 使用这个方式时效果与旧版一致,下载一个文件时会阻塞程序,因此更加建议用后文讲述的多线程方法。
`MixPage`对象的`download`属性是一个`DownloadKit`对象,为尽量与旧版兼容,该属性可直接调用。如`page.download(url, save_path)`
参数: ?> 使用这个方式时效果与旧版一致,下载一个文件时会阻塞程序,因此更加建议用后文讲述的多线程方法。
- file_ur文件 url 参数:
- goal_path存放路径填写到文件夹不填写文件名
- rename重命名文件可不写扩展名不输入则用网络文件原名 - file_ur文件 url
- file_exists若存在同名文件可选择`'rename'`,`'overwrite'`,`'skip'`方式处理,若选择重命名,会在文件名后面添加序号 - goal_path存放路径填写到文件夹不填写文件名
- post_datapost 方式的数据,这个参数不为`None`时自动转成 post 方式 - rename重命名文件可不写扩展名不输入则用网络文件原名
- show_msg是否显示下载信息和进度 - file_exists若存在同名文件可选择`'rename'`,`'overwrite'`,`'skip'`方式处理,若选择重命名,会在文件名后面添加序号
- **kwargs连接参数与 requests 的一致 - post_datapost 方式的数据,这个参数不为`None`时自动转成 post 方式
- show_msg是否显示下载信息和进度
返回:下载是否成功(`bool`)和状态信息(成功时信息为文件路径)的元组,跳过时第一位返回 None - **kwargs连接参数与 requests 的一致
```python 返回:下载是否成功和状态信息的元组。
from DrissionPage import MixPage
```python
page = MixPage() from DrissionPage import MixPage
# 文件 url
url = 'https://www.baidu.com/img/flexible/logo/pc/result.png' page = MixPage()
# 存放路径 # 文件 url
save_path = r'C:\download' url = 'https://www.baidu.com/img/flexible/logo/pc/result.png'
# 存放路径
# 重命名为img.png存在重名时自动在文件名末尾加上序号显示下载进度 save_path = r'C:\download'
res = page.download(url, save_path, 'img', 'rename', show_msg=True)
# 打印结果 # 重命名为img.png存在重名时自动在文件名末尾加上序号显示下载进度
print(res) res = page.download(url, save_path, 'img', 'rename', show_msg=True)
``` # 打印结果
print(res)
显示: ```
```shell 显示:
urlhttps://www.baidu.com/img/flexible/logo/pc/result.png
文件名img.png ```shell
目标路径C:\download urlhttps://www.baidu.com/img/flexible/logo/pc/result.png
100% 下载完成 C:\download\img.png 文件名img.png
目标路径C:\download
(True, 'C:\\download\\img.png') 100% 下载完成 C:\download\img.png
```
(True, 'C:\\download\\img.png')
# 多线程并发下载方式 ```
你可以往`DownloadKit`对象添加个数不限的下载任务,它会自动调配线程去完成这些任务。 # 多线程并发下载方式
默认为 10 个线程。
你可以往`DownloadKit`对象添加个数不限的下载任务,它会自动调配线程去完成这些任务。
## 添加任务 默认为 10 个线程。
使用`add()`方法添加任务 ## 添加任务
参数: 使用`add()`方法添加任务
- file_ur文件 url 参数:
- goal_path存放路径填写到文件夹不填写文件名
- session可指定使用的`Session`对象,默认使用`MixPage`内置的`Session`对象 - file_ur文件 url
- rename重命名文件可不写扩展名不输入则用网络文件原名 - goal_path存放路径填写到文件夹不填写文件名
- file_exists若存在同名文件可选择`'rename'`,`'overwrite'`,`'skip'`方式处理,若选择重命名,会在文件名后面添加序号 - session可指定使用的`Session`对象,默认使用`MixPage`内置的`Session`对象
- post_datapost 方式的数据,这个参数不为`None`时自动转成 post 方式 - rename重命名文件可不写扩展名不输入则用网络文件原名
- **kwargs连接参数与 requests 的一致 - file_exists若存在同名文件可选择`'rename'`,`'overwrite'`,`'skip'`方式处理,若选择重命名,会在文件名后面添加序号
- post_datapost 方式的数据,这个参数不为`None`时自动转成 post 方式
返回:任务对象,可通过任务对象查看任务状态和结果 - **kwargs连接参数与 requests 的一致
```python 返回:任务对象,可通过任务对象查看任务状态和结果
from DrissionPage import MixPage
```python
page = MixPage('s') from DrissionPage import MixPage
# 文件 url
url = 'https://www.baidu.com/img/flexible/logo/pc/result.png' page = MixPage('s')
# 存放路径 # 文件 url
save_path = r'C:\download' url = 'https://www.baidu.com/img/flexible/logo/pc/result.png'
# 存放路径
# 返回一个任务对象 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 输出:
90% '下载中'
``` ```shell
90% '下载中'
## 实时查看所有线程进度 ```
多线程方式不会实时显示下载进度,可用`show()`方法把下载进度打印出来。 ## 实时查看所有线程进度
!> **注意:** <br> 若使用 pyCharm 运行,须在运行配置里勾选“模拟输出控制台中的终端”才能正常显示输出。 多线程方式不会实时显示下载进度,可用`show()`方法把下载进度打印出来。
参数: !> **注意:** <br> 若使用 pyCharm 运行,须在运行配置里勾选“模拟输出控制台中的终端”才能正常显示输出。
- asyn是否异步进行 参数:
- keep任务全部完成后是否保持显示
- asyn是否异步进行
返回None - keep任务全部完成后是否保持显示
```python 返回None
url = 'https://example.com/file/abc.zip'
mission = page.download.add(url, r'.\files') ```python
page.download.show() url = 'https://example.com/file/abc.zip'
``` mission = page.download.add(url, r'.\files')
page.download.show()
输出: ```
```shell 输出:
等待任务数0
线程097.41% D:\files\abc.zip ```shell
线程1空闲 等待任务数0
线程2空闲 线程097.41% D:\files\abc.zip
..... 线程1空闲
``` 线程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.size = 20 page.download.roads = 20
# 设置保存路径,设置后每个任务会使用这个路径,也可添加任务时单独设置 # 大文件分块大小,默认 20MB page.downloadd.block_size = '50M'
page.download.goal_path = r'D:\tmp'
# 设置保存路径,设置后每个任务会使用这个路径,也可添加任务时单独设置
# 设置重试次数,初始为继承 page 的 retry_times 属性 page.download.goal_path = r'D:\tmp'
page.download.retry = 5
# 设置重试次数,初始为继承 page 的 retry_times 属性
# 设置失败重试间隔,初始为继承 page 的 retry_interval 属性 page.download.retry = 5
page.download.interval = 2
# 设置失败重试间隔,初始为继承 page 的 retry_interval 属性
# 设置存在文件名冲突时的处理方式,可选 'skip', 'overwrite', 'rename' page.download.interval = 2
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

@ -1,3 +1,7 @@
# 2.5.8
- 优化 s 模式创建连接的逻辑
# v2.5.7 # v2.5.7
- 列表元素`select()``deselect()`等方法添加`timeout`参数,可等待列表元素加载 - 列表元素`select()``deselect()`等方法添加`timeout`参数,可等待列表元素加载