Python实现多线程下载文件的代码实例


Posted in Python onJune 01, 2014

实现简单的多线程下载,需要关注如下几点:
1.文件的大小:可以从reponse header中提取,如“Content-Length:911”表示大小是911字节
2.任务拆分:指定各个线程下载的文件的哪一块,可以通过request header中添加“Range: bytes=300-400”(表示下载300~400byte的内容),注意可以请求的文件的range是[0, size-1]字节的。
3.下载文件的聚合:各个线程将自己下载的文件块保存为临时文件,所有线程都完成后,再将这些临时文件按顺序聚合写入到最终的一个文件中。

实现代码:

#!/usr/bin/python
# -*- coding: utf-8 -*-
# filename: paxel.py
# FROM: http://3water.com/code/view/58/full/
# Jay modified it a little and save for further potential usage.'''It is a multi-thread downloading tool
    It was developed following axel.
        Author: volans
        E-mail: volansw [at] gmail.com
'''
import sys
import os
import time
import urllib
from threading import Thread
# in case you want to use http_proxy
local_proxies = {'http': 'http://131.139.58.200:8080'}
 
class AxelPython(Thread, urllib.FancyURLopener):
    '''Multi-thread downloading class.
        run() is a vitural method of Thread.
    '''
    def __init__(self, threadname, url, filename, ranges=0, proxies={}):
        Thread.__init__(self, name=threadname)
        urllib.FancyURLopener.__init__(self, proxies)
        self.name = threadname
        self.url = url
        self.filename = filename
        self.ranges = ranges
        self.downloaded = 0
    def run(self):
        '''vertual function in Thread'''
        try:
            self.downloaded = os.path.getsize(self.filename)
        except OSError:
            #print 'never downloaded'
            self.downloaded = 0
        # rebuild start poind
        self.startpoint = self.ranges[0] + self.downloaded
        # This part is completed
        if self.startpoint >= self.ranges[1]:
            print 'Part %s has been downloaded over.' % self.filename
            return
        self.oneTimeSize = 16384  # 16kByte/time
        print 'task %s will download from %d to %d' % (self.name, self.startpoint, self.ranges[1])
        self.addheader("Range", "bytes=%d-%d" % (self.startpoint, self.ranges[1]))
        self.urlhandle = self.open(self.url)
        data = self.urlhandle.read(self.oneTimeSize)
        while data:
            filehandle = open(self.filename, 'ab+')
            filehandle.write(data)
            filehandle.close()
            self.downloaded += len(data)
            #print "%s" % (self.name)
            #progress = u'\r...'
            data = self.urlhandle.read(self.oneTimeSize)
 
def GetUrlFileSize(url, proxies={}):
    urlHandler = urllib.urlopen(url, proxies=proxies)
    headers = urlHandler.info().headers
    length = 0
    for header in headers:
        if header.find('Length') != -1:
            length = header.split(':')[-1].strip()
            length = int(length)
    return length
 
def SpliteBlocks(totalsize, blocknumber):
    blocksize = totalsize / blocknumber
    ranges = []
    for i in range(0, blocknumber - 1):
        ranges.append((i * blocksize, i * blocksize + blocksize - 1))
    ranges.append((blocksize * (blocknumber - 1), totalsize - 1))
    return ranges
 
def islive(tasks):
    for task in tasks:
        if task.isAlive():
            return True
    return False
 
def paxel(url, output, blocks=6, proxies=local_proxies):
    ''' paxel
    '''
    size = GetUrlFileSize(url, proxies)
    ranges = SpliteBlocks(size, blocks)
    threadname = ["thread_%d" % i for i in range(0, blocks)]
    filename = ["tmpfile_%d" % i for i in range(0, blocks)]
    tasks = []
    for i in range(0, blocks):
        task = AxelPython(threadname[i], url, filename[i], ranges[i])
        task.setDaemon(True)
        task.start()
        tasks.append(task)
    time.sleep(2)
    while islive(tasks):
        downloaded = sum([task.downloaded for task in tasks])
        process = downloaded / float(size) * 100
        show = u'\rFilesize:%d Downloaded:%d Completed:%.2f%%' % (size, downloaded, process)
        sys.stdout.write(show)
        sys.stdout.flush()
        time.sleep(0.5)
    filehandle = open(output, 'wb+')
    for i in filename:
        f = open(i, 'rb')
        filehandle.write(f.read())
        f.close()
        try:
            os.remove(i)
            pass
        except:
            pass
    filehandle.close()
if __name__ == '__main__':
    url = 'http://dldir1.qq.com/qqfile/QQforMac/QQ_V3.1.1.dmg'
    output = 'download.file'
    paxel(url, output, blocks=4, proxies={})
Python 相关文章推荐
Python中针对函数处理的特殊方法
Mar 06 Python
简单介绍利用TK在Python下进行GUI编程的教程
Apr 13 Python
简单谈谈Python中的元祖(Tuple)和字典(Dict)
Apr 21 Python
详解如何使用Python编写vim插件
Nov 28 Python
python3+requests接口自动化session操作方法
Oct 13 Python
python实现得到当前登录用户信息的方法
Jun 21 Python
python列表生成器迭代器实例解析
Dec 19 Python
TensorBoard 计算图的查看方式
Feb 15 Python
解决Jupyter Notebook开始菜单栏Anaconda下消失的问题
Apr 13 Python
PyInstaller的安装和使用的详细步骤
Jun 02 Python
详解Python自动化之文件自动化处理
Jun 21 Python
python区块链实现简版工作量证明
May 25 Python
python使用在线API查询IP对应的地理位置信息实例
Jun 01 #Python
pip 错误unused-command-line-argument-hard-error-in-future解决办法
Jun 01 #Python
2款Python内存检测工具介绍和使用方法
Jun 01 #Python
使用Python的Supervisor进行进程监控以及自动启动
May 29 #Python
python应用程序在windows下不出现cmd窗口的办法
May 29 #Python
python正则表达式re模块详细介绍
May 29 #Python
在python中的socket模块使用代理实例
May 29 #Python
You might like
php下使用无限生命期Session的方法
2007/03/16 PHP
php木马webshell扫描器代码
2012/01/25 PHP
php.ini修改php上传文件大小限制的方法详解
2013/06/17 PHP
PHP和Shell实现检查SAMBA与NFS Server是否存在
2015/01/07 PHP
PHP6新特性分析
2016/03/03 PHP
Laravel5.* 打印出执行的sql语句的方法
2017/07/24 PHP
PHP实现驼峰样式字符串(首字母大写)转换成下划线样式字符串的方法示例
2017/08/10 PHP
PHP扩展类型及安装方式解析
2020/04/27 PHP
使用jquery与图片美化checkbox和radio控件的代码(打包下载)
2010/11/11 Javascript
JS预览图像将本地图片显示到浏览器上
2013/08/25 Javascript
详解Vue组件之间的数据通信实例
2017/06/17 Javascript
ES6新增数据结构WeakSet的用法详解
2017/08/07 Javascript
轻松搞定jQuery+JSONP跨域请求的解决方案
2018/03/06 jQuery
微信小程序自定义组件之可清除的input组件
2018/07/17 Javascript
基于vue框架手写一个notify插件实现通知功能的方法
2019/03/31 Javascript
Vue页面切换和a链接的本质区别详解
2019/11/12 Javascript
[02:01]BBC DOTA2国际邀请赛每日综述:八强胜者组鏖战,中国队喜忧参半
2014/07/19 DOTA
python基于右递归解决八皇后问题的方法
2015/05/25 Python
python+jinja2实现接口数据批量生成工具
2019/08/28 Python
详解Python3 pandas.merge用法
2019/09/05 Python
Python使用Tkinter实现滚动抽奖器效果
2020/01/06 Python
python脚本监控logstash进程并邮件告警实例
2020/04/28 Python
Python实现GIF图倒放
2020/07/16 Python
Matplotlib 折线图plot()所有用法详解
2020/07/28 Python
Python3 ffmpeg视频转换工具使用方法解析
2020/08/10 Python
Python操控mysql批量插入数据的实现方法
2020/10/27 Python
PHP如何调用MYSQL存储过程
2014/05/30 面试题
教学实习自我评价
2014/01/28 职场文书
希特勒的演讲稿
2014/05/23 职场文书
县人大领导班子四风对照检查材料思想汇报
2014/10/09 职场文书
小兵张嘎观后感
2015/06/03 职场文书
小兵张嘎观后感300字
2015/06/03 职场文书
新生儿未入户证明
2015/06/23 职场文书
同步小康驻村工作简报
2015/07/20 职场文书
MySQL 查询速度慢的原因
2021/05/25 MySQL
PostgreSQL常用字符串分割函数整理汇总
2022/07/07 PostgreSQL