2015-06-21 38 views
5

mam agregowania danych dziennych z Scrapy stosując indeksowanie dwuetapowy. Pierwszy etap generuje listę adresów URL ze strony indeksu, a drugi etap zapisuje kod HTML, dla każdego z adresów URL na liście, do tematu Kafki.Scrapy `ReactorNotRestartable`: jedna klasa uruchomić dwie (lub więcej) pająki

kafka cluster for Scrapy crawler

Chociaż oba składniki indeksowania są powiązane, chciałbym, żeby być niezależnym: the url_generator będzie działać jako zaplanowane zadanie raz dziennie, a page_requester byłoby nieustannie, przetwarzanie URL kiedy dostępny. Aby być "uprzejmym", dostosuję DOWNLOAD_DELAY, tak aby robot mógł się dobrze zakończyć w ciągu 24 godzin, ale minimalnie obciążył witrynę.

stworzyłem CrawlerRunner klasę, która posiada funkcje do generowania adresów URL i odzyskać HTML:

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() 

Kiedy instancję klasy, jestem w stanie skutecznie wykonywać zarówno funkcję na własną rękę, ale niestety Jestem w stanie wykonać je razem:

from crawl.somesite import crawler_runner 

cr = crawler_runner.CrawlerRunner() 

cr.create_urls() 
cr.crawl_urls() 

drugie wywołanie funkcji generuje twisted.internet.error.ReactorNotRestartable kiedy próbuje wykonać reactor.run() w funkcji crawl_urls.

Zastanawiam się, czy istnieje łatwa poprawka dla tego kodu (np. Jakiś sposób na uruchomienie dwóch oddzielnych reaktorów Twisted), lub jeśli istnieje lepszy sposób na uporządkowanie tego projektu.

Odpowiedz

6

można było uruchamiać wiele pająków w ciągu jednego reaktora utrzymując reaktor otwarty aż wszystkie pająki nie są już wyświetlane. Cel ten został osiągnięty poprzez utrzymywanie listę wszystkich uruchomionych pająków i nie wykonując reactor.stop() dopóki ta lista jest pusta:

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() 

Klasa jest wykonywany:

from crawl.somesite.crawl import CrawlRunner 

cr = CrawlRunner() 
cr.run() 

To rozwiązanie zostało oparte na blogpost by Kiran Koduru.

+0

Czy istnieje sposób, aby dodać roboty do reaktora, gdy jest uruchomiony? Jak to zrobić, że program reactor.run() jest blokowany? –

+1

Dzięki za zasługę :) –