2

数日間実行することになっているイテレータがあります。エラーをキャッチして報告してから、イテレータを続行する必要があります。または、プロセス全体を最初からやり直すこともできます。

関数は次のとおりです。

def get_units(self, scraper):
    units = scraper.get_units()
    i = 0
    while True:
        try:
            unit = units.next()
        except StopIteration:
            if i == 0:
                log.error("Scraper returned 0 units", {'scraper': scraper})
            break
        except:
            traceback.print_exc()
            log.warning("Exception occurred in get_units", extra={'scraper': scraper, 'iteration': i})
        else:
            yield unit
        i += 1

コードの多くのバリエーションの1つである可能性があるためscraper、信頼できず、そこでエラーを処理したくありません。

ただし、でエラーが発生するunits.next()と、すべてが停止します。イテレータの反復の1つが失敗すると、イテレータがをスローするためだと思いStopIterationます。

これが出力です(最後の行のみ)

[2012-11-29 14:11:12 /home/amcat/amcat/scraping/scraper.py:135 DEBUG] Scraping unit <Element div at 0x4258c710>
[2012-11-29 14:11:13 /home/amcat/amcat/scraping/scraper.py:138 DEBUG] .. yields article
[2012-11-29 14:11:13 /home/amcat/amcat/scraping/scraper.py:138 DEBUG] .. yields article
[2012-11-29 14:11:13 /home/amcat/amcat/scraping/scraper.py:138 DEBUG] .. yields article
[2012-11-29 14:11:13 /home/amcat/amcat/scraping/scraper.py:138 DEBUG] .. yields article
[2012-11-29 14:11:13 /home/amcat/amcat/scraping/scraper.py:138 DEBUG] .. yields article
[2012-11-29 14:11:13 /home/amcat/amcat/scraping/scraper.py:138 DEBUG] .. yields article
[2012-11-29 14:11:13 /home/amcat/amcat/scraping/scraper.py:138 DEBUG] .. yields article
[2012-11-29 14:11:13 /home/amcat/amcat/scraping/scraper.py:138 DEBUG] .. yields article
[2012-11-29 14:11:13 /home/amcat/amcat/scraping/scraper.py:138 DEBUG] .. yields article
[2012-11-29 14:11:13 /home/amcat/amcat/scraping/scraper.py:138 DEBUG] .. yields article
[2012-11-29 14:11:13 /home/amcat/amcat/scraping/scraper.py:138 DEBUG] .. yields article Counter-Strike: Global Offensive Update Released
Traceback (most recent call last):
  File "/home/amcat/amcat/scraping/controller.py", line 101, in get_units
    unit = units.next()
  File "/home/amcat/amcat/scraping/scraper.py", line 114, in get_units
    for unit in self._get_units():
  File "/home/amcat/scraping/games/steamcommunity.py", line 90, in _get_units
    app_doc = self.getdoc(url,urlencode(form))
  File "/home/amcat/amcat/scraping/scraper.py", line 231, in getdoc
    return self.opener.getdoc(url, encoding)
  File "/home/amcat/amcat/scraping/htmltools.py", line 54, in getdoc
    response = self.opener.open(url, encoding)
  File "/usr/lib/python2.7/urllib2.py", line 406, in open
    response = meth(req, response)
  File "/usr/lib/python2.7/urllib2.py", line 519, in http_response
    'http', request, response, code, msg, hdrs)
  File "/usr/lib/python2.7/urllib2.py", line 444, in error
    return self._call_chain(*args)
  File "/usr/lib/python2.7/urllib2.py", line 378, in _call_chain
    result = func(*args)
  File "/usr/lib/python2.7/urllib2.py", line 527, in http_error_default
    raise HTTPError(req.get_full_url(), code, msg, hdrs, fp)
HTTPError: HTTP Error 500: Internal Server Error
[2012-11-29 14:11:14 /home/amcat/amcat/scraping/controller.py:110 WARNING] Exception occurred in get_units

...code ends...

では、エラーが発生したときに反復が停止するのを防ぐにはどうすればよいですか?

編集:get_units()内のコードは次のとおりです

def get_units(self):
    """                                                                                                                                                                                                                                  
    Split the scraping job into a number of 'units' that can be processed independently                                                                                                                                                  
    of each other.                                                                                                                                                                                                                       

    @return: a sequence of arbitrary objects to be passed to scrape_unit                                                                                                                                                                 
    """
    self._initialize()
    for unit in self._get_units():
        yield unit

そして、ここに簡略化された_get_units()があります:

INDEX_URL = "http://www.steamcommunity.com"

def _get_units(self):
  doc = self.getdoc(INDEX_URL)  #returns a lxml.etree document

  for a in doc.cssselect("div.discussion a"):
    link = a.get('href')
    yield link

編集:質問のフォローアップ:関数の各forループを変更して、反復が失敗するたびにエラー処理が自動的に実行されるようにします

4

1 に答える 1

3

StopIterationnext()次のアイテムがなくなったときにジェネレータのメソッドによって発生します。ジェネレータ/イテレータ内のエラーとは何の関係もありません。

もう1つの注意点は、イテレータのタイプによっては、例外の後で再開できない場合があることです。イテレータがメソッドを持つオブジェクトであるnext場合、それは機能します。ただし、実際にジェネレーターである場合は、そうではありません。

私の知る限り、これが、からのエラーの後に反復が続行されない唯一の理由units.next()です。つまりunits.next()、失敗し、次に呼び出したときに再開できず、StopIteration例外をスローして完了したと表示されます。

基本的にscraper.get_units()、1回の反復でエラーが発生した後、ループを続行できない理由を理解するには、内部のコードを表示する必要があります。get_units()がジェネレーター関数として実装されている場合、それは明らかです。そうでない場合は、再開を妨げている他の何かである可能性があります。

更新:ジェネレーター関数とは何かを説明します:

class Scraper(object):
    def get_units(self):
        for i in some_stuff:
            bla = do_some_processing()
            bla *= 2  # random stuff
            yield bla

これで、を呼び出すとScraper().get_units()、関数全体を実行する代わりに、ジェネレータオブジェクトが返されます。それを呼び出すnext()と、実行が最初になりyieldます。など。エラーが内部のどこかで発生した場合get_units、それは汚染されます。つまり、次に電話をかけるとnext()StopIteration提供するアイテムが不足したかのように発生します。

http://www.dabeaz.com/generators/(およびhttp://www.dabeaz.com/coroutines/ )を読むことを強くお勧めします。

UPDATE2:考えられる解決策https://gist.github.com/4175802

于 2012-11-30T12:29:42.797 に答える