【问题标题】:Scrapy - ReactorAlreadyInstalledError when using TwistedSchedulerScrapy - 使用 TwistedScheduler 时出现 ReactorAlreadyInstalledError
【发布时间】:2022-07-09 20:55:18
【问题描述】:

我有以下 Python 代码来启动 APScheduler/TwistedScheduler cronjob 来启动蜘蛛。

使用一只蜘蛛没有问题,而且效果很好。但是使用两个蜘蛛会导致错误:twisted.internet.error.ReactorAlreadyInstalledError: reactor already installed。

我确实找到了related question,使用CrawlerRunner 作为解决方案。但是,我使用的是 TwistedScheduler 对象,所以我不知道如何使用多个 cron 作业(多个 add_job())来使其工作。

from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings
from apscheduler.schedulers.twisted import TwistedScheduler

from myprojectscraper.spiders.my_homepage_spider import MyHomepageSpider
from myprojectscraper.spiders.my_spider import MySpider

process = CrawlerProcess(get_project_settings())
# Start the crawler in a scheduler
scheduler = TwistedScheduler(timezone="Europe/Amsterdam")
# Use cron job; runs the 'homepage' spider every 4 hours (eg. 12:10, 16:10, 20:10, etc.)
scheduler.add_job(process.crawl, 'cron', args=[MyHomepageSpider], hour='*/4', minute=10)
# Use cron job; runs the full spider every week on the monday, tuesday and saturday at 4:35 midnight
scheduler.add_job(process.crawl, 'cron', args=[MySpider], day_of_week='mon,thu,sat', hour=4, minute=35)
scheduler.start()
process.start(False)

【问题讨论】:

    标签: python scrapy twisted reactor twisted.internet


    【解决方案1】:

    https://docs.scrapy.org/en/latest/topics/practices.html#run-scrapy-from-a-script

    还有另一个 Scrapy 实用程序可以更好地控制爬取过程:scrapy.crawler.CrawlerRunner。这个类是一个瘦包装器,它封装了一些简单的帮助器来运行多个爬虫,但它不会以任何方式启动或干扰现有的反应器。
    如果您的应用程序已经在使用 Twisted 并且您想在同一个反应器中运行 Scrapy,建议您使用 CrawlerRunner 而不是 CrawlerProcess。

    https://docs.scrapy.org/en/latest/topics/practices.html#running-multiple-spiders-in-the-same-process

    默认情况下,当您运行 scrapy crawl 时,Scrapy 会为每个进程运行一个爬虫。但是,Scrapy 支持使用内部 API 在每个进程中运行多个蜘蛛。

    
    from scrapy.crawler import CrawlerRunner
    from scrapy.utils.project import get_project_settings
    from scrapy.utils.log import configure_logging
    from twisted.internet import reactor
    from apscheduler.schedulers.twisted import TwistedScheduler
    
    from myprojectscraper.spiders.my_homepage_spider import MyHomepageSpider
    from myprojectscraper.spiders.my_spider import MySpider
    
    configure_logging()
    
    runner = CrawlerRunner(get_project_settings())
    scheduler = TwistedScheduler(timezone="Europe/Amsterdam")
    # Use cron job; runs the 'homepage' spider every 4 hours (eg. 12:10, 16:10, 20:10, etc.)
    scheduler.add_job(runner.crawl, 'cron', args=[MyHomepageSpider], hour='*/4', minute=10)
    # Use cron job; runs the full spider every week on the monday, tuesday and saturday at 4:35 midnight
    scheduler.add_job(runner.crawl, 'cron', args=[MySpider], day_of_week='mon,thu,sat', hour=4, minute=35)
    
    deferred = runner.join()
    deferred.addBoth(lambda _: reactor.stop())
    
    scheduler.start()
    reactor.run()  # the script will block here until all crawling jobs are finished
    scheduler.shutdown()
    

    【讨论】:

    • 谢谢。整个Queue、q.put 和q.get 都是可选的,对吗?我使用 Scrapy 中间件来处理项目。
    • *args 也是可选的。如果你不向你的蜘蛛或超类(scrapy.Spider)传递额外的参数。
    • 你测试过代码吗?因为它不起作用。 TwistedScheduler 似乎不接受 run_spider。代码正在执行,但没有显示调度程序的任何输出。脚本基本上是通过执行完成的。虽然脚本应该忙于等待即将到来的 cron 作业(add_job 的即将到来的作业)。
    • @MelroyvandenBerg 是的,Queue 和 *args 是可选的。不,我没有用TwistedScheduler 尝试过,但ReactorAlreadyInstalledError 应该用这个来解决。
    • @MelroyvandenBerg 我自己测试了这段代码。它也有效。无论如何,回答我的第一个问题是很好的体验。学到了很多。谢谢你:)
    【解决方案2】:

    您可以尝试在开始该过程之前删除已安装的反应器:

    from scrapy.crawler import CrawlerProcess
    from scrapy.utils.project import get_project_settings
    from apscheduler.schedulers.twisted import TwistedScheduler
    
    from myprojectscraper.spiders.my_homepage_spider import MyHomepageSpider
    from myprojectscraper.spiders.my_spider import MySpider
    import sys  """<--- import sys here"""
    
    process = CrawlerProcess(get_project_settings())
    # Start the crawler in a scheduler
    scheduler = TwistedScheduler(timezone="Europe/Amsterdam")
    # Use cron job; runs the 'homepage' spider every 4 hours (eg. 12:10, 16:10, 20:10, etc.)
    scheduler.add_job(process.crawl, 'cron', args=[MyHomepageSpider], hour='*/4', minute=10)
    # Use cron job; runs the full spider every week on the monday, tuesday and saturday at 4:35 midnight
    scheduler.add_job(process.crawl, 'cron', args=[MySpider], day_of_week='mon,thu,sat', hour=4, minute=35)
    scheduler.start()
    
    if "twisted.internet.reactor" in sys.modules:
        del sys.modules["twisted.internet.reactor"] """<--- Delete twisted reactor if already installed here """
    
    process.start(False)
    
    
    

    这对我有用。

    【讨论】:

      【解决方案3】:

      我现在将BlockingScheduler 与Process 和CrawlerRunner 结合使用。以及通过configure_logging() 启用日志记录。

      from multiprocessing import Process
      
      from scrapy.crawler import CrawlerRunner
      from scrapy.utils.project import get_project_settings
      from scrapy.utils.log import configure_logging
      from apscheduler.schedulers.blocking import BlockingScheduler
      
      from myprojectscraper.spiders.my_homepage_spider import MyHomepageSpider
      from myprojectscraper.spiders.my_spider import MySpider
      
      from twisted.internet import reactor
      
      # Create Process around the CrawlerRunner
      class CrawlerRunnerProcess(Process):
          def __init__(self, spider):
              Process.__init__(self)
              self.runner = CrawlerRunner(get_project_settings())
              self.spider = spider
      
          def run(self):
              deferred = self.runner.crawl(self.spider)
              deferred.addBoth(lambda _: reactor.stop())
              reactor.run(installSignalHandlers=False)
      
      # The wrapper to make it run multiple spiders, multiple times
      def run_spider(spider):
          crawler = CrawlerRunnerProcess(spider)
          crawler.start()
          crawler.join()
      
      # Enable logging when using CrawlerRunner
      configure_logging()
      
      # Start the crawler in a scheduler
      scheduler = BlockingScheduler(timezone="Europe/Amsterdam")
      # Use cron job; runs the 'homepage' spider every 4 hours (eg. 12:10, 16:10, 20:10, etc.)
      scheduler.add_job(run_spider, 'cron', args=[MyHomepageSpider], hour='*/4', minute=10)
      # Use cron job; runs the full spider every week on the monday, tuesday and saturday at 4:35 midnight
      scheduler.add_job(run_spider, 'cron', args=[MySpider], day_of_week='mon,thu,sat', hour=4, minute=35)
      scheduler.start()
      

      脚本至少不会直接退出(它会阻塞)。我现在按预期得到以下输出:

      2022-03-31 22:50:24 [apscheduler.scheduler] INFO: Adding job tentatively -- it will be properly scheduled when the scheduler starts
      2022-03-31 22:50:24 [apscheduler.scheduler] INFO: Adding job tentatively -- it will be properly scheduled when the scheduler starts
      2022-03-31 22:50:24 [apscheduler.scheduler] INFO: Added job "run_spider" to job store "default"
      2022-03-31 22:50:24 [apscheduler.scheduler] INFO: Added job "run_spider" to job store "default"
      2022-03-31 22:50:24 [apscheduler.scheduler] INFO: Scheduler started
      2022-03-31 22:50:24 [apscheduler.scheduler] DEBUG: Looking for jobs to run
      2022-03-31 22:50:24 [apscheduler.scheduler] DEBUG: Next wakeup is due at 2022-04-01 00:10:00+02:00 (in 4775.280995 seconds)
      

      由于我们使用BlockingScheduler,调度程序不会直接退出,但start() 是一个阻塞调用。这意味着它允许调度程序无限运行作业。

      【讨论】:

        【解决方案4】:

        我的解决方案是输入twisted的源代码并进入internet文件夹并找到selectedreactor.py。

        然后转到页面底部def install并在installReactor(reactor)上方添加以下内容

        如:

        def install():
            """Configure the twisted mainloop to be run using the select() reactor."""
            reactor = SelectReactor()
            from twisted.internet.main import installReactor
            
            if "twisted.internet.reactor" in sys.modules:
                del sys.modules["twisted.internet.reactor"]
        
            installReactor(reactor)
        
        
        __all__ = ["install"]
        

        这应该删除所有预先安装的反应器,然后安装一个新的。

        这应该会永久消除该问题,因为我还没有使用此方法遇到任何问题。

        【讨论】:

          猜你喜欢
          • 2019-04-05
          • 1970-01-01
          • 1970-01-01
          • 2015-06-01
          • 1970-01-01
          • 1970-01-01
          • 2018-10-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多