方法一:
使用 urllib 模块提供的 urlretrieve() 函数。urlretrieve() 方法直接将远程数据下载到本地。
urlretrieve(url, [filename=None, [reporthook=None, [data=None]]])
- 请求url不可为空: urlretrieve(url)
- 下载后的文件保存路径可为空: urlretrieve(url, [filename=None])
- 传输到服务器的数据data可为空: urlretrieve(url, [filename=None, [reporthook=None, [data=None]]])
说明:
-
参数 finename 指定了保存本地路径(如果参数未指定,urllib会生成一个临时文件保存数据。)
-
参数 reporthook 是一个回调函数,当连接上服务器、以及相应的数据块传输完毕时会触发该回调,我们可以利用这个回调函数来显示当前的下载进度。
-
参数 data 指 post 到服务器的数据,该方法返回一个包含两个元素的(filename, headers)元组,filename 表示保存到本地的路径,header 表示服务器的响应头。
实例:
from urllib import request load_url = 'http://docs.python-requests.org/zh_CN/latest/_static/requests-sidebar.png' def Schedule(a,b,c): ''' a:已经下载的数据块 b:数据块的大小 c:远程文件的大小 ''' per = 100.0 * a * b / c if per > 100 : per = 100 print('%.2f%%' % per) request.urlretrieve(url=load_url,filename='d:\\python.png',reporthook=Schedule)
方法二:
使用requests模块
使用该方法下载大文件可以防止占用过多的内存,因为每次只下载小部分数据。
import requests load_url = 'http://docs.python-requests.org/zh_CN/latest/_static/requests-sidebar.png' r = requests.get(url=load_url,stream=True) with open("D:\\python.png","wb") as f: for chunk in r.iter_content(chunk_size=1024): f.write(chunk)
-
stream参数默认为False,它会立即开始下载文件并放到内存中,如果文件过大,有可能导致内存不足。
-
stream参数设置成True时,它不会立即开始下载,当你使用iter_content或iter_lines遍历内容或访问内容属性时才开始下载。
-
iter_content:一块一块的遍历要下载的内容
-
iter_lines:一行一行的遍历要下载的内容
关于requests请求时的stream参数详解:
默认情况下,当你进行网络请求后,响应体会立即被下载。(当响应体数据量过大时,立即下载会占用系统大量内存)【当响应体数据量并不是很大时,可以不用考虑内存,即可以直接下载】
你可以通过 stream
参数覆盖这个行为,推迟下载响应体直到访问 Response.content
属性:(请求后不立即下载响应体,而是先下载响应头)【当访问r.content字节码的时候才会下载响应体】
tarball_url = 'https://github.com/kennethreitz/requests/tarball/master' r = requests.get(tarball_url, stream=True)
此时仅有响应头被下载下来了,连接保持打开状态,因此允许我们根据条件获取内容:
if int(r.headers['content-length']) < TOO_LONG: content = r.content # 此时开始下载响应体 ...
解释:
归功于 urllib3,同一会话内的持久连接是完全自动处理的!同一会话内你发出的任何请求都会自动复用恰当的连接。
只有所有的响应体数据被读取完毕连接才会被释放为连接池;以下是释放连接池的两种情况:
- 所以当requests请求时设置的参数stream为False时【requests请求时,stream默认为False】,所有的响应体数据被读取完之后立即释放连接池。
- 或者当requests请求时设置的参数stream为True时,当访问r.content属性且读取下载响应体数据之后也会立即释放连接池。
当然:如果你在请求中把 stream 设为 True 的时候,且不访问任何方法,那么Requests 无法将连接释放回连接池,除非你消耗了所有的数据,或者调用了 Response.close 。 否则这样会带来连接效率低下的问题。
关于Response.iter_content的详解
Response.iter_content是可迭代对象。
一块一块的遍历要下载的内容【可以理解为一块一块的下载服务器返回的数据量过大的响应体数据】,然后通过对文件流的操作按块逐一写入指定的路径文件中。
在请求中设置stream = True时,可以避免将内容一次性读入内存以获得较大的响应。块大小是它应该循环每次读入内存的字节数【即chunk_size=1024]。