Python 分塊多執行緒下載器
阿新 • • 發佈:2019-02-05
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:
- 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()