J'ai un programme Python 2.7 qui extrait les données des sites Web et les enregistre dans une base de données. Il suit le modèle de producteur de consommation et est écrit en utilisant le module de filetage.Récupération de données avec l'asyncio de Python dans un ordre séquentiel
Juste pour le plaisir, je voudrais réécrire ce programme en utilisant le nouveau module asyncio (à partir de 3.4), mais je ne peux pas comprendre comment faire correctement.
L'exigence la plus cruciale est que le programme doit extraire les données du même site Web dans un ordre séquentiel. Par exemple, pour une url « http://a-restaurant.com » il faut d'abord obtenir « http://a-restaurant.com/menu/0 », puis « http://a-restaurant.com/menu/1 », puis« http://a-restaurant.com/menu/2 » ... Si elles ne sont pas récupérés pour le site Web cesse de pages livrer tout à fait et vous devez commencer à 0.
Cependant fetch pour un autre site (« http://another-restaurant.com ») peut (et doit) exécuter en même temps (les autres sites ont également la restriction sequantial).
Le module d'enfilage convient bien pour cela car je peux créer des threads séparés pour chaque site web et dans chaque thread il peut attendre jusqu'à ce qu'une page ait fini de charger avant d'aller en chercher une autre.
Voici un extrait de code grossièrement simplifiée de la version de filetage (Python 2.7):
class FetchThread(threading.Threading)
def __init__(self, queue, url)
self.queue = queue
self.baseurl = url
...
def run(self)
# Get 10 menu pages in a sequantial order
for food in range(10):
url = self.baseurl + '/' + str(food)
text = urllib2.urlopen(url).read()
self.queue.put(text)
...
def main()
queue = Queue.Queue()
urls = ('http://a-restaurant.com/menu', 'http://another-restaurant.com/menu')
for url in urls:
fetcher = FetchThread(queue, url)
fetcher.start()
...
Et voici comment j'ai essayé de le faire avec asyncio (3.4.1):
@asyncio.coroutine
def fetch(url):
response = yield from aiohttp.request('GET', url)
response = yield from response.read_and_close()
return response.decode('utf-8')
@asyncio.coroutine
def print_page(url):
page = yield from fetch(url)
print(page)
l = []
urls = ('http://a-restaurant.com/menu', 'http://another-restaurant.com/menu')
for url in urls:
for food in range(10):
menu_url = url + '/' + str(food)
l.append(print_page(menu_url))
loop.run_until_complete(asyncio.wait(l))
Et il récupère et imprime tout dans un ordre non séquentiel. Eh bien, je suppose que c'est l'idée de ces coroutines. Ne devrais-je pas utiliser aiohttp et juste aller chercher avec urllib? Mais est-ce que les récupérations pour le premier restaurant bloquent alors les récupérations pour les autres restaurants? Suis-je juste en train de penser que c'est complètement faux? (Ceci est juste un test pour essayer d'aller chercher les choses dans un ordre séquentiel.)
Merci @dano. Pour être clair: tous les restaurants ont besoin d'aller chercher séquentiellement dans leurs menus, mais je voudrais aller chercher les données du premier restaurant et du second restaurant en même temps (juste leurs fetchs de menu respectifs doivent être séquentiels). Donc je suppose que la solution est de 'l = [print_pages_sequential ('http://a-restaurant.com/menu', 10), print_pages_sequential ('http://another-restaurant.com/menu', 10)]' puis exécutez 'loop.run_until_complete (asyncio.wait (l))' (Impossible de tester cela maintenant.) – mat
@ user3313978 Ah, désolé, j'ai mal compris cette exigence. Votre hypothèse sur la solution étant donné que la contrainte est correcte. J'ai mis à jour ma réponse pour refléter la nouvelle contrainte. – dano
Cela ne démarre toujours pas les requêtes dans l'ordre, @dano. Malheureusement, 'gather' et' wait' programment toutes les coroutines passées dans un ordre non-déterministe car elles les enveloppent dans 'Task's. Voir [asyncio numéro 432] (https://github.com/python/asyncio/issues/432). Une solution de contournement consiste à attribuer manuellement une tâche de boucle à chacun de vos objets coroutine avant de les transmettre à 'gather' ou' wait'. par exemple. 'l.append (loop.create_task (print_page_sequential (menu_url, 10)))' ' –