python多进程断点续传分片下载器

标签:python 下载器 多进程


因为爬虫要用到下载器,但是直接用urllib下载很慢,所以找了很久终于找到一个让我欣喜的下载器。他能够断点续传分片下载,极大提高下载速度。

  1. #! /usr/bin/env python
  2. # encoding=utf-8
  3. from __future__ import unicode_literals
  4. from multiprocessing.dummy import Pool as ThreadPool
  5. import threading
  6. import os
  7. import sys
  8. import cPickle
  9. from collections import namedtuple
  10. import urllib2
  11. from urlparse import urlsplit
  12. import time
  13. # global lock
  14. lock = threading.Lock()
  15. # default parameters
  16. defaults = dict(
  17. thread_count=10,
  18. buffer_size=500 * 1024,
  19. block_size=1000 * 1024)
  20. def progress(percent, width=50):
  21. print "%s %d%%\r" % (('%%-%ds' % width) % (width * percent / 100 * '='), percent),
  22. if percent >= 100:
  23. print
  24. sys.stdout.flush()
  25. def write_data(filepath, data):
  26. with open(filepath, 'wb') as output:
  27. cPickle.dump(data, output)
  28. def read_data(filepath):
  29. with open(filepath, 'rb') as output:
  30. return cPickle.load(output)
  31. FileInfo = namedtuple('FileInfo', 'url name size lastmodified')
  32. def get_file_info(url):
  33. class HeadRequest(urllib2.Request):
  34. def get_method(self):
  35. return "HEAD"
  36. res = urllib2.urlopen(HeadRequest(url))
  37. res.read()
  38. headers = dict(res.headers)
  39. size = int(headers.get('content-length', 0))
  40. lastmodified = headers.get('last-modified', '')
  41. name = None
  42. if headers.has_key('content-disposition'):
  43. name = headers['content-disposition'].split('filename=')[1]
  44. if name[0] == '"' or name[0] == "'":
  45. name = name[1:-1]
  46. else:
  47. name = os.path.basename(urlsplit(url)[2])
  48. return FileInfo(url, name, size, lastmodified)
  49. def download(url, output,
  50. thread_count=defaults['thread_count'],
  51. buffer_size=defaults['buffer_size'],
  52. block_size=defaults['block_size']):
  53. # get latest file info
  54. file_info = get_file_info(url)
  55. # init path
  56. if output is None:
  57. output = file_info.name
  58. workpath = '%s.ing' % output
  59. infopath = '%s.inf' % output
  60. # split file to blocks. every block is a array [start, offset, end],
  61. # then each greenlet download filepart according to a block, and
  62. # update the block' offset.
  63. blocks = []
  64. if os.path.exists(infopath):
  65. # load blocks
  66. _x, blocks = read_data(infopath)
  67. if (_x.url != url or
  68. _x.name != file_info.name or
  69. _x.lastmodified != file_info.lastmodified):
  70. blocks = []
  71. if len(blocks) == 0:
  72. # set blocks
  73. if block_size > file_info.size:
  74. blocks = [[0, 0, file_info.size]]
  75. else:
  76. block_count, remain = divmod(file_info.size, block_size)
  77. blocks = [[i * block_size, i * block_size,
  78. (i + 1) * block_size - 1] for i in range(block_count)]
  79. blocks[-1][-1] += remain
  80. # create new blank workpath
  81. with open(workpath, 'wb') as fobj:
  82. fobj.write('')
  83. print 'Downloading %s' % url
  84. # start monitor
  85. threading.Thread(target=_monitor, args=(
  86. infopath, file_info, blocks)).start()
  87. # start downloading
  88. with open(workpath, 'rb+') as fobj:
  89. args = [(url, blocks[i], fobj, buffer_size)
  90. for i in range(len(blocks)) if blocks[i][1] < blocks[i][2]]
  91. if thread_count > len(args):
  92. thread_count = len(args)
  93. pool = ThreadPool(thread_count)
  94. pool.map(_worker, args)
  95. pool.close()
  96. pool.join()
  97. # rename workpath to output
  98. if os.path.exists(output):
  99. os.remove(output)
  100. os.rename(workpath, output)
  101. # delete infopath
  102. if os.path.exists(infopath):
  103. os.remove(infopath)
  104. assert all([block[1] >= block[2] for block in blocks]) is True
  105. def _worker((url, block, fobj, buffer_size)):
  106. req = urllib2.Request(url)
  107. req.headers['Range'] = 'bytes=%s-%s' % (block[1], block[2])
  108. res = urllib2.urlopen(req)
  109. while 1:
  110. chunk = res.read(buffer_size)
  111. if not chunk:
  112. break
  113. with lock:
  114. fobj.seek(block[1])
  115. fobj.write(chunk)
  116. block[1] += len(chunk)
  117. def _monitor(infopath, file_info, blocks):
  118. while 1:
  119. with lock:
  120. percent = sum([block[1] - block[0]
  121. for block in blocks]) * 100 / file_info.size
  122. progress(percent)
  123. if percent >= 100:
  124. break
  125. write_data(infopath, (file_info, blocks))
  126. time.sleep(2)
  127. if __name__ == '__main__':
  128. import argparse
  129. parser = argparse.ArgumentParser(description='多线程文件下载器.')
  130. parser.add_argument('url', type=str, help='下载连接')
  131. parser.add_argument('-o', type=str, default=None,
  132. dest="output", help='输出文件')
  133. parser.add_argument(
  134. '-t', type=int, default=defaults['thread_count'], dest="thread_count", help='下载的线程数量')
  135. parser.add_argument(
  136. '-b', type=int, default=defaults['buffer_size'], dest="buffer_size", help='缓存大小')
  137. parser.add_argument(
  138. '-s', type=int, default=defaults['block_size'], dest="block_size", help='字区大小')
  139. argv = sys.argv[1:]
  140. if len(argv) == 0:
  141. argv = ['https://eyes.nasa.gov/eyesproduct/EYES/os/win']
  142. args = parser.parse_args(argv)
  143. start_time = time.time()
  144. download(args.url, args.output, args.thread_count,
  145. args.buffer_size, args.block_size)
  146. print '下载时间: %ds' % int(time.time() - start_time)

python多进程断点续传分片下载器的更多相关文章

  1. Python实现多线程HTTP下载器

    本文将介绍使用Python编写多线程HTTP下载器,并生成.exe可执行文件. 环境:windows/Linux + Python2.7.x 单线程 在介绍多线程之前首先介绍单线程.编写单线程的思路为 ...

  2. 用python做youtube自动化下载器 代码

    目录 项目地址 思路 流程 1. post i. 先把post中的headers格式化 ii.然后把参数也格式化 iii. 最后再执行requests库的post请求 iv. 封装成一个函数 2. 调 ...

  3. python的内置下载器

    python有个内置下载器,有时候在内部提供文件下载很好用. 进入提供下载的目录 # ls abc.aaa chpw.py finance.py lsdir.py ping.py u2d-partia ...

  4. Qt+Python开发百度图片下载器

    一.资源下载地址 https://www.aliyundrive.com/s/jBU2wBS8poH 本项目路径:项目->收费->百度图片下载器(可试用5分钟) 安装包直接下载地址:htt ...

  5. 用python做youtube自动化下载器 思路

    目录 0. 思路 1.准备 i.savfrom.net 2. 探索并规划获取方式 i.总览 ii. 获取该网页取到下载url的请求 iii. 在本地获取请求 iv.解析请求结果 v.解析解密后的结果 ...

  6. Python 多进程 一分钟下载二百张图片 是什么样子的体验

    需要爬取国内某个网站,但是这个网站封ip,没办法,只能用代理了,然后构建自己的代理池,代理池维护了20条进程, 所用的网络是20M带宽,实际的网速能达到2.5M,考虑到其他原因,网速未必能达到那么多. ...

  7. Android通用简洁的下载器

    下载逻辑在android开发中可谓很常见,那么封装一个通用简洁的下载器时很有必要的.如果不想给工程引入一个很重的jar包那么可以直接复用下面的代码即可. 主要对外接口 构造函数 :     publi ...

  8. 基于iOS 10、realm封装的下载器

    代码地址如下:http://www.demodashi.com/demo/11653.html 概要 在决定自己封装一个下载器前,我本以为没有那么复杂,可在实际开发过程中困难重重,再加上iOS10和X ...

  9. 用python实现的百度音乐下载器-python-pyqt-改进版

    之前写过一个用python实现的百度新歌榜.热歌榜下载器的博文,实现了百度新歌.热门歌曲的爬取与下载.但那个采用的是单线程,网络状况一般的情况下,扫描前100首歌的时间大概得到40来秒.而且用Pyqt ...

随机推荐

  1. Ubuntu-Java-Scala-Spark-IEDA-configure

    最近要接触数据分析,需要快速入门,就想在Ubuntu下配置IDEA和Spark编程环境. 1.下载jdk #java /etc/profile .zshrc, 或者直接在终端输入export JAVA ...

  2. ubuntu 终端只显示当前目录名称

    修改.bashrc文件: 原来: #修改终端提示颜色 color_prompt=yes if [ "$color_prompt" = yes ]; then PS1='${debi ...

  3. hdu Largest prime factor

    类似于素数打表. #include <cstdio> #include <cstring> #include <algorithm> #define maxn 10 ...

  4. 通过JCONSOLE监控TOMCAT的JVM使用情况

    这个也是要学入一下,JVMr 虚拟机原理不可少. 参考配置URL“: http://blog.163.com/kangle0925@126/blog/static/277581982011527723 ...

  5. Codeforces 161D Distance in Tree

    题目大意:给出一棵n个节点的树,统计树中长度为k的路径的条数(1<=n<=50000 , 1<=k<=500) 思路:树分治! #include<cstdio> # ...

  6. LeetCode_Binary Tree Inorder Traversal

    Given a binary tree, return the inorder traversal of its nodes' values. For example: Given binary tr ...

  7. How to install phpmyadmin on centos 6

    Phpmyadmin :   Phpmyadmin is a free tool used to administrate MySQL . Phpmyadmin supports all major ...

  8. [VBA]发布一个计算桩号之差的Excel自定义函数(VBA)

    这是一个可以计算桩号之差(也就是得到长度)的Excel(或WPS)扩展函数,可以减少工程师在统计工程量时的工作量. 该函数具有一定的通用性.可以在MS Office和金山WPS上使用. 文末会给出使用 ...

  9. java开发经验分享(四)

    四. 关于测试 1. 在整个项目计划中,测试时间安排的合理性,对测试阶段的情况应作充分预计,不可为了赶发布点而忽略质量. 2. 务必清楚产品包.更新包.bug包的提交规范.具体请参照<开发规范手 ...

  10. MVC 校验

    校验保障了MVC 应用程序安全性. Models 文件夹包含表示应用程序模型的类 1,创建一个项目MvcValidateDemo. 2,创建一个实体类UserInfo在Models中,包含Id.Use ...