2009-03-20 13 views
9

Ich habe einen web.py-Server, der auf verschiedene Benutzeranforderungen reagiert. Eine dieser Anfragen beinhaltet das Herunterladen und Analysieren einer Reihe von Webseiten.Python: einfacher async Download von URL-Inhalt?

Gibt es eine einfache Möglichkeit, einen asynchronen/Callback-basierten URL-Download-Mechanismus in web.py einzurichten? Geringe Ressourcennutzung ist besonders wichtig, da jede vom Benutzer initiierte Anfrage zum Herunterladen mehrerer Seiten führen kann.

Die Strömung würde wie folgt aussehen:

Anfrage Benutzer -> web.py -> Download 10 Seiten parallel oder asynchron -> Inhalte analysieren, Wiedereinarbeitungs

ich erkenne, dass Verdreht eine nette Weise sein würde, Um dies zu tun, aber ich bin bereits in web.py, so bin ich besonders interessiert an etwas, das in web.py passen kann.

Antwort

2

Verwenden Sie den asynchronen HTTP-Client, der asynchat und asyncore verwendet. http://sourceforge.net/projects/asynchttp/files/asynchttp-production/asynchttp.py-1.0/asynchttp.py/download

+0

Ich habe ein paar Fehlerkorrekturen im asynttpclient-Code. Ich habe versucht, den Autor zu mailen, aber er scheint nicht in der Nähe zu sein. Wenn Sie diese Korrekturen wünschen, können Sie mir eine E-Mail senden. Ich habe zusätzlich auch HTTP-Request-Pipelining aktiviert, was die Geschwindigkeit für viele kleinere Anfragen zusätzlich erhöhen sollte. – dhruvbird

+0

Die Bugfixes und Erweiterungen für den asynttpp-Client finden Sie hier: http://code.google.com/p/asynhttp/ – dhruvbird

0

Ich bin mir nicht sicher, ob ich Ihre Frage verstehe, daher gebe ich zunächst mehrere Teilantworten.

  • Wenn Ihr Anliegen ist, dass web.py von irgendwo zum Herunterladen von Daten ist mit und die Ergebnisse zu analysieren, bevor Sie reagieren, und Sie befürchten, dass der Antrag kann vor bereit sind, die Ergebnisse einer Zeitüberschreitung, Sie Ajax nutzen, um die Arbeit zu teilen oben. Kehren Sie sofort mit einer Containerseite (um die Ergebnisse zu speichern) und ein bisschen Javascript zurück, um den Server nach den Ergebnissen zu befragen, bis der Client sie alle hat. Somit wartet der Client niemals auf den Server, obwohl der Benutzer noch auf die Ergebnisse warten muss.
  • Wenn Ihr Anliegen den Server bindet, der darauf wartet, dass der Client die Ergebnisse erhält, bezweifle ich, dass das tatsächlich ein Problem sein wird. Ihre Netzwerkschichten sollten verlangen, dass Sie nicht warten-on-write
  • Wenn Sie über den Server sorgen sich warten, während der Client von anderswo statische Inhalte herunterlädt, entweder Ajax oder geschickten Einsatz von Umleitungen Ihr Problem
+0

Das Problem mit der Ajax-Lösung ist domänenübergreifende Beschränkungen - ich kann nicht Inhalte von Seiten nicht von der ursprünglichen Server. Btw, ich bin nicht besorgt über warten auf schreiben in diesem Fall , aber das ist tatsächlich ein Problem, das nicht durch die Netzwerkschicht behandelt wird. – Parand

+0

@Parand - Nein, aber Sie können einen billigen Durchgangs-Proxy in Ihrer Domäne einrichten und lassen Sie das durch. – MarkusQ

0

Entlang der Linien der Antwort von MarkusQ ist MochiKit eine nette JavaScript-Bibliothek, mit robusten asynchronen Methoden inspiriert von Twisted.

0

Eigentlich können Sie twisted mit web.py integrieren. Ich bin mir nicht wirklich sicher, wie ich es nur mit Django gemacht habe (verdreht damit).

4

Eine Möglichkeit wäre, die Arbeit auf eine Warteschlange von einer Art zu schreiben (Sie etwas enterprisey wie ActiveMQ mit pyactivemq oder STOMP als Stecker oder man könnte etwas leichtes wie Kestrel verwenden verwenden könnte, die in Scala geschrieben und spricht die gleiche Protocl als Memcache, so dass Sie einfach mit dem Python Memcache Client sprechen können).

Sobald Sie den Warteschlangenmechanismus eingerichtet haben, können Sie so viele oder so wenige Worker-Tasks erstellen, die für die Warteschlange abonniert sind, und die eigentliche Download-Arbeit wie gewünscht ausführen. Sie können sie sogar auf anderen Maschinen leben lassen, damit sie die Geschwindigkeit, mit der Sie Ihre Website bedienen, nicht beeinträchtigen. Wenn die Arbeiter fertig sind, senden sie die Ergebnisse zurück in die Datenbank oder in eine andere Warteschlange, in der der Webserver sie abholen kann.

Wenn Sie keine externen Worker-Prozesse verwalten möchten, können Sie die Worker-Threads im selben Python-Prozess wie den Webserver erstellen, aber dann hat dies natürlich größere Auswirkungen auf die Leistung Ihrer Webseiten-Portierung .

2

Ich würde nur einen Dienst in twisted, die das gleichzeitige abrufen und analysieren und den Zugriff von web.py als eine einfache http-Anfrage.

3

Sie könnten in der Lage sein urllib zu verwenden, um die Dateien herunterladen und die Queue Modul eine Anzahl von Worker-Threads zu verwalten. Beispiel:

import urllib 
from threading import Thread 
from Queue import Queue 

NUM_WORKERS = 20 

class Dnld: 
    def __init__(self): 
     self.Q = Queue() 
     for i in xrange(NUM_WORKERS): 
      t = Thread(target=self.worker) 
      t.setDaemon(True) 
      t.start() 

    def worker(self): 
     while 1: 
      url, Q = self.Q.get() 
      try: 
       f = urllib.urlopen(url) 
       Q.put(('ok', url, f.read())) 
       f.close() 
      except Exception, e: 
       Q.put(('error', url, e)) 
       try: f.close() # clean up 
       except: pass 

    def download_urls(self, L): 
     Q = Queue() # Create a second queue so the worker 
        # threads can send the data back again 
     for url in L: 
      # Add the URLs in `L` to be downloaded asynchronously 
      self.Q.put((url, Q)) 

     rtn = [] 
     for i in xrange(len(L)): 
      # Get the data as it arrives, raising 
      # any exceptions if they occur 
      status, url, data = Q.get() 
      if status == 'ok': 
       rtn.append((url, data)) 
      else: 
       raise data 
     return rtn 

inst = Dnld() 
for url, data in inst.download_urls(['http://www.google.com']*2): 
    print url, data 
6

Hier ist ein interessantes Stück Code. Ich habe es selbst nicht verwenden, aber es sieht gut aus;)

https://github.com/facebook/tornado/blob/master/tornado/httpclient.py

Low Level AsyncHTTPClient:

"Ein nicht-blockierenden HTTP-Client gesichert mit pycurl Beispiel Nutzung:."

import ioloop 

def handle_request(response): 
    if response.error: 
     print "Error:", response.error 
    else: 
     print response.body 
    ioloop.IOLoop.instance().stop() 

http_client = httpclient.AsyncHTTPClient() 
http_client.fetch("http://www.google.com/", handle_request) 
ioloop.IOLoop.instance().start() 

" holen() kann einen String URL nehmen oder eine Httprequest-Instanz, die mehr Optionen bietet, wie POST/Ausführen PUT/DELETE-Anfragen.

Das Schlüsselwortargument max_clients für den Konstruktor AsyncHTTPClient bestimmt die maximale Anzahl von simultanen fetch() - Vorgängen, die auf jedem IOLoop parallel ausgeführt werden können. "

Es gibt auch neue Implementierung in progress: https://github.com/facebook/tornado/blob/master/tornado/simple_httpclient.py " Non-Blocking-HTTP-Client ohne externe Abhängigkeiten. ... Diese Klasse noch in der Entwicklung ist und noch nicht für den produktiven Einsatz empfohlen „

2

Heutzutage gibt es ausgezeichnete Python Libs Sie verwenden möchten, könnten -. urllib3 (verwendet Thread-Pools) und requests (verwendet Thread-Pools durch urllib3 oder nicht Blockieren IO durch