Python实现多线程HTTP下载器示例


Posted in Python onFebruary 11, 2017

本文将介绍使用Python编写多线程HTTP下载器,并生成.exe可执行文件。

环境:windows/Linux + Python2.7.x

单线程

在介绍多线程之前首先介绍单线程。编写单线程的思路为:

1.解析url;

2.连接web服务器;

3.构造http请求包;

4.下载文件。

接下来通过代码进行说明。

解析url

通过用户输入url进行解析。如果解析的路径为空,则赋值为'/';如果端口号为空,则赋值为"80”;下载文件的文件名可根据用户的意愿进行更改(输入'y'表示更改,输入其它表示不需要更改)。

下面列出几个解析函数:

#解析host和path
def analyHostAndPath(totalUrl):
  protocol,s1 = urllib.splittype(totalUrl)
  host, path = urllib.splithost(s1)
  if path == '':
    path = '/'
  return host, path

#解析port
def analysisPort(host):
  host, port = urllib.splitport(host)
  if port is None:
    return 80
  return port

#解析filename
def analysisFilename(path):
  filename = path.split('/')[-1]
  if '.' not in filename:
    return None
  return filename

连接web服务器

使用socket模块,根据解析url得到的host和port连接web服务器,代码如下:

import socket
from analysisUrl import port,host

ip = socket.gethostbyname(host)
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((ip, port))

print "success connected webServer!!"

构造http请求包

根据解析url得到的path, host, port构造一个HTTP请求包。 

from analysisUrl import path, host, port

packet = 'GET ' + path + ' HTTP/1.1\r\nHost: ' + host + '\r\n\r\n' 

下载文件

根据构造的http请求包,向服务器发送文件,抓取响应报文头部的"Content-Length"。 

def getLength(self):
    s.send(packet)
    print "send success!"
    buf = s.recv(1024)
    print buf
    p = re.compile(r'Content-Length: (\d*)')
    length = int(p.findall(buf)[0])
    return length, buf

下载文件并计算下载所用的时间。 

def download(self):
    file = open(self.filename,'wb')
    length,buf = self.getLength()
    packetIndex = buf.index('\r\n\r\n')
    buf = buf[packetIndex+4:]
    file.write(buf)
    sum = len(buf)
    while 1:
      buf = s.recv(1024)
      file.write(buf)
      sum = sum + len(buf)
      if sum >= length:
        break
    print "Success!!"

if __name__ == "__main__":
  start = time.time()
  down = downloader()
  down.download()
  end = time.time()
  print "The time spent on this program is %f s"%(end - start)

多线程

抓取响应报文头部的"Content-Length"字段,结合线程个数,加锁分段下载。与单线程的不同,这里将所有代码整合为一个文件,代码中使用更多的Python自带模块。

得到"Content-Length":

def getLength(self):
    opener = urllib2.build_opener()
    req = opener.open(self.url)
    meta = req.info()
    length = int(meta.getheaders("Content-Length")[0])
    return length

根据得到的Length,结合线程个数划分范围:

def get_range(self):
    ranges = []
    length = self.getLength()
    offset = int(int(length) / self.threadNum)
    for i in range(self.threadNum):
      if i == (self.threadNum - 1):
        ranges.append((i*offset,''))
      else:
        ranges.append((i*offset,(i+1)*offset))
    return ranges

实现多线程下载,在向文件写入内容时,向线程加锁,并使用with lock代替lock.acquire( )...lock.release( );使用file.seek( )设置文件偏移地址,保证写入文件的准确性。

def downloadThread(self,start,end):
    req = urllib2.Request(self.url)
    req.headers['Range'] = 'bytes=%s-%s' % (start, end)
    f = urllib2.urlopen(req)
    offset = start
    buffer = 1024
    while 1:
      block = f.read(buffer)
      if not block:
        break
      with lock:
        self.file.seek(offset)
        self.file.write(block)
        offset = offset + len(block)

  def download(self):
    filename = self.getFilename()
    self.file = open(filename, 'wb')
    thread_list = []
    n = 1
    for ran in self.get_range():
      start, end = ran
      print 'starting:%d thread '% n
      n += 1
      thread = threading.Thread(target=self.downloadThread,args=(start,end))
      thread.start()
      thread_list.append(thread)

    for i in thread_list:
      i.join()
    print 'Download %s Success!'%(self.file)
    self.file.close()

运行结果:

Python实现多线程HTTP下载器示例

将(*.py)文件转化为(*.exe)可执行文件

当写好了一个工具,如何让那些没有安装Python的人使用这个工具呢?这就需要将.py文件转化为.exe文件。

这里用到Python的py2exe模块,初次使用,所以对其进行介绍:

py2exe是一个将Python脚本转换成windows上可独立执行的可执行文件(*.exe)的工具,这样,就可以不用装Python在windows上运行这个可执行程序。

接下来,在multiThreadDownload.py的同目录下,创建mysetup.py文件,编写:

from distutils.core import setup
import py2exe

setup(console=["multiThreadDownload.py"])

接着执行命令:Python mysetup.py py2exe

生成dist文件夹,multiTjhreadDownload.exe文件位于其中,点击运行即可:

Python实现多线程HTTP下载器示例

Python实现多线程HTTP下载器示例

demo下载地址:HttpFileDownload_3water.rar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
编写自定义的Django模板加载器的简单示例
Jul 21 Python
基于Pandas读取csv文件Error的总结
Jun 15 Python
深入理解Django自定义信号(signals)
Oct 15 Python
运用Python的webbrowser实现定时打开特定网页
Feb 21 Python
使用python进行波形及频谱绘制的方法
Jun 17 Python
PyTorch实现更新部分网络,其他不更新
Dec 31 Python
python爬虫基础知识点整理
Jun 02 Python
Selenium webdriver添加cookie实现过程详解
Aug 12 Python
Pytorch 扩展Tensor维度、压缩Tensor维度的方法
Sep 09 Python
python 如何调用 dubbo 接口
Sep 24 Python
python opencv旋转图片的使用方法
Jun 04 Python
Python自动化测试PO模型封装过程详解
Jun 22 Python
Python  pip安装lxml出错的问题解决办法
Feb 10 #Python
使用Python脚本和ADB命令实现卸载App
Feb 10 #Python
Python中str is not callable问题详解及解决办法
Feb 10 #Python
python用Pygal如何生成漂亮的SVG图像详解
Feb 10 #Python
Ubuntu下创建虚拟独立的Python环境全过程
Feb 10 #Python
简单谈谈Python中的几种常见的数据类型
Feb 10 #Python
Python使用QQ邮箱发送Email的方法实例
Feb 09 #Python
You might like
Zerg基本策略
2020/03/14 星际争霸
深入理解PHP原理之错误抑制与内嵌HTML分析
2011/05/02 PHP
PHP代码维护,重构变困难的4种原因分析
2016/01/25 PHP
PHP中用Trait封装单例模式的实现
2019/12/18 PHP
PHP强制转化的形式整理
2020/05/22 PHP
jquery创建并行对象或者合并对象的实现代码
2012/10/10 Javascript
Document.location.href和.replace的区别示例介绍
2014/03/04 Javascript
JS对img标签进行优化使用onerror显示默认图像
2014/04/24 Javascript
通过$(this)使用jQuery包装后的方法或属性
2014/05/18 Javascript
jquery使用slideDown实现模块缓慢拉出效果的方法
2015/03/27 Javascript
jQuery技巧之让任何组件都支持类似DOM的事件管理
2016/04/05 Javascript
JavaScript中数组去除重复的三种方法
2016/04/22 Javascript
jQuery实现页面下拉100像素出现悬浮窗口的方法
2016/09/05 Javascript
ReactNative之键盘Keyboard的弹出与消失示例
2017/07/11 Javascript
react学习笔记之state以及setState的使用
2017/12/07 Javascript
React Native中NavigatorIOS组件的简单使用详解
2018/01/27 Javascript
浅谈es6中export和export default的作用及区别
2018/02/07 Javascript
JavaScript判断日期时间差的实例代码
2018/03/01 Javascript
vue 下列表侧滑操作实例代码详解
2018/07/24 Javascript
在iFrame子页面里实现模态框的方法
2018/08/17 Javascript
JS选取DOM元素常见操作方法实例分析
2018/12/10 Javascript
vue iview实现动态新增和删除
2020/06/17 Javascript
Python基于递归算法实现的走迷宫问题
2017/08/04 Python
python 实现对文件夹内的文件排序编号
2018/04/12 Python
python 使用 requests 模块发送http请求 的方法
2018/12/09 Python
Python常用特殊方法实例总结
2019/03/22 Python
python3.x中安装web.py步骤方法
2020/06/23 Python
Python依赖包迁移到断网环境操作
2020/07/13 Python
Node.js 和 Python之间该选择哪个?
2020/08/05 Python
python 实时调取摄像头的示例代码
2020/11/25 Python
selenium+headless chrome爬虫的实现示例
2021/01/08 Python
CSS3 重置iphone浏览器按钮input,select等表单元素的默认样式
2014/10/11 HTML / CSS
解析HTML5中的新功能本地存储localStorage
2016/03/01 HTML / CSS
澳大利亚手表品牌:Time IV Change
2018/10/06 全球购物
合作协议书怎么写
2014/04/18 职场文书
小学五年级学生评语
2014/04/22 职场文书