Skip to content

⚙️ 下载文件

浏览器没有为下载功能提供方便的程序接口,难以实现有效的下载管理,如检测下载状态、重命名、失败管理等。使用 requests 下载文件能较好实现以上功能,但代码较为繁琐。
因此 DrissionPage 内置了高效可靠的下载工具,提供任务管理、多线程、大文件分块、自动重连、文件名冲突处理等功能。

无论是控制浏览器,还是收发数据包的场景,都可以使用它。甚至可以拦截浏览器的下载动作,转用内置下载器进行下载。使用方式简洁高效。

说明:

该工具名为 DownloadKit,现已独立打包成一个库,详细用法见:DownloadKit。这里只介绍和页面对象有关的用法。

✅️️ 功能简介

SessionPageChromiumPageWebPage都可以使用download()方法进行下载,还可以使用download_set属性对下载参数进行配置。内置下载器功能如下:

  • 支持多线程同时下载多个文件
  • 大文件自动分块使用多线程下载
  • 可拦截浏览器下载动作,自动调用下载器下载
  • 自动创建目标路径
  • 自动去除路径中的非法字符
  • 下载时支持文件重命名
  • 自动处理文件名冲突
  • 自动去除文件名非法字符
  • 支持 post 方式
  • 支持自定义连接参数
  • 任务失败自动重试

✅️️ 简单示例

下载一个文件(单线程):

page.download('https://xxxxxx/file.pdf')

添加下载任务,以多线程方式下载:

page.download.add('https://xxxxxx/file.pdf')

拦截浏览器下载动作,改用 DwonloadKit 下载:

page.download_set.by_DownloadKit()
page('#download_btn').click()

下载设置:

# 设置默认保存路径
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
Path
None 保存文件夹路径,为None则保存在当前目录
rename str None 重命名文件名,可不包含后缀
file_exists str None 遇到同名文件时的处理方式,可选'skip''overwrite''rename'
data str
dict
None post 方式使用的数据,如不为None,使用 post 方式发送请求
show_msg bool True 是否显示下载进度
**kwargs - 必填 requests的get()方法参数
返回类型 返回格式 说明
tuple (任务结果, 任务信息) 返回任务结果和信息组成的tuple

任务结果可能出现以下值:

  • 'success':表示下载成功

  • 'skip':表示存在同名文件,跳过任务

  • 'cancel':表示任务下载途中被取消

  • False:表示下载失败

任务信息成功时返回文件绝对路径,其它情况返回相应说明。

示例:

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)

显示:

url:https://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
Path
None 保存文件夹路径,为None则保存在当前目录
rename str None 重命名文件名,可不包含后缀
file_exists str None 遇到同名文件时的处理方式,可选'skip''overwrite''rename'
data str
dict
None post 方式使用的数据,如不为None,使用 post 方式发送请求
split bool None 是否允许多线程分块下载,为None则使用下载器内置设置,默认为True
默认大于 50MB 的文件使用多线程分块下载
**kwargs - 必填 requests的get()方法参数
返回类型 说明
Mission 返回任务对象,任务对象具体属性及方法详见 DownloadKit 相关说明

示例:

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)

输出:

90% '下载中'

✅️️ 接管浏览器下载任务

📌 切换下载方式

很多时候,网站会用某些非显式的方式触发浏览器下载,因此不能很好地获取文件 url,下载依赖浏览器功能。,页面对象可以设置用DownloadKit拦截浏览器的下载任务,使下载更快,下载过程更可控。

方法是使用download_set.by_DownloadKit()方法,指定使用DownloadKit接管浏览器的下载动作。

page.download_set.by_DownloadKit()

事实上,页面对象创建时就默认开启了此功能,如果想用浏览器本身的下载功能,可以这样写:

page.download_set.by_browser()

注意

接管浏览器下载任务后会使用 get 方式下载,如果是需要 post 的请求,可能不能正确下载。


📌 等待下载开始

在浏览器中点击下载按钮后,有时下载不会立即触发,这时如果过快进行其它操作,可能导致一些意想不到的问题。因此设计了wait.download_begin() 方法,用于等待下载动作的开始,以便正确接管。可控制浏览器的页面对象ChromiumPageWebPage拥有此方法。

注意

如果网站须要很长时间准备下载的文件,请设置一个足够长的超时时间。

🔸 wait.download_begin()

此方法会阻塞程序,等待下载动作触发。如到达超时时间仍未触发,则返回False

参数名称 类型 默认值 说明
timeout float None 等待下载开始的超时时间,为None则使用页面对象timeout属性
返回类型 说明
bool 是否等到下载开始

示例:

page('#download_btn').click()
page.wait.download_begin()

✅️️ 查看任务信息

用单线程方式下载,会阻塞程序直到下载完成,因此无须查看任务信息。

用多线程或接管浏览器下载任务的方式下载,可用以下方式查看任务信息。

📌 获取单个任务对象

使用download.add() 添加任务时,会返回一个任务对象,后续程序可以使用该对象查询该任务下载进度、结果,也可以取消任务。这里不对该对象作详细说明,详情请移步:DownloadKit

示例:

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传入此方法,会返回该任务对象。

示例:

mission_id = mission.id
mission = page.download.get_mission(mission_id)

📌 获取全部任务对象

使用页面对象的download.missions属性,可以获取所有下载任务。该属性返回一个dict,保存了所有下载任务。以任务对象的id为 key。

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()方法,可以获取下载失败的任务列表。

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

获取失败任务对象后,可从其data属性读取任务内容,以便记录日志或择机重试。


✅️️ 下载设置

主要的下载设置使用download_set内置方法进行,更多运行参数使用download属性的子属性进行。

📌 设置文件保存路径

🔸 download_set.save_path()

此方法用于设置文件下载默认保存路径。

参数名称 类型 默认值 说明
path str
Path
必填 文件保存路径,绝对路径和相对路径均可

返回:None

示例:

page.download_set.save_path(r'D:\tmp')

Tips

  • 保存路径可指定不存在的文件夹,程序会自动创建。
  • 设置默认保存路径后,每个任务仍可在创建时指定自己的保存路径,以覆盖默认设置。

📌 设置下载方式

🔸 download_set.by_DownloadKit()

此方法用于设置当前页面对象使用DownloadKit下载文件。

🔸 download_set.by_browser()

此方法用于设置当前页面对象使用浏览器下载工具下载文件。


📌 设置重名文件处理方法

下载过程中可能遇到保存路径已存在同名文件,DownloadKit提供 3 种处理方法。

注意

这个设置只有在使用DownloadKit作为下载工具时有效。

🔸 download_set.if_file_exists.rename()

此方式会对新文件进行重命名,在文件名后加上序号。

假如,保存路径已存在abc.txt文件,新的abc.txt文件会自动重命名为abc_1.txt,再下载一个abc.txt时,会命名为abc_2.txt,如此类推。

示例: 3 次下载同一个文件

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 次下载同一个文件

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()

遇到同名文件时,覆盖。

注意

这个方式在多线程下载时要慎用,万一多个任务下载多个同名文件,会导致互相覆盖的现象。

Tips

  • 除了整体设置,还可以在创建任务时单独设置该任务的处理方式。
  • 文件名如遇到'?''\'等非法字符,会自动替换为空格。

📌 设置大文件是否分块

DownloadKit具备多线程下载大文件功能,在文件超过指定大小时(默认 50MB),可对文件进行多线程分块下载,每个线程负责 50MB 的下载,以提高下载速度。这个功能默认是关闭的,您可以设置是否开启。

注意

这个设置只有在使用DownloadKit作为下载工具时有效。

🔸 download_set.split()

# 使用分块下载
page.download_set.split(on_off=True)
# 禁用分块下载
page.download_set.split(on_off=False)

Tips

除了整体设置,还可以在创建任务时单独设置该任务是否使用分块下载。


📌运行参数设置

运行参数主要包括可使用线程上限、连接失败重试次数、重试间隔。

🔸 download.roads

此参数设置整个DownloadKit对象允许使用的线程数上限,默认为 10。

默认设置下,页面对象最多使用 10 个下载线程,如进程已满,新任务会进入等待队列排队,待有空线程时自动开始。

这个属性只能在没有任务在运行的时候设置。

page.download.roads = 20  # 允许最多使用20个线程进行下载

🔸 download.retry

此属性用于设置连接失败时重试次数,默认为 3。

page.download.roads = 5  # 设置连接失败时重试5次

🔸 download.interval

此属性用于设置连接失败时重试间隔,默认为 5 秒。

page.download.interval = 10  # 设置连接失败时等待10秒再重试

Tips

重试次数和间隔在初始化时继承页面对象的retry_timesretry_interval属性,可用上面例子的方法对下载的重试次数和间隔进行设置,设置后不会影响页面对象的设置。