2012-08-23 26 views
7

Tôi tải tập tin trên http và hiển thị tiến độ sử dụng urllib và đoạn mã sau - mà hoạt động tốt:Hủy việc tải về chậm trong python

import sys 
from urllib import urlretrieve 

urlretrieve('http://example.com/file.zip', '/tmp/localfile', reporthook=dlProgress) 

def dlProgress(count, blockSize, totalSize): 
    percent = int(count*blockSize*100/totalSize) 
    sys.stdout.write("\r" + "progress" + "...%d%%" % percent) 
    sys.stdout.flush() 

Bây giờ tôi cũng muốn khởi động lại tải nếu nó đang xảy ra quá chậm (nói ít hơn 1MB trong 15 giây). Làm thế nào tôi có thể đạt được điều này?

+1

Bạn có thể tăng ngoại lệ trong bản đăng ký của mình. – Tobold

+1

Vâng, việc tăng ngoại lệ có vẻ là cách phổ biến để ngừng tải xuống, từ một cái nhìn nhanh về Google. Nó không được đề cập trong tài liệu mặc dù, mà làm cho tôi lo lắng rằng nó có thể có hành vi bất ngờ. Ví dụ: có thể dữ liệu được tìm nạp bởi một chuỗi chuyên dụng và việc ném một ngoại lệ sẽ biến nó thành một đứa trẻ mồ côi và không thực sự dừng quá trình tải xuống. – Kevin

Trả lời

4

Điều này sẽ hiệu quả. Nó tính toán tốc độ tải xuống thực tế và hủy bỏ nếu nó quá thấp.

import sys 
from urllib import urlretrieve 
import time 

url = "http://www.python.org/ftp/python/2.7.3/Python-2.7.3.tgz" # 14.135.620 Byte 
startTime = time.time() 

class TooSlowException(Exception): 
    pass 

def convertBToMb(bytes): 
    """converts Bytes to Megabytes""" 
    bytes = float(bytes) 
    megabytes = bytes/1048576 
    return megabytes 


def dlProgress(count, blockSize, totalSize): 
    global startTime 

    alreadyLoaded = count*blockSize 
    timePassed = time.time() - startTime 
    transferRate = convertBToMb(alreadyLoaded)/timePassed # mbytes per second 
    transferRate *= 60 # mbytes per minute 

    percent = int(alreadyLoaded*100/totalSize) 
    sys.stdout.write("\r" + "progress" + "...%d%%" % percent) 
    sys.stdout.flush() 

    if transferRate < 4 and timePassed > 2: # download will be slow at the beginning, hence wait 2 seconds 
     print "\ndownload too slow! retrying..." 
     time.sleep(1) # let's not hammer the server 
     raise TooSlowException 

def main(): 
    try: 
     urlretrieve(url, '/tmp/localfile', reporthook=dlProgress) 

    except TooSlowException: 
     global startTime 
     startTime = time.time() 
     main() 

if __name__ == "__main__": 
    main() 
+0

Đẹp nhất, chỉ là những gì tôi cần, cảm ơn bạn. –

+0

Lưu ý rằng thao tác này sẽ chỉ hoạt động trong trường hợp kết nối chậm. Kết nối bị ngắt thông thường sẽ không hoạt động trừ khi bạn thêm thời gian chờ vào socket. Nếu không - OK! +1 –

3

Something như thế này:

class Timeout(Exception): 
    pass 

def try_one(func,t=3): 
    def timeout_handler(signum, frame): 
     raise Timeout() 

    old_handler = signal.signal(signal.SIGALRM, timeout_handler) 
    signal.alarm(t) # triger alarm in 3 seconds 

    try: 
     t1=time.clock() 
     func() 
     t2=time.clock() 

    except Timeout: 
     print('{} timed out after {} seconds'.format(func.__name__,t)) 
     return None 
    finally: 
     signal.signal(signal.SIGALRM, old_handler) 

    signal.alarm(0) 
    return t2-t1 

Cuộc gọi 'try_one' với func bạn muốn thời gian ra và thời gian để thời gian chờ:

try_one(downloader,15) 

OR, bạn có thể làm điều này:

import socket 
socket.setdefaulttimeout(15) 
+1

Đây là một giải pháp tốt nếu bạn đang tải xuống các tệp nhỏ có kích thước đã biết. Nếu bạn không biết kích thước trước thời hạn, bạn sẽ không biết bao nhiêu giây để chuyển đến 'try_one'. Và nếu bạn đang tải xuống một tệp 100MB, 'try_one (downloader, 1500)' sẽ không bỏ cuộc cho đến khi 1500 giây trôi qua. Tốt hơn là, nó sẽ thoát ngay sau khi nó tự tin rằng việc tải xuống sẽ không hoàn thành kịp thời. – Kevin

+0

Có, đã đồng ý. Cảm ơn các giải pháp nhưng tôi muốn hủy bỏ dựa trên ngưỡng thông lượng tối thiểu không cho dù tải xuống đã hoàn thành trong một thời gian chờ nhất định. –

+0

@HolyMackerel: Chỉ cần sửa đổi móc báo cáo của bạn để có Thời gian chờ khoảng 10 giây và kiểm tra tỷ lệ. Vấn đề là một tải xuống treo nơi 0 byte được xfered và hook báo cáo của bạn không bao giờ được gọi. –

0

HolyMackerel! Sử dụng các công cụ!

import urllib2, sys, socket, time, os 

def url_tester(url = "http://www.python.org/ftp/python/2.7.3/Python-2.7.3.tgz"): 
    file_name = url.split('/')[-1] 
    u = urllib2.urlopen(url,None,1)  # Note the timeout to urllib2... 
    file_size = int(u.info().getheaders("Content-Length")[0]) 
    print ("\nDownloading: {} Bytes: {:,}".format(file_name, file_size)) 

    with open(file_name, 'wb') as f:  
     file_size_dl = 0 
     block_sz = 1024*4 
     time_outs=0 
     while True:  
      try: 
       buffer = u.read(block_sz) 
      except socket.timeout: 
       if time_outs > 3: # file has not had activity in max seconds... 
        print "\n\n\nsorry -- try back later" 
        os.unlink(file_name) 
        raise 
       else:    # start counting time outs... 
        print "\nHmmm... little issue... I'll wait a couple of seconds" 
        time.sleep(3) 
        time_outs+=1 
        continue 

      if not buffer: # end of the download    
       sys.stdout.write('\rDone!'+' '*len(status)+'\n\n') 
       sys.stdout.flush() 
       break 

      file_size_dl += len(buffer) 
      f.write(buffer) 
      status = '{:20,} Bytes [{:.2%}] received'.format(file_size_dl, 
              file_size_dl * 1.0/file_size) 
      sys.stdout.write('\r'+status) 
      sys.stdout.flush() 

    return file_name 

Điều này sẽ in trạng thái như mong đợi. Nếu tôi tháo cáp ethernet của tôi, tôi nhận được:

Downloading: Python-2.7.3.tgz Bytes: 14,135,620 
      827,392 Bytes [5.85%] received 


sorry -- try back later 

Nếu tôi tháo cáp, sau đó cắm nó trở lại trong vòng chưa đầy 12 giây, tôi nhận được:

Downloading: Python-2.7.3.tgz Bytes: 14,135,620 
      716,800 Bytes [5.07%] received 
Hmmm... little issue... I'll wait a couple of seconds 

Hmmm... little issue... I'll wait a couple of seconds 
Done! 

Các tập tin được tải về thành công.

Bạn có thể thấy rằng urllib2 hỗ trợ cả thời gian chờ và kết nối lại. Nếu bạn ngắt kết nối và ngắt kết nối trong 3 * 4 giây == 12 giây, nó sẽ hết thời gian cho tốt và tăng ngoại lệ gây tử vong. Điều này cũng có thể được xử lý.

+0

Cảm ơn, đó là một giải pháp tốt đẹp nhưng nó bị trì hoãn tải xuống thay vì tải xuống chậm. –

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