DrissionPage/docs/7_advance/3_download.md
2023-02-03 00:03:36 +08:00

464 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

浏览器没有为下载功能提供方便的程序接口,难以实现有效的下载管理,如检测下载状态、重命名、失败管理等。使用 requests 下载文件能较好实现以上功能,但代码较为繁琐。
因此 DrissionPage 内置了高效可靠的下载工具,提供任务管理、多线程、大文件分块、自动重连、文件名冲突处理等功能。
无论是控制浏览器,还是收发数据包的场景,都可以使用它。甚至可以拦截浏览器的下载动作,转用内置下载器进行下载。使用方式简洁高效。
?> 该工具名为 DownloadKit现已独立打包成一个库详细用法见[DownloadKit](https://gitee.com/g1879/DownloadKit)。这里只介绍和页面对象有关的用法。
# ✔️ 功能简介
`SessionPage``ChromiumPage``WebPage`都可以使用`download()`方法进行下载,还可以使用`download_set`属性对下载参数进行配置。内置下载器功能如下:
- 支持多线程同时下载多个文件
- 大文件自动分块使用多线程下载
- 可拦截浏览器下载动作,自动调用下载器下载
- 自动创建目标路径
- 自动去除路径中的非法字符
- 下载时支持文件重命名
- 自动处理文件名冲突
- 自动去除文件名非法字符
- 支持 post 方式
- 支持自定义连接参数
- 任务失败自动重试
---
# ✔️ 简单示例
下载一个文件(单线程):
```python
page.download('https://xxxxxx/file.pdf')
```
添加下载任务,以多线程方式下载:
```python
page.download.add('https://xxxxxx/file.pdf')
```
拦截浏览器下载动作,改用 DwonloadKit 下载:
```python
page.download_set.by_DownloadKit()
page('#download_btn').click()
```
下载设置:
```python
# 设置默认保存路径
page.download_set.save_path('...')
# 设置存在同名文件时处理方式
page.download_set.if_file_exists.skip()
# 设置使用浏览器或DownloadKit下载
page.download_set.by_DownloadKit()
page.download_set.by_browser()
# 设置使用DownloadKit下载时是否允许多线程同时下载一个文件
page.download_set.split(True)
```
---
# ✔️ 单线程下载
直接调用页面对象的`download()`方法,可下载一个文件,该方法是阻塞式的,会等待下载完成再往下操作。下载时默认打印下载进度。
### 🔸 `download()`
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------------:|:---------------:|:------:| ------------------------------------------------ |
| `file_url` | `str` | 必填 | 文件网址 |
| `goal_path` | `str`<br>`Path` | `None` | 保存文件夹路径,为`None`则保存在当前目录 |
| `rename` | `str` | `None` | 重命名文件名,可不包含后缀 |
| `file_exists` | `str` | `None` | 遇到同名文件时的处理方式,可选`'skip'``'overwrite'``'rename'` |
| `data` | `str`<br>`dict` | `None` | post 方式使用的数据,如不为`None`,使用 post 方式发送请求 |
| `show_msg` | `bool` | `True` | 是否显示下载进度 |
| `**kwargs` | - | 必填 | requests的`get()`方法参数 |
| 返回类型 | 返回格式 | 说明 |
|:-------:|:------------:| ------------------- |
| `tuple` | (任务结果, 任务信息) | 返回任务结果和信息组成的`tuple` |
任务结果可能出现以下值:
- `'success'`:表示下载成功
- `'skip'`:表示存在同名文件,跳过任务
- `'cancel'`:表示任务下载途中被取消
- `False`:表示下载失败
任务信息成功时返回文件绝对路径,其它情况返回相应说明。
**示例:**
```python
from DrissionPage import WebPage
page = WebPage('s')
# 文件 url
url = 'https://www.baidu.com/img/flexible/logo/pc/result.png'
# 存放路径
save_path = r'C:\download'
# 重命名为img.png存在重名时自动在文件名末尾加上序号显示下载进度
res = page.download(url, save_path, 'img', 'rename', show_msg=True)
# 打印结果
print(res)
```
显示:
```console
urlhttps://www.baidu.com/img/flexible/logo/pc/result.png
文件名img.png
目标路径C:\download
100% 下载完成 C:\download\img.png
('success', 'C:\\download\\img.png')
```
---
# ✔️ 多线程并发下载
您可以添加数量不限的下载任务,程序会自动调配线程去完成这些任务。
默认设置下,页面对象最多使用 10 个下载线程,如进程已满,新任务会进入等待队列排队,待有空线程时自动开始。
添加多线程任务的方法是调用页面对象`donwload`属性的`add()`方法。
### 🔸 `download.add()`
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------------:|:---------------:|:------:| ----------------------------------------------------------------- |
| `file_url` | `str` | 必填 | 文件网址 |
| `goal_path` | `str`<br>`Path` | `None` | 保存文件夹路径,为`None`则保存在当前目录 |
| `rename` | `str` | `None` | 重命名文件名,可不包含后缀 |
| `file_exists` | `str` | `None` | 遇到同名文件时的处理方式,可选`'skip'``'overwrite'``'rename'` |
| `data` | `str`<br>`dict` | `None` | post 方式使用的数据,如不为`None`,使用 post 方式发送请求 |
| `split` | `bool` | `None` | 是否允许多线程分块下载,为`None`则使用下载器内置设置,默认为`True`<br>默认大于 50MB 的文件使用多线程分块下载 |
| `**kwargs` | - | 必填 | requests的`get()`方法参数 |
| 返回类型 | 说明 |
|:---------:| ---------------------------------------------------------------------------- |
| `Mission` | 返回任务对象,任务对象具体属性及方法详见 [DownloadKit](https://gitee.com/g1879/DownloadKit) 相关说明 |
**示例:**
```python
from DrissionPage import WebPage
page = WebPage('s')
# 文件 url
url = 'https://www.baidu.com/img/flexible/logo/pc/result.png'
# 存放路径
save_path = r'C:\download'
# 返回一个任务对象
mission = page.download.add(url, save_path)
# 通过任务对象查看状态
print(mission.rate, mission.info)
```
**输出:**
```console
90% '下载中'
```
---
# ✔️ 接管浏览器下载任务
## 📍 切换下载方式
很多时候,网站会用某些非显式的方式触发浏览器下载,因此不能很好地获取文件 url下载依赖浏览器功能。页面对象可以设置用`DownloadKit`拦截浏览器的下载任务,使下载更快,下载过程更可控。
方法是使用`download_set.by_DownloadKit()`方法,指定使用`DownloadKit`接管浏览器的下载动作。
```python
page.download_set.by_DownloadKit()
```
事实上,页面对象创建时就默认开启了此功能,如果想用浏览器本身的下载功能,可以这样写:
```python
page.download_set.by_browser()
```
!>**注意:**<br>接管浏览器下载任务后会使用 get 方式下载,如果是需要 post 的请求,可能不能正确下载。
---
## 📍 等待下载开始
在浏览器中点击下载按钮后,有时下载不会立即触发,这时如果过快进行其它操作,可能导致一些意想不到的问题。因此设计了`wait_download_begin()`
方法,用于等待下载动作的开始,以便正确接管。可控制浏览器的页面对象`ChromiumPage``WebPage`拥有此方法。
!>**注意:**<br>如果网站须要很长时间准备下载的文件,请设置一个足够长的超时时间。
### 🔸 `wait_download_begin()`
此方法会阻塞程序,等待下载动作触发。如到达超时时间仍未触发,则返回`False`
| 参数名称 | 类型 | 默认值 | 说明 |
|:---------:|:----------------:|:------:| ------------------------------------- |
| `timeout` | `int`<br>`float` | `None` | 等待下载开始的超时时间,为`None`则使用页面对象`timeout`属性 |
| 返回类型 | 说明 |
|:------:| -------- |
| `bool` | 是否等到下载开始 |
**示例:**
```python
page('#download_btn').click()
page.wait_download_begin()
```
---
# ✔️ 查看任务信息
用单线程方式下载,会阻塞程序直到下载完成,因此无须查看任务信息。
用多线程或接管浏览器下载任务的方式下载,可用以下方式查看任务信息。
## 📍 获取单个任务对象
使用`download.add()`
添加任务时,会返回一个任务对象,后续程序可以使用该对象查询该任务下载进度、结果,也可以取消任务。这里不对该对象作详细说明,详情请移步:[DownloadKit](https://gitee.com/g1879/DownloadKit)。
**示例:**
```python
mission = page.download.add('http://xxxx.pdf')
print(mission.id) # 获取任务id
print(mission.rate) # 打印下载进度(百分比)
print(mission.state) # 打印任务状态,'waiting'、'running'、'done'
print(mission.info) # 打印任务信息
print(mission.result) # 打印任务结果,'success'表示成功False表示失败'skip'表示跳过,'cancel'表示取消
```
除添加任务时获取对象,也可以使用`download.get_mission()`获取。在上一个示例中可以看到,任务对象有`id`属性,把任务的`id`传入此方法,会返回该任务对象。
**示例:**
```python
mission_id = mission.id
mission = page.download.get_mission(mission_id)
```
---
## 📍 获取全部任务对象
使用页面对象的`download.missions`属性,可以获取所有下载任务。该属性返回一个`dict`,保存了所有下载任务。以任务对象的`id`为 key。
```python
page.download_set.save_path(r'D:\download')
page.download('http://xxxxx/xxx1.pdf')
page.download('http://xxxxx/xxx1.pdf')
print(page.download.missions)
```
**输出:**
```
{
1: <Mission 1 D:\download\xxx1.pdf xxx1.pdf>
2: <Mission 2 D:\download\xxx1_1.pdf xxx1_1.pdf>
...
}
```
---
## 📍 获取下载失败的任务
使用`download.get_failed_missions()`方法,可以获取下载失败的任务列表。
```python
page.download_set.save_path(r'D:\download')
page.download('http://xxxxx/xxx1.pdf')
page.download('http://xxxxx/xxx1.pdf')
print(page.download.get_failed_missions()
```
**输出:**
```
[
<Mission 1 状态码404 None>,
<Mission 2 状态码404 None>
...
]
```
?>**Tips**<br>获取失败任务对象后,可从其`data`属性读取任务内容,以便记录日志或择机重试。
---
# ✔️ 下载设置
主要的下载设置使用`download_set`内置方法进行,更多运行参数使用`download`属性的子属性进行。
## 📍 设置文件保存路径
### 🔸 `download_set.save_path()`
此方法用于设置文件下载默认保存路径。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------:|:---------------:|:---:| ------------------ |
| `path` | `str`<br>`Path` | 必填 | 文件保存路径,绝对路径和相对路径均可 |
**返回:**`None`
**示例:**
```python
page.download_set.save_path(r'D:\tmp')
```
?>**Tips**<br>- 保存路径可指定不存在的文件夹,程序会自动创建。<br>- 设置默认保存路径后,每个任务仍可在创建时指定自己的保存路径,以覆盖默认设置。
---
## 📍 设置下载方式
### 🔸 `download_set.by_DownloadKit()`
此方法用于设置当前页面对象使用`DownloadKit`下载文件。
### 🔸 `download_set.by_browser()`
此方法用于设置当前页面对象使用浏览器下载工具下载文件。
---
## 📍 设置重名文件处理方法
下载过程中可能遇到保存路径已存在同名文件,`DownloadKit`提供 3 种处理方法。
!>**注意:**<br>这个设置只有在使用`DownloadKit`作为下载工具时有效。
### 🔸 `download_set.if_file_exists.rename()`
此方式会对新文件进行重命名,在文件名后加上序号。
假如,保存路径已存在`abc.txt`文件,新的`abc.txt`文件会自动重命名为`abc_1.txt`,再下载一个`abc.txt`时,会命名为`abc_2.txt`,如此类推。
**示例:** 3 次下载同一个文件
```python
page.download_set.if_file_exists.rename()
page.download('http://xxxxx/xxx.pdf')
page.download('http://xxxxx/xxx.pdf')
page.download('http://xxxxx/xxx.pdf')
```
在文件夹会生成如下 3 个文件:
```
xxx.pdf
xxx_1.pdf
xxx_2.pdf
```
---
### 🔸 `download_set.if_file_exists.skip()`
遇到同名文件时,跳过。同时任务对象的`result`属性设置为`'skip'`
**示例:** 3 次下载同一个文件
```python
page.download_set.if_file_exists.skip()
page.download('http://xxxxx/xxx.pdf')
page.download('http://xxxxx/xxx.pdf')
page.download('http://xxxxx/xxx.pdf')
```
在文件夹只会生成如下 1 个文件:
```
xxx.pdf
```
---
### 🔸 `download_set.if_file_exists.overwrite()`
遇到同名文件时,覆盖。
!>**注意:**<br>这个方式在多线程下载时要慎用,万一多个任务下载多个同名文件,会导致互相覆盖的现象。
?>**Tips**<br>- 除了整体设置,还可以在创建任务时单独设置该任务的处理方式。<br>- 文件名如遇到`'?'``'\'`等非法字符,会自动替换为空格。
---
## 📍 设置大文件是否分块
`DownloadKit`具备多线程下载大文件功能,在文件超过指定大小时(默认 50MB可对文件进行多线程分块下载每个线程负责 50MB 的下载,以提高下载速度。这个功能默认是关闭的,您可以设置是否开启。
!>**注意:**
这个设置只有在使用`DownloadKit`作为下载工具时有效。
### 🔸 `download_set.split()`
```python
# 使用分块下载
page.download_set.split(on_off=True)
# 禁用分块下载
page.download_set.split(on_off=False)
```
?>**Tips**
除了整体设置,还可以在创建任务时单独设置该任务是否使用分块下载。
---
## 📍运行参数设置
运行参数主要包括可使用线程上限、连接失败重试次数、重试间隔。
### 🔸 `download.roads`
此参数设置整个`DownloadKit`对象允许使用的线程数上限,默认为 10。
默认设置下,页面对象最多使用 10 个下载线程,如进程已满,新任务会进入等待队列排队,待有空线程时自动开始。
这个属性只能在没有任务在运行的时候设置。
```python
page.download.roads = 20 # 允许最多使用20个线程进行下载
```
---
### 🔸 `download.retry`
此属性用于设置连接失败时重试次数,默认为 3。
```python
page.download.roads = 5 # 设置连接失败时重试5次
```
---
### 🔸 `download.interval`
此属性用于设置连接失败时重试间隔,默认为 5 秒。
```python
page.download.interval = 10 # 设置连接失败时等待10秒再重试
```
?> **Tips**<br> 重试次数和间隔在初始化时继承页面对象的`retry_times``retry_interval`属性,可用上面例子的方法对下载的重试次数和间隔进行设置,设置后不会影响页面对象的设置。