Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >python编写断点续传下载软件

python编写断点续传下载软件

作者头像
phith0n
发布于 2020-10-15 02:30:21
发布于 2020-10-15 02:30:21
1.7K00
代码可运行
举报
运行总次数:0
代码可运行

一年一度的python小程序编写系列之——断点续传下载软件。

一、HTTP断点续传原理

其实HTTP断点续传原理比较简单,在HTTP数据包中,可以增加Range头,这个头以字节为单位指定请求的范围,来下载范围内的字节流。如:

如上图勾下来的地方,我们发送数据包时选定请求的内容的范围,返回包即获得相应长度的内容。所以,我们在下载的时候,可以将目标文件分成很多“小块”,每次下载一小块(用Range标明小块的范围),直到把所有小块下载完。

当网络中断,或出错导致下载终止时,我们只需要记录下已经下载了哪些“小块”,还没有下载哪些。下次下载的时候在Range处填写未下载的小块的范围即可,这样就能构成一个断点续传。

其实像迅雷这种多线程下载器也是同样的原理。将目标文件分成一些小块,再分配给不同线程去下载,最后整合再检查完整性即可。

二、Python下载文件实现方式

我们仍然使用之前介绍过的requests库作为HTTP请求库。

先看看这段文档:http://docs.python-requests.org/en/latest/user/advanced/#body-content-workflow,当请求时设置steam=True的时候就不会立即关闭连接,而我们以流的形式读取body,直到所有信息读取完全或者调用Response.close关闭连接。

所以,如果要下载大文件的话,就将steam设置为True,慢慢下载,而不是等整个文件下载完才返回。

stackoverflow上有同学给出了一个简单的下载demo:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def download_file(url):
    local_filename = url.split('/')[-1]
    # NOTE the stream=True parameter
    r = requests.get(url, stream=True)
    with open(local_filename, 'wb') as f:
        for chunk in r.iter_content(chunk_size=1024): 
            if chunk: # filter out keep-alive new chunks
                f.write(chunk)
                f.flush()
    return local_filename

这基本上就是我们核心的下载代码了。

三、断点续传结合大文件下载

好,我们结合这两个知识点写个小程序:支持断点续传的下载器。

我们可以先考虑一下需要注意的有哪些点,或者可以添加的功能有哪些:

  1. 用户自定义性:可以定义cookie、referer、user-agent。如某些下载站检查用户登录才允许下载等情况。
  2. 很多服务端不支持断点续传,如何判断?
  3. 怎么去表达进度条?
  4. 如何得知文件的总大小?使用HEAD请求?那么服务器不支持HEAD请求怎么办?
  5. 下载后的文件名(header中可能有filename,url中也有filename,用户还可以自己指定filename),怎么处理?还要考虑windows不允许哪些字符做文件名。
  6. 如何去分块,是否加入多线程。

其实想一下还是有很多疑虑,而且有些地方可能一时还解决不了。先大概想一下各个问题的答案:

  1. headers可以由用户自定义
  2. 正式下载之前先HEAD请求,得到服务器status code是否是206,header中是否有Range-content等标志,判断是否支持断点续传。
  3. 可以先不使用进度条,只显示当前下载大小和总大小
  4. 在HEAD请求中匹配出Range-content中的文件总大小,或获得content-length大小(当不支持断点续传的时候会返回总content-length)。如果不支持HEAD请求或没有content-type就设置总大小为0.(总之不会妨碍下载即可)
  5. 文件名优先级:用户自定义 > header中content-disposition > url中的定义,为了避免麻烦,我这里和linux下的wget一样,忽略content-disposition的定义。如果用户不指定保存的用户名的话,就以url中最后一个“/”后的内容作为用户名。
  6. 为了稳定和简单,不做多线程了。如果不做多线程的话,我们分块就可以按照很小来分,如1KB,然后从头开始下载,一K一K这样往后填充。这样避免了很多麻烦。当下载中断的时候,我们只需要简单查看当前已经下载了多少字节,就可以从这个字节后一个开始继续下载。

解决了这些疑问,我们就开始动笔了。实际上,疑问并不是在未动笔的时候干想出来的,基本都是我写了一半突然发现的问题。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def download(self, url, filename, headers = {}):
    finished = False
    block = self.config['block']
    local_filename = self.remove_nonchars(filename)
    tmp_filename = local_filename + '.downtmp'
    if self.support_continue(url):  # 支持断点续传
        try:
            with open(tmp_filename, 'rb') as fin:
                self.size = int(fin.read()) + 1
        except:
            self.touch(tmp_filename)
        finally:
            headers['Range'] = "bytes=%d-" % (self.size, )
    else:
        self.touch(tmp_filename)
        self.touch(local_filename)

    size = self.size
    total = self.total
    r = requests.get(url, stream = True, verify = False, headers = headers)
    if total > 0:
        print "[+] Size: %dKB" % (total / 1024)
    else:
        print "[+] Size: None"
    start_t = time.time()
    with open(local_filename, 'ab') as f:
        try:
            for chunk in r.iter_content(chunk_size = block): 
                if chunk:
                    f.write(chunk)
                    size += len(chunk)
                    f.flush()
                sys.stdout.write('\b' * 64 + 'Now: %d, Total: %s' % (size, total))
                sys.stdout.flush()
            finished = True
            os.remove(tmp_filename)
            spend = int(time.time() - start_t)
            speed = int(size / 1024 / spend)
            sys.stdout.write('\nDownload Finished!\nTotal Time: %ss, Download Speed: %sk/s\n' % (spend, speed))
            sys.stdout.flush()

        except:
            import traceback
            print traceback.print_exc()
            print "\nDownload pause.\n"
        finally:
            if not finished:
                with open(tmp_filename, 'wb') as ftmp:
                    ftmp.write(str(size))

这是下载的方法。首先if语句调用self.support_continue(url)判断是否支持断点续传。如果支持则从一个临时文件中读取当前已经下载了多少字节,如果不存在这个文件则会抛出错误,那么size默认=0,说明一个字节都没有下载。

然后就请求url,获得下载连接,for循环下载。这个时候我们得抓住异常,一旦出现异常,不能让程序退出,而是正常将当前已下载字节size写入临时文件中。下次再次下载的时候读取这个文件,将Range设置成bytes=(size+1)-,也就是从当前字节的后一个字节开始到结束的范围。从这个范围开始下载,来实现一个断点续传。

判断是否支持断点续传的方法还兼顾了一个获得目标文件大小的功能:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def support_continue(self, url):
    headers = {
        'Range': 'bytes=0-4'
    }
    try:
        r = requests.head(url, headers = headers)
        crange = r.headers['content-range']
        self.total = int(re.match(ur'^bytes 0-4/(\d+)$', crange).group(1))
        return True
    except:
        pass
    try:
        self.total = int(r.headers['content-length'])
    except:
        self.total = 0
    return False

用正则匹配出大小,获得直接获取headers['content-length'],获得将其设置为0.

核心代码基本上就是这些,再就是一些设置了,各位更可以去github直接看:https://github.com/phith0n/py-wget/blob/master/py-wget.py

运行来获取一下emlog最新的安装包:

中间我按Contrl + C人工打断了下载进程,但之后还是继续下载,实现了“断点续传”。但在我实际测试过程中,并不是那么多请求可以断点续传的,所以我对于不支持断点续传的文件这样处理:重新下载。

下载后的压缩包正常解压,也充分证明了下载的完整性:

做了个小动图:

如果你想把我的这个小脚本当一个工具来用的话,可以查看github下的说明:https://github.com/phith0n/py-wget

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
⭐Python实现多线程并发下载大文件(制作支持断点续传的下载器的绝佳参考⁉️)⭐
不知道各位童鞋们是否遇到过需要使用python下载大文件的需求,或者需要从一些网速很慢的网站上下载文件。如果你在实际下载过程碰到下载不稳定经常失败的情况,本文的方法将会给你带来一些解决思路和方案。
全栈程序员站长
2022/09/13
2.2K0
⭐Python实现多线程并发下载大文件(制作支持断点续传的下载器的绝佳参考⁉️)⭐
Python 实现视频爬取下载及断电续传优化
一般情况下我们使用爬虫更多的应该是爬数据或者图片吧,今天在这里和大家分享一下关于使用爬虫技术来进行视频下载的方法,不仅可以方便的下载一些体积小的视频,针对大容量的视频下载同样试用。
崔庆才
2019/05/06
1.9K0
Python 实现视频爬取下载及断电续传优化
撸了个多线程断点续传下载器,我从中学习到了这些知识
感谢看客老爷点进来了,周末闲来无事,想起同事强哥的那句话:“你有没有玩过断点续传?” 当时转念一想,断点续传下载用的确实不少,具体细节嘛,真的没有去思考过啊。这不,思考过后有了这篇文章。感谢强哥,让我有了一篇可以水的文章,下面会用纯 Java 无依赖实现一个简单的多线程断点续传下载器。
未读代码
2020/07/28
9610
撸了个多线程断点续传下载器,我从中学习到了这些知识
断点续传原理分析
,文件的传输是一项至关重要的功能,也是资源共享的基础。无论是HTTP、FTP等协议,都支持文件的传输。然而,由于网络的不稳定性或其他原因,文件传输过程中可能会中断。为了解决这个问题,断点续传技术应运而生。
炒香菇的书呆子
2024/10/12
6310
IOS支持音频流断点续传
在使用cgi编写输出音频流接口,前端同事无法拖动播放,于是查阅资料找到了一个关键词:断点续传 断点续传的解释: 断点续传:指的是在上传/下载时,将任务(一个文件或压缩包)人为的划分为几个部分,每一个部分采用一个线程进行上传/下载,如果碰到网络故障,可以从已经上传/下载的部分开始继续上传/下载未完成的部分,而没有必要从头开始上传/下载。可以节省时间,提高速度。 断点续传的用途: 有时用户上传/下载文件需要历时数小时,万一线路中断,不具备断点续传的 HTTP/FTP 服务器或下载软件就只能从头重传,比较好的 HTTP/FTP 服务器或下载软件具有断点续传能力,允许用户从上传/下载断线的地方继续传送,这样大大减少了用户的烦恼。 常见的支持断点续传的上传/下载软件:QQ 旋风、迅雷、快车、电驴、酷6、土豆、优酷、百度视频、新浪视频、腾讯视频、百度云等。 HTTP1.1 协议(RFC2616)开始支持获取文件的部分内容,这为并行下载以及断点续传提供了技术支持。它通过在 Header 里两个参数实现的,客户端发请求时对应的是 Range ,服务器端响应时对应的是 Content-Range。 解决方案: 在返回标头中新增两个参数 printf("Content-Length: %ld\n", file_size); printf("Content-Range: bytes 0-%ld/%ld\n", file_size - 1, file_size); 这里我将文件长度和范围都返回给前端,实现了拖动播放。
lainzhang
2022/08/24
1.2K0
大文件进行断点续传,用php如何实现,确定不来看一下?
断点续传原理 所谓断点续传,也就是要从文件已经下载的地方开始继续下载。在以前版本的 HTTP 协议是不支持断点的,HTTP/1.1 开始就支持了。一般断点下载时才用到 Range 和 Content-Range 实体头。 精粹 Range的完整格式是: Range: bytes=startOffset-targetOffset/sum [表示从startOffset读取,一直读取到targetOffset位置,读取总数为sum直接] Range: bytes=startOffset-targetOffs
友儿
2022/09/11
9570
PHP实现文件下载断点续传
如果我们的网站提供文件下载的服务,那么通常我们都希望下载可以断点续传(Resumable Download),也就是说用户可以暂停下载,并在未来的某个时间从暂停处继续下载,而不必重新下载整个文件。
wangxl
2018/07/27
1.9K0
掀开断点续传那一层面纱(下载篇)
  这一篇文章主要介绍的是http协议下载时的断点续传,详细到各个步骤。主要步骤有:DNS查找、TCP三次握手、http请求发送、TCP协议数据传输、暂停后的状态、继续下载、TCP三次握手、http请求发送、数据传输、。。。、下载成功发送http响应信息、TCP四次握手断开连接。
那一叶随风
2018/09/27
8410
掀开断点续传那一层面纱(下载篇)
iOS断点续传
在项目中可能会用到较大的文件下载,比如较大的音频和视频文件,不可能一次下载完毕,用户可能下载一段时间,关闭程序,再次打开程序接着下载。这个时候,就需要实现断点续传的功能。让用户可以随时暂停下载,下次开始下载,还能接着上次的下载的进度。
赵哥窟
2019/05/10
1.7K0
Python实现GCS bucket断点续传功能,分块上传文件
我有一个关于使用断点续传到Google Cloud Storage的上传速度的问题。我已经编写了一个Python客户端,用于将大文件上传到GCS(它具有一些特殊功能,这就是为什么gsutil对我公司不适用的原因)。在大约2个月前运行的测试中,它很好地利用了可用的连接带宽,其中25Mbps连接中大约有20Mbps。该项目被冻结了将近2个月,现在,当重新打开该项目时,同一客户端以非常慢的速度上载,速度约为25Mbps的1.4Mbps。我已经编写了简单的Python脚本来检查它是否也会遇到相同的问题,并且速度稍快一些,但仍约为2Mbps。Gsutil工具的执行效果几乎与我的Python脚本相同。我还以超过50Mbps的上传速度在不同的网络基础架构上运行了该测试,效果非常好。
青年夏日
2021/05/11
1.4K0
Node+Vue 实现大文件上传,断点续传等
【每日更新 Suggest 👍 question & answers】正经的前端学习 🌟🌟 一个 ☝️ 正经的前端学习,以阶段性来驱动学习,每天进步一点点🤏 !leetcode题解,记录自己的leetcode解题之路…… webvueblog.github.io/file-breakp… 地址 👇 github.com/webVueBlog/… Node+Vue 实现大文件上传,断点续传等 Vue 大文件上传和断点续传(帮忙点赞star谢谢,感谢♥) file-breakpoint-continue 源代码
达达前端
2022/04/29
2.9K0
Node+Vue 实现大文件上传,断点续传等
ASP.NET用HttpListener实现文件断点续传
本文转载:http://www.cnblogs.com/TianFang/archive/2007/01/03/610739.html
跟着阿笨一起玩NET
2018/09/19
8020
如何手写一款KOA的中间件来实现断点续传
这几天在认认真真地学习KOA框架,了解它的原理以及KOA中间件的实现方法。在研究KOA如何处理上传的表单数据的时候,我灵光一闪,这是不是可以用于断点续传?
小美娜娜
2019/04/04
9170
如何手写一款KOA的中间件来实现断点续传
php文件下载限速,文件断点续传,多线程下载文件原理解析
文件下载限速 首先,我们写一段使用php输出文件给浏览器下载的代码 <?php /**  * Created by PhpStorm.  * User: tioncico  * Date: 19-2
仙士可
2019/12/19
2.7K0
php文件下载限速,文件断点续传,多线程下载文件原理解析
【黄啊码】PHP实现文件下载,支持断点续传
PHP实现文件下载接口,支持断点续传,下载器可以查看文件大小。文件分片传输,内存消耗低。注意:使用时建议增加安全路径限制及可下载文件类型限制
黄啊码
2022/09/02
4150
前端实现文件的断点续传
以前文件无法分割,但随着HTML5新特性的引入,类似普通字符串、数组的分割,我们可以可以使用slice方法来分割文件。
书童小二
2018/09/03
3.2K0
前端实现文件的断点续传
Node.js实现大文件断点续传
这就产生了对应的解决方法,对于大文件上传时的暂停、断网、网络较差的情况下, 使用切片+断点续传就能够很好的应对上述的情况
coder2028
2022/10/03
1.8K0
http大文件断点续传
Accept-Ranges: bytes - 该响应头表明服务器支持Range请求,以及服务器所支持的单位是字节(这也是唯一可用的单位).我们还能知道:服务器支持断点续传,以及支持同时下载文件的多个部分,也就是说下载工具可以利用范围请求加速下载该文件.Accept-Ranges: none 响应头表示服务器不支持范围请求.
用户1286680
2021/08/18
1.9K0
Qt开源网络库[13]-断点续传下载
  默认开启断点续传下载功能,即当程序异常退出时,再次运行会接着从已下载的位置下载。   需要注意的是:如果服务器不支持断点续传功能,则每次下载都是从头开始下载。
Qt君
2023/03/17
5050
Qt开源网络库[13]-断点续传下载
.net断点续传的原理
在了解HTTP断点续传的原理之前,先来说说HTTP协议,HTTP协议是一种基于tcp的简单协议,分为请求和回复两种。请求协议是由客户机(浏览器)向服务器(WEB SERVER)提交请求时发送报文的协议。回复协议是由服务器(web server),向客户机(浏览器)回复报文时的协议。请求和回复协议都由头和体组成。头和体之间以一行空行为分隔。
全栈程序员站长
2022/07/18
8250
相关推荐
⭐Python实现多线程并发下载大文件(制作支持断点续传的下载器的绝佳参考⁉️)⭐
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验