图片下载
tqdm
tqdm是一个快速、扩展性强的进度条工具库,用户只需要封装任意的迭代器 tqdm(iterator),tqdm官方文档。
对于爬虫进度的监控,这是个不错的工具。
requests模块实现下载
对于requests的网络请求返回结果中,当需要获取文本的时候我们会使用response.text获取文本信息,使用response.content获取字节流,比如下载图片保存到一个文件,而对于大个的文件我们就要采取分块读取的方式了。
第一步,我们需要设置requests.get的stream参数为True。 默认情况下是stream的值为false,它会立即开始下载文件并存放到内存当中,倘若文件过大就会导致内存不足的情况.当把get函数的stream参数设置成True时,它不会立即开始下载,当你使用iter_content或iter_lines遍历内容或访问内容属性时才开始下载。需要注意一点:文件没有下载之前,它也需要保持连接。
断点续传
所谓断点续传,也就是要从文件已经下载的地方开始继续下载。在以前版本的 HTTP 协议是不支持断点的,HTTP/1.1 开始就支持了。一般断点下载时会用到 header请求头的Range字段,这也是现在众多号称多线程下载工具(如 FlashGet、迅雷等)实现多线程下载的核心所在。
HTTP请求头Range
range是请求资源的部分内容(不包括响应头的大小),单位是byte,即字节,从0开始. 如果服务器能够正常响应的话,服务器会返回 206 Partial Content 的状态码及说明. 如果不能处理这种Range的话,就会返回整个资源以及响应状态码为 200 OK .(这个要注意,要分段下载时,要先判断这个)。
Range请求头格式
Range: bytes=start-end
Range头域
Range头域可以请求实体的一个或者多个子范围。例如,
表示头500个字节:bytes=0-499
表示第二个500字节:bytes=500-999
表示最后500个字节:bytes=-500
表示500字节以后的范围:bytes=500-
第一个和最后一个字节:bytes=0-0,-1
同时指定几个范围:bytes=500-600,601-999
Range: bytes=10- :第10个字节及最后个字节的数据
Range: bytes=40-100 :第40个字节到第100个字节之间的数据.
注意,这个表示[start,end],即是包含请求头的start及end字节的,所以,下一个请求,应该是上一个请求的[end+1, nextEnd]
程序实现
这里的断点续传没有做过度复杂的实现,只是简单的在当前目录下进行的。
# -*- coding: utf-8 -*-
import requests
from tqdm import tqdm
import os
import time
# 用法一
def tqdm_demo():
text = ""
for char in tqdm(["a", "b", "c", "d"]):
text = text + char
time.sleep(0.5)
# 用法二
def tqdm_demo2():
pbar = tqdm(["a", "b", "c", "d"])
for char in pbar:
time.sleep(0.5)
pbar.set_description("Processing %s" % char)
# 手动控制运行
# tqdm.update()方法用于手动更新进度条,对读取文件之类的流操作非常有用。
def tqdm_demo3():
with tqdm(total=100) as pbar:
for i in range(10):
pbar.update(10)
time.sleep(0.5)
def download_from_url(url, dst):
'''
:param url: 下载地址
:param dst: 文件名称
:return:
'''
#发起网络请求
response = requests.get(url, stream=True)
#获取返回的文件的大小
file_size = int(response.headers['content-length'])
#判断当前目录中是否有该文件,如果有获取文件的大小,从而实现断点续传
if os.path.exists(dst):
first_byte = os.path.getsize(dst)
else:
first_byte = 0
#如果文件大小已经超过了服务器返回的文件的大小,返回文件长度
if first_byte >= file_size: #(4)
return file_size
#设置断点续传的位置
header = {"Range": f"bytes=%s-%s"%(first_byte,file_size)}
# desc :进度条的前缀
# unit 定义每个迭代的单元。默认为"it",即每个迭代,在下载或解压时,设为"B",代表每个“块”。
# unit_scale 默认为False,如果设置为1或者True,会自动根据国际单位制进行转换 (kilo, mega, etc.) 。比如,在下载进度条的例子中,如果为False,数据大小是按照字节显示,设为True之后转换为Kb、Mb。
#total:总的迭代次数,不设置则只显示统计信息,没有图形化的进度条。设置为len(iterable),会显示黑色方块的图形化进度条。
pbar = tqdm(total=file_size, initial=first_byte,unit='B', unit_scale=True, desc=dst)
#发送网络请求
req = requests.get(url, headers=header, stream=True) #(5)
#这里的二进制需要采用追加的方式写入文件,不然无法实现断点续传
with(open(dst, 'ab')) as f:
for chunk in req.iter_content(chunk_size=1024): #(6)
if chunk:
#用于方便观察进度条,在下载大视频的时候去掉也能观察出来
time.sleep(0.01)
f.write(chunk)
f.flush()
pbar.update(1024)
pbar.close()
return file_size
if __name__ == '__main__':
url = "https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1551406646&di=a385cb186c0f1c2c45e5c49b4015e848&src=http://img18.3lian.com/d/file/201709/21/f498e01633b5b704ebfe0385f52bad20.jpg"
download_from_url(url, "百度美女图片.jpg")
#以下是tqdm实例
tqdm_demo()
tqdm_demo2()
tqdm_demo3()