python多线程http下载实现示例


Posted in Python onDecember 30, 2013

测试平台 Ubuntu 13.04 X86_64 Python 2.7.4

花了将近两个小时, 问题主要刚开始没有想到传一个文件对象到线程里面去, 导致下载下来的文件和源文件MD5不一样,浪费不少时间.

有兴趣的同学可以拿去加上参数,改进下, 也可以加上断点续传.

# -*- coding: utf-8 -*-
# Author: ToughGuy
# Email: wj0630@gmail.com
# 写这玩意儿是为了初步了解下python的多线程机制
# 平时没写注释的习惯, 这次花时间在代码里面写上注释也是希望有问题的地方请各位指正, 因为可能我自己也没弄明白.
# 测试平台 Ubuntu 13.04 X86_64 Python 2.7.4
import threading
import urllib2
import sys
max_thread = 10
# 初始化锁
lock = threading.RLock()
class Downloader(threading.Thread):
    def __init__(self, url, start_size, end_size, fobj, buffer):
        self.url = url
        self.buffer = buffer
        self.start_size = start_size
        self.end_size = end_size
        self.fobj = fobj
        threading.Thread.__init__(self)
    def run(self):
        """
            马甲而已
        """
        with lock:
            print 'starting: %s' % self.getName()
        self._download()
    def _download(self):
        """
            我才是搬砖的
        """
        req = urllib2.Request(self.url)
        # 添加HTTP Header(RANGE)设置下载数据的范围
        req.headers['Range'] = 'bytes=%s-%s' % (self.start_size, self.end_size)
        f = urllib2.urlopen(req)
        # 初始化当前线程文件对象偏移量
        offset = self.start_size
        while 1:
            block = f.read(self.buffer)
            # 当前线程数据获取完毕后则退出
            if not block:
                with lock:
                    print '%s done.' % self.getName()
                break
            # 写如数据的时候当然要锁住线程
            # 使用 with lock 替代传统的 lock.acquire().....lock.release()
            # 需要python >= 2.5
            with lock:
                sys.stdout.write('%s saveing block...' % self.getName())
                # 设置文件对象偏移地址
                self.fobj.seek(offset)
                # 写入获取到的数据
                self.fobj.write(block)
                offset = offset + len(block)
                sys.stdout.write('done.\n')

def main(url, thread=3, save_file='', buffer=1024):
    # 最大线程数量不能超过max_thread
    thread = thread if thread <= max_thread else max_thread
    # 获取文件的大小
    req = urllib2.urlopen(url)
    size = int(req.info().getheaders('Content-Length')[0])
    # 初始化文件对象
    fobj = open(save_file, 'wb')
    # 根据线程数量计算 每个线程负责的http Range 大小
    avg_size, pad_size = divmod(size, thread)
    plist = []
    for i in xrange(thread):
        start_size = i*avg_size
        end_size = start_size + avg_size - 1
        if i == thread - 1:
            # 最后一个线程加上pad_size
            end_size = end_size + pad_size + 1
        t = Downloader(url, start_size, end_size, fobj, buffer)
        plist.append(t)
    #  开始搬砖
    for t in plist:
        t.start()
    # 等待所有线程结束
    for t in plist:
        t.join()
    # 结束当然记得关闭文件对象
    fobj.close()
    print 'Download completed!'
if __name__ == '__main__':
    url = 'http://192.168.1.2:8082/downloads/10M.zip'
    main(url=url, thread=10, save_file='test.iso', buffer=4096)
Python 相关文章推荐
Python collections模块实例讲解
Apr 07 Python
详解python时间模块中的datetime模块
Jan 13 Python
深入学习Python中的上下文管理器与else块
Aug 27 Python
PyCharm在win10的64位系统安装实例
Nov 26 Python
Python 比较文本相似性的方法(difflib,Levenshtein)
Oct 15 Python
django使用LDAP验证的方法示例
Dec 10 Python
Python中GeoJson和bokeh-1的使用讲解
Jan 03 Python
Python使用pydub库对mp3与wav格式进行互转的方法
Jan 10 Python
简单了解Python3里的一些新特性
Jul 13 Python
借助Paramiko通过Python实现linux远程登陆及sftp的操作
Mar 16 Python
python如何写try语句
Jul 14 Python
win10+anaconda安装yolov5的方法及问题解决方案
Apr 29 Python
python正则匹配查询港澳通行证办理进度示例分享
Dec 27 #Python
python模拟登录百度代码分享(获取百度贴吧等级)
Dec 27 #Python
python读文件逐行处理的示例代码分享
Dec 27 #Python
python调用cmd复制文件代码分享
Dec 27 #Python
win7安装python生成随机数代码分享
Dec 27 #Python
python正则匹配抓取豆瓣电影链接和评论代码分享
Dec 27 #Python
python正则表达式去掉数字中的逗号(python正则匹配逗号)
Dec 25 #Python
You might like
PHP中实现中文字符进制转换原理分析
2011/12/06 PHP
浅谈php中urlencode与rawurlencode的区别
2016/09/05 PHP
php常用正则函数实例小结
2016/12/29 PHP
php正则判断是否为合法身份证号的方法
2017/03/16 PHP
PHP实现可精确验证身份证号码的工具类示例
2018/05/31 PHP
从零开始学习jQuery (二) 万能的选择器
2010/10/01 Javascript
js变换显示图片的实例
2013/04/16 Javascript
js setTimeout 常见问题小结
2013/08/13 Javascript
使用js如何实现全选与全不选
2013/12/30 Javascript
JavaScript实现基于十进制的四舍五入实例
2015/07/17 Javascript
分享一个原生的JavaScript拖动方法
2016/09/25 Javascript
详解js的异步编程技术的方法
2017/02/09 Javascript
JS实现图片预览的两种方式
2017/06/27 Javascript
vue中的数据绑定原理的实现
2018/07/02 Javascript
Vue组件之单向数据流的解决方法
2018/11/10 Javascript
浅谈发布订阅模式与观察者模式
2019/04/09 Javascript
jQuery操作attr、prop、val()/text()/html()、class属性
2019/05/23 jQuery
js getBoundingClientRect使用方法详解
2019/07/17 Javascript
express中static中间件的具体使用方法
2019/10/17 Javascript
解决Django模板无法使用perms变量问题的方法
2017/09/10 Python
深入浅出学习python装饰器
2017/09/29 Python
python 使用值来排序一个字典的方法
2018/11/16 Python
解决pycharm回车之后不能换行或不能缩进的问题
2019/01/16 Python
Python中函数的基本定义与调用及内置函数详解
2019/05/13 Python
Python 实现将数组/矩阵转换成Image类
2020/01/09 Python
python产生模拟数据faker库的使用详解
2020/11/04 Python
Carter’s官方旗舰店:美国受欢迎的婴童服装品牌
2018/01/21 全球购物
德国富尔达运动鞋店:43einhalb
2020/12/25 全球购物
有关打架的检讨书
2014/01/25 职场文书
俄语专业职业生涯规划
2014/02/26 职场文书
《放飞蜻蜓》教学反思
2014/04/27 职场文书
学生喝酒检讨书500字
2014/11/02 职场文书
答谢词范文
2015/01/05 职场文书
院系推荐意见
2015/06/05 职场文书
记一次Mysql不走日期字段索引的原因小结
2021/10/24 MySQL
CentOS7 minimal 最小化安装网络设置过程
2022/12/24 Servers