2012-12-20 51 views
12

tôi sử dụng Python yêu cầu thư viện để tải về một tập tin lớn, ví dụ .:Các yêu cầu với nhiều kết nối

r = requests.get("http://bigfile.com/bigfile.bin") 
content = r.content 

Các tập tin tải lớn tại + - 30 Kb mỗi giây, trong đó là một chút chậm. Mọi kết nối đến máy chủ bigfile đều bị điều chỉnh, vì vậy tôi muốn tạo nhiều kết nối.

Có cách nào để thực hiện nhiều kết nối cùng một lúc để tải xuống một tệp không?

Trả lời

17

Bạn có thể sử dụng tiêu đề HTTP Range để tìm nạp một phần của tệp (already covered for python here).

Chỉ cần bắt đầu một vài chủ đề và lấy phạm vi khác nhau với mỗi người và bạn đã hoàn tất;)

def download(url,start): 
    req = urllib2.Request('http://www.python.org/') 
    req.headers['Range'] = 'bytes=%s-%s' % (start, start+chunk_size) 
    f = urllib2.urlopen(req) 
    parts[start] = f.read() 

threads = [] 
parts = {} 

# Initialize threads 
for i in range(0,10): 
    t = threading.Thread(target=download, i*chunk_size) 
    t.start() 
    threads.append(t) 

# Join threads back (order doesn't matter, you just want them all) 
for i in threads: 
    i.join() 

# Sort parts and you're done 
result = '' 
for i in range(0,10): 
    result += parts[i*chunk_size] 

Cũng lưu ý rằng không phải mọi máy chủ hỗ trợ Range tiêu đề (và đặc biệt là các máy chủ với php scripts responsible for data fetching thường không thực hiện xử lý nó).

6

Dưới đây là một kịch bản Python rằng tiết kiệm url trao cho một tập tin và sử dụng nhiều chủ đề để tải về nó:

#!/usr/bin/env python 
import sys 
from functools import partial 
from itertools import count, izip 
from multiprocessing.dummy import Pool # use threads 
from urllib2 import HTTPError, Request, urlopen 

def download_chunk(url, byterange): 
    req = Request(url, headers=dict(Range='bytes=%d-%d' % byterange)) 
    try: 
     return urlopen(req).read() 
    except HTTPError as e: 
     return b'' if e.code == 416 else None # treat range error as EOF 
    except EnvironmentError: 
     return None 

def main(): 
    url, filename = sys.argv[1:] 
    pool = Pool(4) # define number of concurrent connections 
    chunksize = 1 << 16 
    ranges = izip(count(0, chunksize), count(chunksize - 1, chunksize)) 
    with open(filename, 'wb') as file: 
     for s in pool.imap(partial(download_part, url), ranges): 
      if not s: 
       break # error or EOF 
      file.write(s) 
      if len(s) != chunksize: 
       break # EOF (servers with no Range support end up here) 

if __name__ == "__main__": 
    main() 

Sự kết thúc của tập tin được phát hiện nếu một máy chủ trả về cơ thể trống rỗng, hoặc 416 mã HTTP, hoặc nếu kích thước phản hồi không chính xác là chunksize.

Hỗ trợ máy chủ không hiểu tiêu đề Range (mọi thứ được tải xuống trong một yêu cầu duy nhất trong trường hợp này; để hỗ trợ tệp lớn, thay đổi download_chunk() để lưu vào tệp tạm thời và trả về tên tệp để đọc trong chuỗi chính thay vì chính nội dung tệp).

Nó cho phép thay đổi số lượng độc lập các kết nối đồng thời (kích thước nhóm) và số byte được yêu cầu trong một yêu cầu http duy nhất.

Để sử dụng nhiều quy trình thay vì chủ đề, thay đổi khẩu:

from multiprocessing.pool import Pool # use processes (other code unchanged) 
1

Giải pháp này đòi hỏi các tiện ích Linux có tên là "aria2c", nhưng nó có lợi thế là dễ dàng nối lại tải.

Nó cũng giả định rằng tất cả các tệp bạn muốn tải xuống được liệt kê trong danh sách thư mục http cho vị trí MY_HTTP_LOC. Tôi đã thử nghiệm tập lệnh này trên một phiên bản của máy chủ http lighttpd/1.4.26. Tuy nhiên, bạn có thể dễ dàng sửa đổi tập lệnh này để nó hoạt động cho các thiết lập khác.

#!/usr/bin/python 

import os 
import urllib 
import re 
import subprocess 

MY_HTTP_LOC = "http://AAA.BBB.CCC.DDD/" 

# retrieve webpage source code 
f = urllib.urlopen(MY_HTTP_LOC) 
page = f.read() 
f.close 

# extract relevant URL segments from source code 
rgxp = '(\<td\ class="n"\>\<a\ href=")([0-9a-zA-Z\(\)\-\_\.]+)(")' 
results = re.findall(rgxp,str(page)) 
files = [] 
for match in results: 
    files.append(match[1]) 

# download (using aria2c) files 
for afile in files: 
    if os.path.exists(afile) and not os.path.exists(afile+'.aria2'): 
     print 'Skipping already-retrieved file: ' + afile 
    else: 
     print 'Downloading file: ' + afile   
     subprocess.Popen(["aria2c", "-x", "16", "-s", "20", MY_HTTP_LOC+str(afile)]).wait() 
+1

Nếu bạn thực sự chỉ cần tải 1 tập tin, sau đó chỉ cần đi đến một thiết bị đầu cuối trên linux (nếu bạn đang sử dụng Linux) và chạy 'aria2c -x 16 -s 20 '. Tôi thích giải pháp của tôi bởi vì bất cứ khi nào tôi có nhiều tệp lớn (hoặc thậm chí chỉ 1 tệp lớn) mà tôi cần tải xuống, tôi chỉ cần đưa chúng vào thư mục 'MY_HTTP_LOC' và sau đó chạy tập lệnh của tôi. – synaptik

Các vấn đề liên quan