2017-06-27 5 views
0

Avant que vous me liez à d'autres réponses liées à ceci, notez que je les ai lues et suis encore un peu confus. Très bien, nous y voilà. Donc je crée une webapp dans Django. J'importe la nouvelle bibliothèque scrapy pour explorer un site Web. Je n'utilise pas de céleri (j'en connais très peu, mais je l'ai vu dans d'autres sujets liés à cela).ReactorNotRestartable - Twisted et scrapy

L'une des URL de notre site Web,/crawl /, est destinée à démarrer le moteur d'exploration. C'est la seule URL de notre site qui nécessite un scrapy à utiliser. Voici la fonction qui est appelée lorsque l'URL est visité:

def crawl(request): 
    configure_logging({'LOG_FORMAT': '%(levelname)s: %(message)s'}) 
    runner = CrawlerRunner() 

    d = runner.crawl(ReviewSpider) 
    d.addBoth(lambda _: reactor.stop()) 
    reactor.run() # the script will block here until the crawling is finished 

    return render(request, 'index.html') 

Vous remarquerez que c'est une adaptation du tutoriel scrapy sur leur site web. La première fois que cette URL est visitée lorsque le serveur démarre, tout fonctionne comme prévu. La deuxième fois et plus loin, une exception ReactorNotRestartable est levée. Je comprends que cette exception se produit lorsqu'un réacteur qui a déjà été arrêté reçoit une commande pour recommencer, ce qui n'est pas possible.

En regardant l'exemple de code, je suppose que la ligne "runner = CrawlerRunner()" retournerait un ~ nouveau ~ réacteur à utiliser chaque fois que cette URL est visitée. Mais je crois que ma compréhension des réacteurs tordus n'est peut-être pas tout à fait claire.

Comment puis-je obtenir et faire fonctionner un nouveau réacteur chaque fois que cette url est visitée?

Merci beaucoup

Répondre

0

D'une manière générale, vous ne pouvez pas avoir un nouveau réacteur. Il y en a un global. C'est clairement une erreur et peut-être que cela sera corrigé à l'avenir, mais c'est l'état actuel des choses.

Vous pouvez utiliser Crochet pour gérer un seul réacteur en cours d'exécution (pour la durée de vie de l'ensemble de votre processus - ne pas démarrer et s'arrêter de manière répétée) dans un thread distinct.

Tenir compte the example from the Crochet docs:

#!/usr/bin/python 
""" 
Do a DNS lookup using Twisted's APIs. 
""" 
from __future__ import print_function 

# The Twisted code we'll be using: 
from twisted.names import client 

from crochet import setup, wait_for 
setup() 


# Crochet layer, wrapping Twisted's DNS library in a blocking call. 
@wait_for(timeout=5.0) 
def gethostbyname(name): 
    """Lookup the IP of a given hostname. 

    Unlike socket.gethostbyname() which can take an arbitrary amount of time 
    to finish, this function will raise crochet.TimeoutError if more than 5 
    seconds elapse without an answer being received. 
    """ 
    d = client.lookupAddress(name) 
    d.addCallback(lambda result: result[0][0].payload.dottedQuad()) 
    return d 


if __name__ == '__main__': 
    # Application code using the public API - notice it works in a normal 
    # blocking manner, with no event loop visible: 
    import sys 
    name = sys.argv[1] 
    ip = gethostbyname(name) 
    print(name, "->", ip) 

Cela vous donne une fonction gethostbyname de blocage qui est mis en œuvre à l'aide des API Twisted. La mise en œuvre utilise twisted.names.client qui repose simplement sur la possibilité d'importer le réacteur global.

Notez qu'il n'y a pas d'appel reactor.run ou reactor.stop - juste l'appel Crochet setup.

+0

Mais comment le ferais-je dans le cas d'un projet django? Comment faire pour que le démarrage du réacteur sur le site Web commence et se termine sur le site Web fermé? Et comment puis-je le référencer plus tard chaque fois que le crawler doit fonctionner? –

+0

Pour répondre à votre première question, c'est ce que fait Crochet. :) La réponse à la deuxième partie pourrait prendre plusieurs formes - peut-être créer un objet qui a une référence au réacteur ou peut-être simplement compter sur l'importation du réacteur global en vous donnant toujours le même réacteur. –

+2

Il semble que ce fut trompeusement simple. Espérons que c'était vraiment simple. J'ai simplement mis en commentaire ces lignes: 'd.addBoth (lambda _: reactor.stop()) reactor.run()' Et ajouté l'appel d'importation et d'installation en haut du fichier. Et cela semble fonctionner correctement. Je n'ai pas une très bonne compréhension des réacteurs et autres, alors j'espère qu'il n'y a pas quelque chose qui me manque. Merci bien! –