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 相关文章推荐
Django的session中对于用户验证的支持
Jul 23 Python
Python3.4 tkinter,PIL图片转换
Jun 21 Python
python查找指定文件夹下所有文件并按修改时间倒序排列的方法
Oct 21 Python
python3 实现验证码图片切割的方法
Dec 07 Python
Python matplotlib画图与中文设置操作实例分析
Apr 23 Python
对Python的交互模式和直接运行.py文件的区别详解
Jun 29 Python
基于python3 pyQt5 QtDesignner实现窗口化猜数字游戏功能
Jul 15 Python
python Pandas如何对数据集随机抽样
Jul 29 Python
Python for i in range ()用法详解
Sep 18 Python
python实现凯撒密码、凯撒加解密算法
Jun 11 Python
Python logging日志模块 配置文件方式
Jul 12 Python
Python接收手机短信的代码整理
Aug 02 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&amp;MYSQL留言板源码
2020/07/19 PHP
PHP闭包函数详解
2016/02/13 PHP
PHP实现的MD5结合RSA签名算法实例
2017/10/07 PHP
PHP实现动态删除XML数据的方法示例
2018/03/30 PHP
PHP重载基础知识回顾
2020/09/10 PHP
javascript之函数直接量(function(){})()
2007/06/29 Javascript
javascript appendChild,innerHTML,join性能比较代码
2009/08/29 Javascript
javascript面向对象之二 命名空间
2011/02/08 Javascript
在标题栏显示新消息提示,很多公司项目中用到这个方法
2011/11/04 Javascript
JQuery的Ajax请求实现局部刷新的简单实例
2014/02/11 Javascript
js插件设置innerHTML时在IE8下提示“未知运行时错误”解决方法
2015/04/25 Javascript
BootStrap文件上传样式超好看【持续更新】
2016/05/10 Javascript
很棒的js选项卡切换效果
2016/07/15 Javascript
Vue键盘事件用法总结
2017/04/18 Javascript
jQuery 实现图片的依次加载图片功能
2017/07/06 jQuery
安装vue-cli报错 -4058 的解决方法
2017/10/19 Javascript
详解webpack-dev-server的简单使用
2018/04/02 Javascript
用POSTMAN发送JSON格式的POST请求示例
2018/09/04 Javascript
electron实现qq快捷登录的方法示例
2018/10/22 Javascript
Vue动态创建注册component的实例代码
2019/06/14 Javascript
小程序按钮避免多次调用接口和点击方案实现(不用showLoading)
2020/04/15 Javascript
解决vant中 tab栏遇到的坑 van-tabs
2020/11/04 Javascript
python定时检查启动某个exe程序适合检测exe是否挂了
2013/01/21 Python
Python中处理字符串之endswith()方法的使用简介
2015/05/18 Python
Django之模型层多表操作的实现
2019/01/08 Python
python tkinter实现界面切换的示例代码
2019/06/14 Python
Python2与Python3的区别详解
2020/02/09 Python
FOREO官方网站:LUNA露娜洁面仪
2016/11/28 全球购物
字符串str除首尾字符外的其他字符按升序排列
2013/03/08 面试题
研究生毕业自我鉴定范文
2014/03/27 职场文书
银行竞聘报告范文
2014/11/06 职场文书
未婚证明范本
2015/06/15 职场文书
党员发展大会主持词
2015/07/03 职场文书
运动会广播稿200字
2015/08/19 职场文书
2019最新版劳务派遣管理制度
2019/08/16 职场文书
解决MySQL存储时间出现不一致的问题
2021/04/28 MySQL