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实现sublime3的less编译插件示例
Apr 27 Python
python自动化测试之setUp与tearDown实例
Sep 28 Python
Python脚本实现代码行数统计代码分享
Mar 10 Python
python读写json文件的简单实现
Apr 11 Python
Python实现破解12306图片验证码的方法分析
Dec 29 Python
Python3 使用cookiejar管理cookie的方法
Dec 28 Python
python实现名片管理器的示例代码
Dec 17 Python
django实现HttpResponse返回json数据为中文
Mar 27 Python
详解用Python爬虫获取百度企业信用中企业基本信息
Jul 02 Python
如何基于matlab相机标定导出xml文件
Nov 02 Python
Django REST Framework 分页(Pagination)详解
Nov 30 Python
python分分钟绘制精美地图海报
Feb 15 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
浅谈php7的重大新特性
2015/10/23 PHP
PHP PDOStatement::bindValue讲解
2019/01/30 PHP
Javascript表格翻页效果实现思路及代码
2013/08/23 Javascript
使用Jquery获取带特殊符号的ID 标签的方法
2014/04/30 Javascript
jQuery提交多个表单的小技巧
2014/07/27 Javascript
JavaScript阻止事件冒泡示例分享
2014/12/28 Javascript
JavaScript判断字符长度、数字、Email、电话等常用判断函数分享
2015/04/01 Javascript
JavaScript中判断数据类型的方法总结
2016/05/24 Javascript
JS判断日期格式是否合法的简单实例
2016/07/11 Javascript
JAVA Web实时消息后台服务器推送技术---GoEasy
2016/11/04 Javascript
javascript实现简易计算器
2017/02/01 Javascript
JavaScript中在光标处插入添加文本标签节点的详细方法
2017/03/22 Javascript
JS检测是否可以访问公网服务器功能代码
2017/06/19 Javascript
简单的Vue SSR的示例代码
2018/01/12 Javascript
vue生成token并保存到本地存储中
2018/07/17 Javascript
element-ui 关于获取select 的label值方法
2018/08/24 Javascript
Vue引用Swiper4插件无法重写分页器样式的解决方法
2018/09/27 Javascript
简单两步使用node发送qq邮件的方法
2019/03/01 Javascript
解决vue使用vant下拉框van-dropdown-item 绑定title值不变问题
2020/08/05 Javascript
[05:28]刀塔密之一:团结则存
2014/07/03 DOTA
Python面向对象特殊成员
2017/04/24 Python
Python使用Windows API创建窗口示例【基于win32gui模块】
2018/05/09 Python
Python格式化字符串f-string概览(小结)
2019/06/18 Python
PyTorch 普通卷积和空洞卷积实例
2020/01/07 Python
pytorch下大型数据集(大型图片)的导入方式
2020/01/08 Python
解决使用python print打印函数返回值多一个None的问题
2020/04/09 Python
css3实现3D色子翻转特效
2014/12/23 HTML / CSS
20佳惊艳的HTML5应用程序示例分享
2011/05/03 HTML / CSS
华为消费者德国官方网站:HUAWEI德国
2020/11/03 全球购物
好人好事演讲稿
2014/09/01 职场文书
2014年林业工作总结
2014/12/05 职场文书
2015年暑假工作总结
2015/07/13 职场文书
小学语文教师研修日志
2015/11/13 职场文书
市级三好生竞选稿
2015/11/21 职场文书
党员廉政准则心得体会
2016/01/20 职场文书
Python中的datetime包与time包包和模块详情
2022/02/28 Python