331 votes

Quel est le moyen le plus rapide d'envoyer 100 000 requêtes HTTP en Python?

Je vais ouvrir un fichier qui a 100 000 url. J'ai besoin d'envoyer une requête http à chaque url et imprimer le code d'état. Je suis à l'aide de la version 2.6 de Python, et jusqu'à présent, regarda les nombreux confusion des moyens Python met en œuvre threading/simultanéité. J'ai même regardé le python assentiment de la bibliothèque, mais ne peut pas comprendre comment écrire ce programme correctement. Quelqu'un a rencontré un problème similaire? Je suppose que généralement j'ai besoin de savoir comment effectuer des milliers de tâches en Python aussi vite que possible - je suppose que cela signifie 'simultanément'.

Merci, Igor

221voto

Tarnay Kálmán Points 2692

Solution sans torsion:

 from urlparse import urlparse
from threading import Thread
import httplib, sys
from Queue import Queue

concurrent = 200

def doWork():
    while True:
        url = q.get()
        status, url = getStatus(url)
        doSomethingWithResult(status, url)
        q.task_done()

def getStatus(ourl):
    try:
        url = urlparse(ourl)
        conn = httplib.HTTPConnection(url.netloc)   
        conn.request("HEAD", url.path)
        res = conn.getresponse()
        return res.status, ourl
    except:
        return "error", ourl

def doSomethingWithResult(status, url):
    print status, url

q = Queue(concurrent * 2)
for i in range(concurrent):
    t = Thread(target=doWork)
    t.daemon = True
    t.start()
try:
    for url in open('urllist.txt'):
        q.put(url.strip())
    q.join()
except KeyboardInterrupt:
    sys.exit(1)
 

Celui-ci est légèrement plus rapide que la solution tordue et utilise moins de CPU.

57voto

0x00mh Points 2696

Une solution utilisant la bibliothèque réseau asynchrone tornado

 from tornado import ioloop, httpclient

i = 0

def handle_request(response):
    print(response.code)
    global i
    i -= 1
    if i == 0:
        ioloop.IOLoop.instance().stop()

http_client = httpclient.AsyncHTTPClient()
for url in open('urls.txt'):
    i += 1
    http_client.fetch(url.strip(), handle_request, method='HEAD')
ioloop.IOLoop.instance().start()
 

44voto

ironfroggy Points 3496

Les threads ne sont absolument pas la solution ici. Ils fourniront à la fois des goulots d'étranglement au niveau du processus et du noyau, ainsi que des limites de débit inacceptables si l'objectif global est "le moyen le plus rapide".

Un peu de twisted et son client asynchrone HTTP vous donneraient de bien meilleurs résultats.

17voto

singingwolfboy Points 1358

Utilisez la bibliothèque de requêtes : elle prend en charge de manière native l' envoi simultané de plusieurs requêtes HTTP à l' aide de gevent. Simple, facile, puissant!

8voto

Erik Garrison Points 503

Une bonne approche pour résoudre ce problème est d'abord écrire le code nécessaire pour obtenir un résultat, puis incorporer le filetage code de parallélisation de l'application.

Dans un monde parfait, cela signifie simplement simultanément à partir de 100 000 fils de sortie de leurs résultats dans un dictionnaire ou d'une liste pour un traitement ultérieur, mais dans la pratique, vous êtes limité dans le nombre parallèle des requêtes HTTP, vous pouvez émettre dans ce mode. Localement, vous avez des limites dans la façon dont de nombreux sockets vous pouvez ouvrir simultanément, le nombre de threads d'exécution de votre interpréteur Python. À distance, vous pouvez être limité dans le nombre de connexions simultanées si toutes les demandes sont contre un serveur ou à plusieurs. Ces limitations ne seront probablement nécessaire que vous écrivez le script de telle manière que seul le sondage d'une petite fraction de l'Url à la fois (100, comme une autre affiche mentionné, est probablement un décent taille du pool de threads, même si vous pouvez trouver que vous pouvez déployer avec succès beaucoup plus).

Vous pouvez suivre ce modèle pour résoudre le problème ci-dessus:

  1. Démarrer un thread qui lance une nouvelle demande de threads jusqu'à ce que le nombre de threads en cours d'exécution (vous pouvez les suivre via le filetage.active_count() ou en appuyant sur le thread des objets dans une structure de données) est >= à votre nombre maximal de requêtes simultanées (disons 100), puis dort pendant un court délai. Ce fil devrait s'arrêter lorsqu'il n'y a plus aucune Url de processus. Ainsi, le fil se garder de se réveiller, le lancement de nouveaux threads, et dormir jusqu'à ce que votre sont finis.
  2. Avoir la demande de threads stocker leurs résultats dans certaines de structure de données pour les réutiliser plus tard et de sortie. Si la structure que vous stocker les résultats dans un list ou dict dans Disponible, vous pouvez en toute sécurité à ajouter ou insérer des objets uniques à partir de votre fils sans verrous, mais si vous écrivez à un fichier ou d'exiger en plus complexe de la croix-fil des données d'interaction , vous devez utiliser un verrou d'exclusion mutuelle pour protéger cet état de corruption.

Je vous suggère d'utiliser le filetage du module. Vous pouvez l'utiliser pour lancer et de suivre les threads en cours d'exécution. Python est en charge des threads est nue, mais la description de votre problème suggère qu'il est tout à fait suffisant pour vos besoins.

Enfin, si vous désirez voir une simple application d'un réseau parallèle application écrite en Python, découvrez ssh.py. C'est une petite bibliothèque qui utilise Python filetage pour paralléliser les nombreuses connexions SSH. Le design est assez proche de vos exigences, que vous trouverez peut-être une bonne ressource.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X