简介
Python提供了多种下载web文件(pdf、文档、图片和视频等)的方式,在本文中将介绍以下要点:
- 下载常规文件;
- 下载重定向文件;
- 下载大型文件;
- 多线程下载。
requests方法
requests 模块提供了简单的方式实现url链接文件的下载。
比如以下代码:
1 2 3 4 5 6 |
import requests url = 'https://www.python.org/static/img/python-logo@2x.png' file = requests.get(url) with open('logo.png', 'wb') as f: f.write(file.content) |
在这里我们下载Python官网的logo图片。首先导入 requests 模块,调用 get 方法下载url的响应头,连接保持打开状态,并将结果保存到变量 file 中,最后通过 file.content 将内容写入文件方法,将内容保存到当前目录,并命名为logo.png。
wget方法
熟悉Linux命令的都对 wget 不陌生,Python同样提供了 wget 模块,能够通过url很方便地下载文件。
代码如下:
1 2 3 4 |
import wget url = 'https://www.python.org/static/img/python-logo@2x.png' wget.download(url, './logo.png') |
同样是下载Python官网的logo文件。在这段代码中,url和本地保存的地址直接传递给 wget.download 方法。需要注意的是 wget 本身不在 anaconda 中,需要使用 pip install wget 下载安装。
重定向文件下载
在下载一个文件时, 通过url链接常常不能直接访问文件url,web服务器会经过重定向方式引导到真正的链接地址,Python提供了适合重定向的链接下载方式。
代码如下:
1 2 3 4 5 6 |
import requests url = 'https://readthedocs.org/projects/python-guide/downloads/pdf/latest/' file = requests.get(url, allow_redirects=True) with open('finthon.pdf', 'wb') as f: f.write(file.content) |
在这里,我们同样使用 requests 模块的 get 方法获取该url的内容,在 get 方法中传入 allow_redirects=True 参数,允许重定向操作,并将重定向后的内容传给变量 file ,最后保存文件到finthon.pdf。
大文件分块下载
默认情况下, requests 当你进行网络请求后,响应体会立即被下载,当数据过大时会导致内存不足。当在请求上设置 stream=True 时,这避免了立即将内容读入内存以获得较大的响应。推迟下载响应体直到访问 Response.content 属性。如果 stream=False (默认),数据将作为单个块返回。
代码如下:
1 2 3 4 5 6 7 8 |
import requests url = 'https://www.python.org/static/img/python-logo@2x.png' with open('logo.png', 'wb') as f: with requests.get(url, stream=True) as r: for chunk in r.iter_content(chunk_size=20): if chunk: f.write(chunk) |
如果你在请求中 stream=True , Requests 无法将连接释放回连接池,除非你消耗了所有的数据,或者调用了 Response.close 。 这样会带来连接效率低下的问题。如果你发现你在使用 stream=True 的同时还在部分读取请求的 body(或者完全没有读取 body),那么你就应该考虑使用 with 语句发送请求,这样可以保证请求一定会被关闭。
在这里 chunk_size 控制每次读取大小为20字节的块,当所有的块都读完后,保存到logo.png文件中。
并行批量下载多个文件
1 2 3 |
import requests from time import time from multiprocessing.pool import ThreadPool |
我们导入了 time 模块来检查下载文件需要多少时间。 ThreadPool 模块允许你使用池运行多个线程或进程。
首先创建一个简单的函数,将响应分块发送到一个文件:
1 2 3 4 5 6 |
def get_url(url, name): with open(name, 'wb') as f: with requests.get(url, stream=True) as r: for chunk in r.iter_content(chunk_size=20): if chunk: f.write(chunk) |
接下来我们要批量下载多个url文件:
1 2 3 4 5 6 7 8 |
urls = [('event1', 'https://www.python.org/events/python-events/805'), ('event2', 'https://www.python.org/events/python-events/801'), ('event3', 'https://www.python.org/events/python-events/790'), ('event4', 'https://www.python.org/events/python-events/798'), ('event5', 'https://www.python.org/events/python-events/807'), ('event6', 'https://www.python.org/events/python-events/807'), ('event7', 'https://www.python.org/events/python-events/757'), ('event8', 'https://www.python.org/events/python-events/816')] |
我们将这些url传递给 requests.get 。最后,我们打开文件(url中指定的路径)并写入页面内容。
现在,我们可以分别为每个url调用这个函数,我们也可以同时为所有url调用这个函数。让我们在for循环中分别为每个url调用这个函数,注意计时器:
1 2 3 4 5 |
start = time() for x in urls: get_url(x[1], x[0]) end = time() print('time to download: ', end - start) |
输出:
1 |
time to download: 10.102667570114136 |
现在,考虑以下代码:
1 2 3 4 5 6 7 8 |
p = ThreadPool(8) start = time() for x in urls: p.apply_async(get_url, args=(x[1], x[0])) p.close() p.join() #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束 end = time() print('time to download: ', end - start) |
输出:
1 |
time to download: 2.9488866329193115 |
可以看出,使用多进程后,程序运行的速度更快了。
创建进度条下载
进度条是 clint 模块的一个UI组件。输入以下命令来安装 clint 模块:
1 |
pip install clint |
有如下代码:
1 2 3 4 5 6 7 8 9 10 |
import requests from clint.textui import progress url = 'https://do1.dr-chuck.com/pythonlearn/EN_us/pythonlearn.pdf' r = requests.get(url, stream=True) with open('lp.pdf', 'wb') as f: total_length = int(r.headers.get('content-length')) for x in progress.bar(r.iter_content(chunk_size=200000), expected_size=(total_length/1024) + 1): if x: f.write(x) |
在这段代码中,我们首先导入了 requests 模块,然后,我们从 clint.textui 导入了进度组件。唯一的区别是在for循环中。在将内容写入文件时,我们使用了进度条模块的 bar 方法。
总结
本文介绍了Python下载web文件的各种方法,记得点赞收藏哦!