2017-04-30 8 views
2

私は間隔でリフレッシュするリストから一度に1つずつプロキシをプルしたいと思います。(Mis)Generatorsを理解してください

プロキシの一部がうまくいかず、リストから次のプロンプトを使いたいです。これは私のジェネレータが来るところですが、最初に.next()を呼び出すことでジェネレータをローリングさせることができますが、2回目には同じ値が得られます!

明らかに、ジェネレータの仕組みを理解する上で重要な部分が欠けているはずです。

マイジェネレータコードはProxyHandlerクラスの内部にある:

class ProxyHandler: 

    def __init__(self): 
     self.proxies = list() 
     self.current = dict() 

    def get_proxies(self): 
     """ Retrieves proxies """ 

    def __len__(self): 
     return len(self.proxies) 

    def yield_proxy(self): 
     if not self.proxies: 
      print 'Created new proxy list' 
      self.get_proxies() # This populates self.proxies which is a list of tuples where the 0th element is the host and the 1st element is the port 
     for p in self.proxies: 
      try: 
       proxy = {'http': 'http://%s:%s' % (p[0], p[1])} # Formatted to python's request lib proxy format 
       self.current = proxy 
       yield proxy 
      except StopIteration: 
       print 'Reached end of proxy list' 
       self.current = {} 
       self.get_proxies() 
       yield self.yield_proxy() 

と使用方法:

def get_response(self, url): 
    proxy = self.proxy_handler.current 
    if proxy == {}: 
     proxy = self.proxy_handler.yield_proxy().next() 
    print 'Current proxy -', proxy 
    response = url_request(url, proxy=proxy) # url_request() is basically a modified version of python's requests 
    print response 
    if response: # url_request() returns true if status code == 200 
     return response, proxy 
    gen = self.proxy_handler.yield_proxy() 
    gen.next() 
    return self.get_ebay_response(url) 

答えて

2

あなたが新たに毎回ジェネ​​レータを作成している:

gen = self.proxy_handler.yield_proxy() 
gen.next() 

新しい発電機が起動します最初から;別々の発電機は状態を共有しません。ジェネレータをどこかに保存し、そのオブジェクトを再利用して新しい値を取得します。

あなたはおそらくselfに属性としてそのジェネレータオブジェクトを格納することができ:

proxy_generator = None 

def get_response(self, url): 
    if not self.proxy: 
     if self.proxy_generator is None 
      self.proxy_generator = self.proxy_handler.yield_proxy() 
     self.proxy = next(self.proxy_generator) 
    proxy = self.proxy 

は、私はあなたが、遅かれ早かれに切り替える必要がありますのPython 3(との上位互換性、あなたのコードを維持するためにnext() functionを使用し、 Python 2は従来の言語です)。

次に、あなたの発電機がスローされることは決してないだろう。例外をキャッチしようとします:では

for p in self.proxies: 
    try: 
     proxy = {'http': 'http://%s:%s' % (p[0], p[1])} # Formatted to python's request lib proxy format 
     self.current = proxy 
     yield proxy 
    except StopIteration: 
     print 'Reached end of proxy list' 
     self.current = {} 
     self.get_proxies() 
     yield self.yield_proxy() 

をごtryアクセスされている何のジェネレータがありません。あなたはforループにself.proxies以上の仕事を、forはすでにがループを終了するにはStopIteratorをキャッチする方法を知っています。とにかくself.proxiesはリストに過ぎません。

あなたがプロキシを介してループサイクルを作りたいと思った場合は、無限while Trueループでそれを行う:

while True: 
    for p in self.proxies: 
     proxy = {'http': 'http://%s:%s' % (p[0], p[1])} # Formatted to python's request lib proxy format 
     self.current = proxy 
     yield proxy 

    print 'Reached end of proxy list' 
    self.current = {} 
    self.get_proxies() 

あなたがそこにself.currentをクリアする必要があり、再フェッチと思う私はなぜわからないんだけどプロキシあなたのジェネレータのタプルは変更されていないので、なぜ再フェッチするのですか?そして、ループを最初からやり直しても、現在のプロキシは有効です。私はそれらの最後の3行を削除します。

コードをさらに簡単にすることができます。発電機は長さがないので、__len__の方法は必要ありません。せいぜい、この方法はが間違ったという情報を生成します。 self.proxies属性は反復処理を開始するまでは空白なので、オブジェクトの長さは0から始まります。メソッドを完全に削除します。ただ、すべてのこれらの値を生成するジェネレータを取得するためにiter(self.proxy_handler)の代わりself.proxy_handler.yield_proxy()を使用し、

class ProxyHandler: 
    def __init__(self): 
     self.proxies = [] 
     self.current = {} 

    def get_proxies(self): 
     """ Retrieves proxies """ 

    def __iter__(self): 
     if not self.proxies: 
      print 'Created new proxy list' 
      self.get_proxies() 
     while True: 
      for p in self.proxies: 
       proxy = {'http': 'http://%s:%s' % (p[0], p[1])} 
       self.current = proxy 
       yield proxy 

これは全体ProxyHandlerインスタンス反復可能になります:

次に、あなたのオブジェクト発電機を生産__iter__方法を与えることができます。

最後に、ジェネレータ式をitertools.cycle()と一緒に使用すると、イテレータを無限にすることができます。ただし、current属性をドロップする必要がありますが、あなたのジェネレータはとにかく現在のオブジェクトが生じたとき、あなたが実際にその属性を必要としないとして、それは本当に問題ではないはずです。

from itertools import cycle 

class ProxyHandler: 
    def __init__(self): 
     self.proxies = [] 

    def get_proxies(self): 
     """ Retrieves proxies """ 

    def __iter__(self): 
     if not self.proxies: 
      print 'Created new proxy list' 
      self.get_proxies() 
     return cycle({'http': 'http://%s:%s' % (p[0], p[1])} for p in self.proxies) 

をジェネレータ式が生成します同じ種類のオブジェクト。

これはまだすべてiter(self.proxy_generator)が必要です。 __iter__ return selfを返し、next()メソッドを追加することによって、イテレータ(イテラータブルではなく)をインスタンスにすることができます。値を生成することにnext()呼び出しに渡し、その後、最初の呼び出しの属性に上記のジェネレータ式を移動:

class ProxyHandler: 
    def __init__(self): 
     self.proxies = [] 
     self._gen = None 

    def get_proxies(self): 
     """ Retrieves proxies """ 

    def __iter__(self): 
     return self 

    def next(self): 
     if not self._gen: 
      self.get_proxies() 
      self._gen = cycle({'http': 'http://%s:%s' % (p[0], p[1])} for p in self.proxies) 
     return next(self._gen) 

    __next__ = next # Python 3 compatibility 

今、あなたは `次の(self.proxy_handler)ごとに使用することができます。

def get_response(self, url): 
    if not self.proxy: 
     self.proxy = next(self.proxy_handler) 
    proxy = self.proxy 
+0

これを解決するには、私のメソッド外にジェネレータを作成し、必要なときに呼び出す必要がありますか? –

+0

@galalmighty:正確に。ジェネレータは状態を保持する単一オブジェクトです。値を必要とするたびに置き換えるのではなく、代わりにそれを保持してください。 –

+0

洞察を深め、詳細で非常にクリア。ありがとうございました!約10分で多くのことを学びました。 –

関連する問題