2009-01-19 19 views
10

के साथ थ्रॉटलिंग urllib2 का उपयोग करते समय आसानी से केबीपीएस को कैप करना संभव है? यदि ऐसा है, तो कोई भी कोड उदाहरण या संसाधन जो आप मुझे निर्देशित कर सकते हैं, की सराहना की जाएगी।urllib2

उत्तर

19

urllib मॉड्यूल में urlretrieve(url, filename=None, reporthook=None, data=None) फ़ंक्शन है। यदि आप reporthook -फंक्शन/ऑब्जेक्ट को token bucket या एक लीकी बाल्टी के रूप में लागू करते हैं, तो आपके पास अपनी वैश्विक दर-सीमा है।

संपादित करें: नज़दीकी परीक्षा में मुझे लगता है कि reporthook के साथ वैश्विक दर-सीमा करना उतना आसान नहीं है जैसा मैंने सोचा था। reporthook केवल डाउनलोड की गई राशि और कुल आकार दिया जाता है, जो कि टोकन-बाल्टी के साथ उपयोग करने के लिए जानकारी के लिए पर्याप्त नहीं है। इसके चारों ओर जाने का एक तरीका प्रत्येक रेट-लिमिटर में अंतिम डाउनलोड की गई राशि को संग्रहीत करना है, लेकिन वैश्विक टोकन-बाल्टी का उपयोग करना है।


संपादित करें 2: एक उदाहरण में दोनों कोड संयुक्त।

"""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

धन्यवाद मैजरएक्स। यह वही नहीं है जो मैं खोज रहा था क्योंकि मुझे urllib के बजाय urllib2 के लिए कार्यान्वयन की आवश्यकता है, लेकिन मुझे लगता है कि यह निश्चित रूप से मुझे सही दिशा में इंगित करता है। –

+2

बस एफवाईआई: मैंने एक सामान्य "read_limiting_rate()" फ़ंक्शन लिखा है जो Python3 में सभी पठनीय वस्तुओं पर लागू होता है। http://pastie.org/3120175 – Achimnol

संबंधित मुद्दे