web-dev-qa-db-de.com

Scrapy - Reaktor nicht neu startbar

mit:

from twisted.internet import reactor
from scrapy.crawler import CrawlerProcess

Ich habe diesen Prozess immer erfolgreich durchgeführt:

process = CrawlerProcess(get_project_settings())
process.crawl(*args)
# the script will block here until the crawling is finished
process.start() 

aber da ich diesen Code in eine web_crawler(self) Funktion verschoben habe, so:

def web_crawler(self):
    # set up a crawler
    process = CrawlerProcess(get_project_settings())
    process.crawl(*args)
    # the script will block here until the crawling is finished
    process.start() 

    # (...)

    return (result1, result2) 

und begann die Methode mit Klasseninstanziierung aufzurufen, wie:

def __call__(self):
    results1 = test.web_crawler()[1]
    results2 = test.web_crawler()[0]

und läuft:

test()

Ich erhalte folgende Fehlermeldung:

Traceback (most recent call last):
  File "test.py", line 573, in <module>
    print (test())
  File "test.py", line 530, in __call__
    artists = test.web_crawler()
  File "test.py", line 438, in web_crawler
    process.start() 
  File "/Library/Python/2.7/site-packages/scrapy/crawler.py", line 280, in start
    reactor.run(installSignalHandlers=False)  # blocking call
  File "/Library/Python/2.7/site-packages/twisted/internet/base.py", line 1194, in run
    self.startRunning(installSignalHandlers=installSignalHandlers)
  File "/Library/Python/2.7/site-packages/twisted/internet/base.py", line 1174, in startRunning
    ReactorBase.startRunning(self)
  File "/Library/Python/2.7/site-packages/twisted/internet/base.py", line 684, in startRunning
    raise error.ReactorNotRestartable()
twisted.internet.error.ReactorNotRestartable

was ist falsch?

8
data_garden

Sie können den Reaktor nicht erneut starten, aber Sie sollten ihn mehrmals ausführen können, indem Sie einen separaten Prozess fälschen:

import scrapy
import scrapy.crawler as crawler
from multiprocessing import Process, Queue
from twisted.internet import reactor

# your spider
class QuotesSpider(scrapy.Spider):
    name = "quotes"
    start_urls = ['http://quotes.toscrape.com/tag/humor/']

    def parse(self, response):
        for quote in response.css('div.quote'):
            print(quote.css('span.text::text').extract_first())


# the wrapper to make it run more times
def run_spider(spider):
    def f(q):
        try:
            runner = crawler.CrawlerRunner()
            deferred = runner.crawl(spider)
            deferred.addBoth(lambda _: reactor.stop())
            reactor.run()
            q.put(None)
        except Exception as e:
            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

Führen Sie es zweimal aus:

print('first run:')
run_spider(QuotesSpider)

print('\nsecond run:')
run_spider(QuotesSpider)

Ergebnis:

first run:
“The person, be it gentleman or lady, who has not pleasure in a good novel, must be intolerably stupid.”
“A day without sunshine is like, you know, night.”
...

second run:
“The person, be it gentleman or lady, who has not pleasure in a good novel, must be intolerably stupid.”
“A day without sunshine is like, you know, night.”
...
12
Ferrard

Dies hat mir geholfen, den Kampf gegen ReactorNotRestartable-Fehler zu gewinnen: letzte Antwort des Autors der Frage
0) pip install crochet
1) import from crochet import setup
2) setup() - am Anfang der Datei
3) 2 Zeilen entfernen:
a) d.addBoth(lambda _: reactor.stop())
b) reactor.run()

Ich hatte das gleiche Problem mit diesem Fehler und verbringe mehr als 4 Stunden damit, dieses Problem zu lösen. Lesen Sie hier alle Fragen dazu. Endlich gefunden - und geteilt. So habe ich das gelöst. Die einzigen bedeutungsvollen Zeilen von Scrapy docs links sind 2 letzte Zeilen in diesem my-Code:

#some more imports
from crochet import setup
setup()

def run_spider(spiderName):
    module_name="first_scrapy.spiders.{}".format(spiderName)
    scrapy_var = import_module(module_name)   #do some dynamic import of selected spider   
    spiderObj=scrapy_var.mySpider()           #get mySpider-object from spider module
    crawler = CrawlerRunner(get_project_settings())   #from Scrapy docs
    crawler.crawl(spiderObj)                          #from Scrapy docs

Mit diesem Code kann ich auswählen, welche Spinne ausgeführt werden soll, und zwar nur mit dem Namen, der an die Funktion run_spider Übergeben wurde. Nach dem Verschrotten muss eine andere Spinne ausgewählt und erneut ausgeführt werden.
Hoffe das hilft jemandem, wie es mir geholfen hat :)

9
Chiefir

Gemäß der Scrapy-Dokumentation führt die start()-Methode der CrawlerProcess-Klasse Folgendes aus:

"[...] startet einen Twisted-Reaktor, passt seine Poolgröße an REACTOR_THREADPOOL_MAXSIZE an und installiert einen DNS-Cache basierend auf DNSCACHE_ENABLED und DNSCACHE_SIZE."

Der Fehler, den Sie erhalten, wird von Twisted ausgelöst, da ein Twisted-Reaktor nicht neu gestartet werden kann. Es verwendet eine Unmenge von Globals, und selbst wenn Sie mit Jimmy-Rig eine Art Code ausführen, um es neu zu starten (ich habe gesehen, dass es erledigt wurde), gibt es keine Garantie, dass es funktionieren wird. 

Wenn Sie glauben, dass Sie den Reaktor neu starten müssen, machen Sie wahrscheinlich etwas falsch.

Abhängig davon, was Sie tun möchten, würde ich auch den Running Scrapy von einem Skript Teil der Dokumentation aus überprüfen.

1
Rejected

Der Fehler liegt in diesem Code:

def __call__(self):
    result1 = test.web_crawler()[1]
    result2 = test.web_crawler()[0] # here

web_crawler() gibt zwei Ergebnisse zurück und versucht zu diesem Zweck, den Prozess zweimal zu starten, und den Reactor neu zu starten, wie von @Rejected gezeigt.

hier erhalten Sie die Ergebnisse eines einzigen Prozesses und das Speichern beider Ergebnisse in einem Tuple.

def __call__(self):
    result1, result2 = test.web_crawler()
1
data_garden

Dies hat mein Problem gelöst, unter reactor.run() oder process.start() unter dem folgenden Code:

time.sleep(0.5)

os.execl(sys.executable, sys.executable, *sys.argv)
0
Neeraj Yadav

Einige Leute wiesen bereits darauf hin: Sie sollten den Reaktor nicht neu starten müssen.

Wenn Sie Ihre Prozesse verketten möchten (crawl1 dann crawl2 dann crawl3), fügen Sie einfach Callbacks hinzu.

Zum Beispiel habe ich diese Schleifenspinne verwendet, die diesem Muster folgt:

1. Crawl A
2. Sleep N
3. goto 1

Und so sieht es in Scrapy aus:

import time

from scrapy.crawler import CrawlerRunner
from scrapy.utils.project import get_project_settings
from twisted.internet import reactor

class HttpbinSpider(scrapy.Spider):
    name = 'httpbin'
    allowed_domains = ['httpbin.org']
    start_urls = ['http://httpbin.org/ip']

    def parse(self, response):
        print(response.body)

def sleep(_, duration=5):
    print(f'sleeping for: {duration}')
    time.sleep(duration)  # block here


def crawl(runner):
    d = runner.crawl(HttpbinSpider)
    d.addBoth(sleep)
    d.addBoth(lambda _: crawl(runner))
    return d


def loop_crawl():
    runner = CrawlerRunner(get_project_settings())
    crawl(runner)
    reactor.run()


if __== '__main__':
    loop_crawl()

Um den Prozess genauer zu erläutern, plant die Funktion crawl eine Durchforstung und fügt zwei zusätzliche Rückrufe hinzu, die nach dem Crawlen aufgerufen werden.

$ python endless_crawl.py 
b'{\n  "Origin": "000.000.000.000"\n}\n'
sleeping for: 5
b'{\n  "Origin": "000.000.000.000"\n}\n'
sleeping for: 5
b'{\n  "Origin": "000.000.000.000"\n}\n'
sleeping for: 5
b'{\n  "Origin": "000.000.000.000"\n}\n'
sleeping for: 5
0
Granitosaurus