一般来说,下载小的文件,跳过已下载、断点续传、显示下载进度价值不大。
然而,对于大文件下载列表,
1、如果文件很大,显示下载进度就很重要,要不然不知道是不是在下载;
2、如果加上网络也不稳定,容易断开,每次重新开始也很坑人,断点续传就很有必要了;
3、如果下载一个有很多文件的列表,如果已经下载的能直接跳过,自动从没下载或者没有下载完整的地方开始。直接重新启动程序,代码不作任何修改,不完整的就可以自动补充完整,不亦妙乎。
断点续传、显示下载进度、自动跳过,这些单独的算法都有,那么能不能合三为一呢
关键之处在于:
1、如何判断已经下载的都是完整的,总不能每一个文件再都访问一遍网站,获取大小,然后比较吧。速度慢不说,反复一遍遍访问网站也没必要吧。
2、因此,最好是一开始下载的先用临时的文件名(如用.tmp后缀命名),如果下载完整,然后再修改为要下载的文件名。
3、同时,获取下载内容要用上重试,不下载下来,誓不罢休。
关于断点续传,这个实现挺不错的。
Python爬虫使用requests下载文件,实现下载大文件断点续传:https://blog.csdn.net/Python_sn/article/details/109167016
以下代码系参考以上网友断点续传代码修改而来,备自己所需吧
import os,shutil import csvimport requests from retrying import retry import sys def is_request_exception(e): print(e) return True from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) # 禁用安全请求警告
# 反复重试
@retry(retry_on_exception=is_request_exception,wait_random_min=2000, wait_random_max=10000) def get_retry(url,headers): r = requests.get(url, headers=headers, verify=False, timeout=300, stream=True) return r def DownloadFile_new(url, savePath): #url是下载地址,savePath是保存位置 savePath_tmp = savePath + '.tmp' print("downloading {0}".format(url)) if not os.path.exists(savePath) or os.path.exists(savePath_tmp): try: headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0"} response = get_retry(url,headers) response.raise_for_status() # 如果响应状态码不是 200,就主动抛出异常 total_size = int(response.headers['Content-Length']) # print(response.headers) if os.path.exists(savePath_tmp): temp_size = os.path.getsize(savePath_tmp) print("当前:%d 字节, 总:%d 字节, 已下载:%2.2f%% " % (temp_size, total_size, 100 * temp_size / total_size)) if temp_size == total_size: shutil.move(savePath_tmp,savePath) print('已下载完毕') return True else: temp_size = 0 print("总:%d 字节,开始下载..." % (total_size,)) headers = {'Range': 'bytes=%d-' % temp_size, "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0"} res_left = get_retry(url, headers) with open(savePath_tmp, "ab") as f: for chunk in res_left.iter_content(chunk_size=1024): temp_size += len(chunk) f.write(chunk) f.flush() done = int(50 * temp_size / total_size) sys.stdout.write("\r[%s%s] %d%%" % ('█' * done, ' ' * (50 - done), 100 * temp_size / total_size)) sys.stdout.flush() if temp_size ==total_size: shutil.move(savePath_tmp,savePath) print('\nsave file succeed\n') except requests.RequestException as e: #出错,记录 print(e) with open('D:/timeout.txt', 'a+',encoding = 'UTF-8') as fw: #读入存储文件路径 fw.write('%s,%s\n' % (url,e)) else: print('文件已下载') with open('D:/下载列表.csv','r',encoding='utf-8') as dl_list: items=csv.reader(dl_list,delimiter='\t') for item in items: url=item[0] savePath=item[1] DownloadFile_new(url, savePath)