2009-01-19 34 views
10

có thể dễ dàng giới hạn tốc độ kbps khi sử dụng urllib2 không? Nếu có, bất kỳ ví dụ mã hoặc tài nguyên nào bạn có thể hướng dẫn tôi sẽ được đánh giá cao.Điều chỉnh với urllib2

Trả lời

19

Có chức năng urlretrieve(url, filename=None, reporthook=None, data=None) trong mô-đun urllib. Nếu bạn triển khai reporthook -chức năng/đối tượng dưới dạng token bucket hoặc nhóm bị rò rỉ, bạn có giới hạn tốc độ toàn cầu của mình.

CHỈNH SỬA: Khi kiểm tra kỹ hơn Tôi thấy rằng không dễ dàng thực hiện giới hạn tốc độ toàn cầu với reporthook như tôi nghĩ. reporthook chỉ được cung cấp số tiền đã tải xuống và tổng kích thước mà chính chúng không đủ thông tin để sử dụng với nhóm mã thông báo. Một cách để vượt qua nó là lưu trữ số tiền được tải xuống lần cuối trong mỗi giới hạn tốc độ, nhưng sử dụng một nhóm mã thông báo toàn cục.


EDIT 2: Kết hợp cả hai mã thành một ví dụ.

"""Rate limiters with shared token bucket.""" 

import os 
import sys 
import threading 
import time 
import urllib 
import urlparse 

class TokenBucket(object): 
    """An implementation of the token bucket algorithm. 
    source: http://code.activestate.com/recipes/511490/ 

    >>> bucket = TokenBucket(80, 0.5) 
    >>> print bucket.consume(10) 
    True 
    >>> print bucket.consume(90) 
    False 
    """ 
    def __init__(self, tokens, fill_rate): 
     """tokens is the total tokens in the bucket. fill_rate is the 
     rate in tokens/second that the bucket will be refilled.""" 
     self.capacity = float(tokens) 
     self._tokens = float(tokens) 
     self.fill_rate = float(fill_rate) 
     self.timestamp = time.time() 
     self.lock = threading.RLock() 

    def consume(self, tokens): 
     """Consume tokens from the bucket. Returns 0 if there were 
     sufficient tokens, otherwise the expected time until enough 
     tokens become available.""" 
     self.lock.acquire() 
     tokens = max(tokens,self.tokens) 
     expected_time = (tokens - self.tokens)/self.fill_rate 
     if expected_time <= 0: 
      self._tokens -= tokens 
     self.lock.release() 
     return max(0,expected_time) 

    @property 
    def tokens(self): 
     self.lock.acquire() 
     if self._tokens < self.capacity: 
      now = time.time() 
      delta = self.fill_rate * (now - self.timestamp) 
      self._tokens = min(self.capacity, self._tokens + delta) 
      self.timestamp = now 
     value = self._tokens 
     self.lock.release() 
     return value 

class RateLimit(object): 
    """Rate limit a url fetch. 
    source: http://mail.python.org/pipermail/python-list/2008-January/472859.html 
    (but mostly rewritten) 
    """ 
    def __init__(self, bucket, filename): 
     self.bucket = bucket 
     self.last_update = 0 
     self.last_downloaded_kb = 0 

     self.filename = filename 
     self.avg_rate = None 

    def __call__(self, block_count, block_size, total_size): 
     total_kb = total_size/1024. 

     downloaded_kb = (block_count * block_size)/1024. 
     just_downloaded = downloaded_kb - self.last_downloaded_kb 
     self.last_downloaded_kb = downloaded_kb 

     predicted_size = block_size/1024. 

     wait_time = self.bucket.consume(predicted_size) 
     while wait_time > 0: 
      time.sleep(wait_time) 
      wait_time = self.bucket.consume(predicted_size) 

     now = time.time() 
     delta = now - self.last_update 
     if self.last_update != 0: 
      if delta > 0: 
       rate = just_downloaded/delta 
       if self.avg_rate is not None: 
        rate = 0.9 * self.avg_rate + 0.1 * rate 
       self.avg_rate = rate 
      else: 
       rate = self.avg_rate or 0. 
      print "%20s: %4.1f%%, %5.1f KiB/s, %.1f/%.1f KiB" % (
        self.filename, 100. * downloaded_kb/total_kb, 
        rate, downloaded_kb, total_kb, 
       ) 
     self.last_update = now 


def main(): 
    """Fetch the contents of urls""" 
    if len(sys.argv) < 4: 
     print 'Syntax: %s rate url1 url2 ...' % sys.argv[0] 
     raise SystemExit(1) 
    rate_limit = float(sys.argv[1]) 
    urls = sys.argv[2:] 
    bucket = TokenBucket(10*rate_limit, rate_limit) 

    print "rate limit = %.1f" % (rate_limit,) 

    threads = [] 
    for url in urls: 
     path = urlparse.urlparse(url,'http')[2] 
     filename = os.path.basename(path) 
     print 'Downloading "%s" to "%s"...' % (url,filename) 
     rate_limiter = RateLimit(bucket, filename) 
     t = threading.Thread(
      target=urllib.urlretrieve, 
      args=(url, filename, rate_limiter)) 
     t.start() 
     threads.append(t) 

    for t in threads: 
     t.join() 

    print 'All downloads finished' 

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

Cảm ơn bạn MizardX. Nó không phải là chính xác những gì tôi đang tìm kiếm như tôi cần một thực hiện cho urllib2 hơn là urllib, nhưng tôi nghĩ rằng điều này chắc chắn đã chỉ cho tôi đi đúng hướng. –

+2

Chỉ cần thông tin: Tôi đã viết một hàm "read_limiting_rate()" chung có thể áp dụng cho tất cả các đối tượng có thể đọc được trong Python3. http://pastie.org/3120175 – Achimnol