2015-06-21 11 views
5

Ich sammle tägliche Daten mit Scrapy unter Verwendung eines zweistufigen Crawls. Die erste Stufe erzeugt eine Liste von URLs von einer Indexseite und die zweite Stufe schreibt das HTML für jede der URLs in der Liste zu einem Kafka-Thema.Scrapy `ReactorNotRestartable`: Eine Klasse, um zwei (oder mehr) Spider auszuführen

kafka cluster for Scrapy crawler

Obwohl die beiden Komponenten des Crawl verwendet sind, würde Ich mag sie, unabhängig zu sein: die url_generator würde als eine geplante Aufgabe ausgeführt werden einmal am Tag, und die page_requester liefe ständig, Verarbeitung URL, wenn verfügbar. Um "höflich" zu sein, passe ich die DOWNLOAD_DELAY so an, dass der Crawler gut innerhalb der 24 Stunden endet, aber die Seite nur minimal belastet.

Ich habe eine CrawlerRunner Klasse, die Funktionen der URL und rufen Sie die HTML zu erzeugen hat:

from twisted.internet import reactor 
from scrapy.crawler import Crawler 
from scrapy import log, signals 
from scrapy_somesite.spiders.create_urls_spider import CreateSomeSiteUrlList 
from scrapy_somesite.spiders.crawl_urls_spider import SomeSiteRetrievePages 
from scrapy.utils.project import get_project_settings 
import os 
import sys 

class CrawlerRunner: 

    def __init__(self): 
     sys.path.append(os.path.join(os.path.curdir, "crawl/somesite")) 
     os.environ['SCRAPY_SETTINGS_MODULE'] = 'scrapy_somesite.settings' 
     self.settings = get_project_settings() 
     log.start() 

    def create_urls(self): 
     spider = CreateSomeSiteUrlList() 
     crawler_create_urls = Crawler(self.settings) 
     crawler_create_urls.signals.connect(reactor.stop, signal=signals.spider_closed) 
     crawler_create_urls.configure() 
     crawler_create_urls.crawl(spider) 
     crawler_create_urls.start() 
     reactor.run() 

    def crawl_urls(self): 
     spider = SomeSiteRetrievePages() 
     crawler_crawl_urls = Crawler(self.settings) 
     crawler_crawl_urls.signals.connect(reactor.stop, signal=signals.spider_closed) 
     crawler_crawl_urls.configure() 
     crawler_crawl_urls.crawl(spider) 
     crawler_crawl_urls.start() 
     reactor.run() 

Wenn ich die Klasse instanziiert, ich bin in der Lage erfolgreich entweder Funktion auf eigene auszuführen, aber leider ich bin nicht in der Lage, sie zusammenführen:

from crawl.somesite import crawler_runner 

cr = crawler_runner.CrawlerRunner() 

cr.create_urls() 
cr.crawl_urls() 

der zweite Funktionsaufruf ein twisted.internet.error.ReactorNotRestartable erzeugt, wenn er versucht reactor.run() in der crawl_urls Funktion auszuführen.

Ich frage mich, ob es eine einfache Lösung für diesen Code gibt (z. B. eine Möglichkeit, zwei getrennte Twisted-Reaktoren zu betreiben), oder ob es eine bessere Möglichkeit gibt, dieses Projekt zu strukturieren.

Antwort

6

Es war möglich, mehrere Spinnen innerhalb eines Reaktors zu betreiben, indem der Reaktor offen gehalten wurde, bis alle Spinnen aufgehört hatten zu laufen. Dies wurde, indem sie eine Liste aller laufenden Spinnen erreicht und nicht reactor.stop() Ausführung, bis diese Liste leer ist:

import sys 
import os 
from scrapy.utils.project import get_project_settings 
from scrapy_somesite.spiders.create_urls_spider import Spider1 
from scrapy_somesite.spiders.crawl_urls_spider import Spider2 

from scrapy import signals, log 
from twisted.internet import reactor 
from scrapy.crawler import Crawler 

class CrawlRunner: 

    def __init__(self): 
     self.running_crawlers = [] 

    def spider_closing(self, spider): 
     log.msg("Spider closed: %s" % spider, level=log.INFO) 
     self.running_crawlers.remove(spider) 
     if not self.running_crawlers: 
      reactor.stop() 

    def run(self): 

     sys.path.append(os.path.join(os.path.curdir, "crawl/somesite")) 
     os.environ['SCRAPY_SETTINGS_MODULE'] = 'scrapy_somesite.settings' 
     settings = get_project_settings() 
     log.start(loglevel=log.DEBUG) 

     to_crawl = [Spider1, Spider2] 

     for spider in to_crawl: 

      crawler = Crawler(settings) 
      crawler_obj = spider() 
      self.running_crawlers.append(crawler_obj) 

      crawler.signals.connect(self.spider_closing, signal=signals.spider_closed) 
      crawler.configure() 
      crawler.crawl(crawler_obj) 
      crawler.start() 

     reactor.run() 

Die Klasse ausgeführt wird:

from crawl.somesite.crawl import CrawlRunner 

cr = CrawlRunner() 
cr.run() 

Diese Lösung wurde auf Basis eines blogpost by Kiran Koduru.

+0

Gibt es eine Möglichkeit, Crawler zum Reaktor hinzuzufügen, während er läuft? Wie kann dies getan werden reactor.run() blockiert? –

+1

Danke für den Kredit :) –

Verwandte Themen