Почему вызов паука-скрапа из pywikibot приводит к ошибке ReactorNotRestartable? - PullRequest
0 голосов
/ 04 ноября 2019

Я могу вызвать паука-скрапа из другого скрипта Python, используя CrawlerRunner или CrawlerProcess. Но когда я пытаюсь вызвать тот же самый класс вызова паука от робота pywikibot, я получаю ошибку ReactorNotRestartable. Почему это так и как я могу это исправить?

Вот ошибка:

  File ".\scripts\userscripts\ReplicationWiki\RWLoad.py", line 161, in format_new_page
    aea = AEAMetadata(url=DOI_url)
  File ".\scripts\userscripts\ReplicationWiki\GetAEAMetadata.py", line 39, in __init__
    reactor.run() # the script will block here until all crawling jobs are finished
  File "C:\Users\lextr\.conda\envs\py37\lib\site-packages\twisted\internet\base.py", line 1282, in run
    self.startRunning(installSignalHandlers=installSignalHandlers)
  File "C:\Users\lextr\.conda\envs\py37\lib\site-packages\twisted\internet\base.py", line 1262, in startRunning
    ReactorBase.startRunning(self)
  File "C:\Users\lextr\.conda\envs\py37\lib\site-packages\twisted\internet\base.py", line 765, in startRunning
    raise error.ReactorNotRestartable()
twisted.internet.error.ReactorNotRestartable
CRITICAL: Exiting due to uncaught exception <class 'twisted.internet.error.ReactorNotRestartable'>

Вот скрипт, который вызывает моего паука-скрапа. Он работает нормально, если я просто вызываю класс из main.

from twisted.internet import reactor, defer
from scrapy import signals
from scrapy.crawler import Crawler, CrawlerProcess, CrawlerRunner
from scrapy.settings import Settings
from scrapy.utils.project import get_project_settings

from Scrapers.spiders.ScrapeAEA import ScrapeaeaSpider

class AEAMetadata:
    """
    Helper to run ScrapeAEA spider and return JEL codes and data links
    for a given AEA article link.
    """

    def __init__(self, *args, **kwargs):
        """Initializer"""

        url = kwargs.get('url')
        if not url:
            raise ValueError('No article url given')

        self.items = []
        def collect_items(item, response, spider):
            self.items.append(item)

        settings = get_project_settings()
        crawler = Crawler(ScrapeaeaSpider, settings)
        crawler.signals.connect(collect_items, signals.item_scraped)

        runner = CrawlerRunner(settings)
        d = runner.crawl(crawler, url=url)
        d.addBoth(lambda _: reactor.stop())
        reactor.run() # the script will block here until all crawling jobs are finished

         #process = CrawlerProcess(settings)
         #process.crawl(crawler, url=url)
         #process.start()  # the script will block here until the crawling is finished

    def get_jelcodes(self):
        jelcodes = self.items[0]['jelcodes']
        return jelcodes

def main():
    aea = AEAMetadata(url='https://doi.org/10.1257/app.20180286')
    jelcodes = aea.get_jelcodes()
    print(jelcodes)

if __name__ == '__main__':
    main()

Обновлен простой тест, который дважды создает экземпляр класса AEAMetadata. Вот код вызова в моем боте pywikibot, который терпит неудачу:

from GetAEAMetadata import AEAMetadata

def main(*args):
    for _ in [1,2]:
        print('Top')
        url = 'https://doi.org/10.1257/app.20170442'
        aea = AEAMetadata(url=url)
        print('After AEAMetadata')
        jelcodes = aea.get_jelcodes()
        print(jelcodes)


if __name__ == '__main__':
    main()

1 Ответ

0 голосов
/ 08 ноября 2019

Мой вызов AEAMetadata был встроен в более крупный скрипт, который обманул меня до мысли, что класс AEAMetadata был создан только один раз до сбоя. Фактически, AEAMetadata вызывался дважды.

И я также думал, что скрипт будет блокироваться после реактора.run (), потому что комментарий во всех примерах скрапа показал, что это так. Тем не менее, вторым отложенным обратным вызовом является реактор.stop (), который разблокирует реактор.run ().

Более базовое неверное предположение заключалось в том, что реактор удалялся и создавался заново на каждой итерации. Фактически, реактор создается и инициализируется, когда он сначала импортируется. И это глобальный объект, который живет столько же, сколько и основной процесс, и не предназначен для перезапуска. Экстремальные значения, действительно необходимые для удаления и перезапуска реактора, описаны здесь: http://www.blog.pythonlibrary.org/2016/09/14/restarting-a-twisted-reactor/

Итак, я думаю, что я ответил на свой вопрос. И я переписываю свой сценарий, чтобы он не пытался использовать реактор так, как его никогда не планировали использовать.

И, спасибо, Галлайо, за то, что заставил меня задуматься в правильном направлении.

...