1. 程式人生 > >Python 分塊多執行緒下載器

Python 分塊多執行緒下載器

BlocksDownload

將通過 HTTP 協議傳輸的檔案進行分塊,並用多執行緒下載,充分利用本地頻寬。

說明

  • Python 2.7, no third-party library
  • 每個執行緒對應一個 http 連線
  • max_block_size 越大記憶體佔用越大,影響資料能否儘早寫入磁碟而不是停留在記憶體裡。單個下載塊太大會出 MemoryError
  • 經過測試:壓縮檔案,視訊檔案,音訊檔案沒問題,但有些網站的安裝包無法開啟,什麼”缺少端對端驗證”
  • 目前網上大多電影都是通過 p2p 方式分享的,所以這個程式可能並沒有太大的作用
  • 優先根據指定的 threading 數量設定執行緒數目,不指定的話將會根據 max_block_size 大小計算合適的執行緒個數。
  • 利用 Event 事件實現程序同步。
  • 請謹慎使用test,因為會在下載會破壞性覆蓋同名檔案。
    下載陰陽師apk提速效果還是很明顯的。

downloader:

def __init__(self, url, download_to, max_block_size=1024*1024*5, thread_num=0):
* url:待下載檔案連結
* download_to:存放下載檔案的路徑
* max_block_size:可能出現的最大的下載塊大小, 單位 Byte
* thread_num: 制定下載執行緒個數,預設會根據 max_block_size 自動計算
> thread_num 的自定義會導致根據 max_block_size 計算失效

適用於:

  • 通過 http 協議傳輸的大型檔案(>200MB)
  • 伺服器端未對單個主機的連結個數進行限制或者限制已知。

Update Note:

  1. 2017-04-07
    實現了分塊多執行緒下載的功能,但要構建一個健壯的下載器,還有很多細節需要考慮,需要更多包裝。
    比如:(1)提供 FTP 協議的相容,(2)更人性化的使用方法包裝
# coding:utf-8

#-----Python 3 Compatible
from __future__ import absolute_import
from __future__ import division
from
__future__ import print_function from __future__ import unicode_literals #--------------------------------- import urllib import urllib2 import threading import time import datetime class downloader: def __init__(self, url, download_to, max_block_size=1024*1024*5, thread_num=0): self.url = url self.name = download_to req = urllib2.Request(self.url) response = urllib2.urlopen(req) file_size = response.headers.getheader('Content-Length') self.total = int(file_size) # 根據要求或者塊大小計算執行緒個數 if thread_num: self.thread_num = thread_num else: self.thread_num = (self.total+max_block_size-1)//max_block_size print(self.thread_num) self.event_list = [threading.Event() for _ in range(self.thread_num)] self.event_list[0].set() print('File size is %d KB'%(self.total/1024)) # 劃分每個下載塊的範圍 def get_range(self): ranges=[] offset = int(self.total/self.thread_num) for i in range(self.thread_num): if i == self.thread_num-1: ranges.append((i*offset,'')) else: ranges.append((i*offset,(i+1)*offset)) return ranges def download(self,start,end, event_num): post_data = {'Range':'Bytes=%s-%s' % (start,end),'Accept-Encoding':'*'} # headers = urllib.urlencode(post_data) req = urllib2.Request(self.url, headers=post_data) res = urllib2.urlopen(req) # res = requests.get(self.url,headers=headers) print('%s:%s chunk starts to download'%(start,end)) self.event_list[event_num].wait() self.fd.seek(start) self.fd.write(res.read()) print("Number[%d] block was written"%event_num) if event_num<len(self.event_list)-1: self.event_list[event_num+1].set() def run(self): self.fd = open(self.name,'ab') thread_list = [] n = 0 for ran in self.get_range(): start,end = ran print('thread %d Range:%s ~ %s Bytes'%(n, start, end)) thread = threading.Thread(target=self.download, args=(start,end,n)) thread.start() thread_list.append(thread) n += 1 map(lambda thd:thd.join(), thread_list) print('download %s load success'%(self.name)) self.fd.close()