2 votes

Scrapy RéacteurNonRedémarrable avec un Certain Réacteur

Je tente de planifier Scrapy en utilisant Celery et j'ai rencontré l'erreur commune ReactorNotRestartable. Ces discussions passées ont abordé cette erreur.

ReactorNotRestartable - Twisted et scrapy Scrapy - Reactor not Restartable

La bibliothèque que j'utilise nécessite twisted.internet.asyncioreactor.AsyncioSelectorReactor au lieu de celui par défaut. Si je suis les exemples, mon code s'arrête car le réacteur demandé ne correspond pas au réacteur en cours d'exécution. J'ai essayé de le modifier pour utiliser le bon réacteur, mais j'obtiens toujours la même exception de réacteur non conforme.

from scrapy.utils.log import configure_logging
from multiprocessing import Process, Queue

def run_spider(spider, domain=None, check=None):
    def f(q):
        try:
            configure_logging()
            runner = CrawlerRunner(get_project_settings())
            deferred = runner.crawl(spider, domain=domain, check=check)
            deferred.addBoth(lambda _: reactor.stop())
            reactor = AsyncioSelectorReactor()
            reactor.run()
            q.put(None)
        except Exception as e:
            print("EXCEPTION!")
            q.put(e)

    q = Queue()
    p = Process(target=f, args=(q,))
    p.start()
    result = q.get()
    p.join()

    if result is not None:
        raise result

Traceback (most recent call last):
  File "/usr/local/lib/python3.10/site-packages/twisted/internet/defer.py", line 1697, in _inlineCallbacks
    result = context.run(gen.send, result)
  File "/code/scrapy_parsing/scripts/run_spider.py", line 203, in crawl
    yield runner.crawl(spider)
  File "/usr/local/lib/python3.10/site-packages/scrapy/crawler.py", line 232, in crawl
    crawler = self.create_crawler(crawler_or_spidercls)
  File "/usr/local/lib/python3.10/site-packages/scrapy/crawler.py", line 266, in create_crawler
    return self._create_crawler(crawler_or_spidercls)
  File "/usr/local/lib/python3.10/site-packages/scrapy/crawler.py", line 271, in _create_crawler
    return Crawler(spidercls, self.settings)
  File "/usr/local/lib/python3.10/site-packages/scrapy/crawler.py", line 103, in __init__
    verify_installed_reactor(reactor_class)
  File "/usr/local/lib/python3.10/site-packages/scrapy/utils/reactor.py", line 138, in verify_installed_reactor
    raise Exception(msg)
Exception: Le réacteur installé (twisted.internet.epollreactor.EPollReactor) ne correspond pas à celui demandé (twisted.internet.asyncioreactor.AsyncioSelectorReactor)```

1voto

Jean-Paul Calderone Points 27680

Dans le corps de votre fonction, vous avez :

            reactor = AsyncioSelectorReactor()
            reactor.run()

En général, vous n'êtes pas censé instancier un réacteur Twisted (du tout, comme ceci ou autrement). Au lieu de cela, vous devriez utiliser sa fonction install :

from twisted.internet import asyncioreactor
asyncioreactor.install()
from twisted.internet import reactor

Je suppose que vous avez essayé de créer un nouveau réacteur dans la boucle interne parce que vous voulez démarrer et arrêter le réacteur plusieurs fois, piloté par quelque chose que Scrapy fait. En général, Twisted ne prend pas en charge cette utilisation. Votre boucle interne est exécutée par multiprocessing.Process qui est peut-être une tentative de contourner cette limitation de Twisted - en faisant en sorte que chaque itération de votre boucle interne s'exécute dans un nouveau processus, où aucun réacteur Twisted n'a été démarré ou arrêté auparavant. En général, je ne m'attendrais pas à ce que chaque partie de Twisted fonctionne correctement lorsqu'elle est pilotée par multiprocessing.Process. La gestion des processus est complexe et il n'y a pas eu beaucoup d'efforts pour faire fonctionner correctement ces deux éléments ensemble. Cependant, un sous-ensemble limité de fonctionnalités peut éventuellement fonctionner (mais vous devrez probablement découvrir vous-même quel est ce sous-ensemble).

Complicant tout cela est le fait que Scrapy lui-même utilise également Twisted, donc vous avez Twisted utilisé dans le processus parent et les processus enfants. En raison de cela, il n'est pas clair pour moi si le processus enfant hérite d'un réacteur initialisé dans le processus parent ou s'il pourra démarrer un tout nouveau réacteur sans être entravé par ce que le parent a fait.

Si le processus enfant est vraiment une ardoise propre, alors vous devriez faire en sorte que les premières lignes de f effectuent l'installation du réacteur que j'ai suggéré ci-dessus :

from twisted.internet import asyncioreactor
asyncioreactor.install()
from twisted.internet import reactor

S'il n'est pas une ardoise propre, alors vous pourriez faire en sorte que le processus parent utilise également ce réacteur, en mettant les mêmes lignes exactes au début de votre programme global. Cependant, cela vous ramènera probablement à ReactorNotRestartable ou quelque chose de similaire car le réacteur que le parent installe/démarre entravera l'installation/démarrage d'un réacteur dans le processus enfant.

La réponse encore plus générale est que lors de l'utilisation de Twisted, vous devriez structurer votre programme entier de manière à ce que le réacteur n'ait besoin d'être démarré et arrêté qu'une seule fois et que toutes les boucles soient exécutées entre ces deux événements. Lors de l'intégration de Twisted dans une application existante, cela est parfois difficile mais https://pypi.org/project/crochet/ peut aider en facilitant le contournement de la restructuration de l'ensemble du programme et en poussant toutes les activités basées sur Twisted dans un thread séparé où le réacteur peut être démarré et arrêté une seule fois.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X