2017-07-11 1 views
0

Je souhaite interroger de façon asynchrone une base de données pour les clés, puis envoyer des demandes à plusieurs adresses pour chaque clé.Base de données de requête asynchrone pour les clés à utiliser dans plusieurs requêtes

J'ai une fonction qui retourne un Deferred de la base de données dont la valeur est la clé pour plusieurs demandes. Idéalement, je voudrais appeler cette fonction et retourner un générateur de différés de start_requests.

@inlineCallbacks 
def get_request_deferred(self): 

    d = yield engine.execute(select([table])) # async 
    d.addCallback(make_url) 
    d.addCallback(Request) 
    return d 

def start_requests(self): 
    ???? 

Mais tenter de plusieurs façons soulève

builtins.AttributeError: 'Deferred' object has no attribute 'dont_filter' 

que je considère dire que start_requests doit retourner Request objets, non Deferreds dont les valeurs sont Request objets. La même chose semble être vraie pour le middleware de l'araignée process_start_requests().

Alternativement, je peux faire des demandes initiales à, par exemple, http://localhost/ et les changer pour l'url réel une fois que la clé est disponible à partir de la base de données par l'intermédiaire du middleware process_request() du téléchargeur. Toutefois, process_request renvoie uniquement un objet Request; il ne peut pas donner des demandes de plusieurs pages en utilisant la clé: la tentative yield Request(url) soulève

AssertionError: Middleware myDownloaderMiddleware.process_request 
must return None, Response or Request, got generator 

Quelle est la solution la plus propre à

  • obtenir pour chaque touche du clavier en mode asynchrone base de données
  • , générer plusieurs demandes
+0

Jetez un oeil http://techspot.zzzeek.org/2015/02/15/asynchronous-python-and-databases/ – MishaVacic

Répondre

0

Vous pouvez laisser le rappel pour l'objet différé passer les URL à un générateur de quelque sorte. Le générateur convertira alors toutes les URLs reçues en objets Request scrapy et les cédera. Ci-dessous un exemple en utilisant le code lié (non testé):

import scrapy 
from Queue import Queue 
from pdb import set_trace as st 
from twisted.internet.defer import Deferred, inlineCallbacks 


class ExampleSpider(scrapy.Spider): 
    name = 'example' 

    def __init__(self): 
     self.urls = Queue() 
     self.stop = False 
     self.requests = request_generator() 
     self.deferred = deferred_generator() 

    def deferred_generator(self): 
     d = Deferred() 
     d.addCallback(self.deferred_callback) 
     yield d 

    def request_generator(self): 
     while not self.stop: 
      url = self.urls.get() 
      yield scrapy.Request(url=url, callback=self.parse) 

    def start_requests(self): 
     return self.requests.next() 

    def parse(self, response): 
     st() 

     # when you need to parse the next url from the callback 
     yield self.requests.next() 

    @static_method 
    def deferred_callback(url): 
     self.urls.put(url) 
     if no_more_urls(): 
      self.stop = True 

Ne pas oublier d'arrêter le générateur de demande lorsque vous avez terminé.

+0

Je ne suis pas sûr de comprendre ce que vous voulez dire. Ce code https://dpaste.de/LpvQ dit 'l'objet de générateur n'a aucun attribut 'dont_filter'' qui est le même problème -' start_requests() 'doit donner des objets de demande, pas des générateurs. Il en va de même pour la boucle sur le générateur: https://dpaste.de/JoD5. Pouvez-vous clarifier? – Hatshepsut

+0

Je pense que je comprends ce que vous essayez de faire. J'ai mis à jour la réponse pour éclaircir les choses. – AJPennster

0

Vous n'avez fourni aucun cas d'utilisation pour les requêtes de base de données asynchrones. Je suppose que vous ne pouvez pas commencer à gratter vos URL, sauf si vous interrogez la base de données en premier? Si c'est le cas, il vaut mieux faire la requête de manière synchrone, parcourir les résultats de la requête, extraire ce dont vous avez besoin, puis générer des objets Request. Cela n'a aucun sens d'interroger une base de données de manière asynchrone et de rester assis en attendant que la requête se termine.

+0

C'est une file d'attente. Si j'avais tous les emplois en même temps, il n'y en aurait plus sur une deuxième connexion. Pour les éraflures de longue durée, obtenir un travail à la fois, asynchrone, est assez efficace, je pense. – Hatshepsut

+0

Je ne suis pas tout à fait sûr de ce que vous essayez de transmettre. Pouvez-vous fournir plus de contexte dans ce que votre problème est et éventuellement fournir plus de code que vous avez essayé –