Frage

Ich habe ein Skript, das mehrere Webseiten und analysiert die Informationen abruft.

(Ein Beispiel finden Sie unter http://bluedevilbooks.com/search/ zu sehen ? DEPT = MATH & CLASS = 103 & SEC = 01 )

Ich lief cProfile auf, und als ich davon ausgegangen, nimmt urlopen viel Zeit in Anspruch. Gibt es eine Möglichkeit, die Seiten schneller zu holen? Oder eine Möglichkeit, mehrere Seiten zu holen auf einmal? Ich werde tun, was am einfachsten ist, wie ich bin neu zu Python und Web-Entwicklung.

Vielen Dank im Voraus! :)

UPDATE: Ich habe eine Funktion namens fetchURLs(), die ich eine Reihe von URLs ich Notwendigkeit zu machen verwenden, um so etwas wie urls = fetchURLS().The URLS sind alle XML-Dateien von Amazon und eBay-APIs (die mich verwirrt, warum es so lange dauert, um Last, vielleicht mein Webhost langsam?)

Was ich tun muß, ist jede URL geladen werden, jede Seite gelesen, und die Daten an einem anderen Teil des Skripts senden, die die Daten analysieren und anzuzeigen.

Beachten Sie, dass ich nicht den letzten Teil bis alle Seiten abgerufen wurden tun, das ist, was mein Problem ist.

Auch mein Gast Grenzen mich zu 25 Prozessen zu einer Zeit, glaube ich, so was am einfachsten auf dem Server wäre schön:)


Hier ist es für Zeit:

Sun Aug 15 20:51:22 2010    prof

         211352 function calls (209292 primitive calls) in 22.254 CPU seconds

   Ordered by: internal time
   List reduced from 404 to 10 due to restriction <10>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       10   18.056    1.806   18.056    1.806 {_socket.getaddrinfo}
     4991    2.730    0.001    2.730    0.001 {method 'recv' of '_socket.socket' objects}
       10    0.490    0.049    0.490    0.049 {method 'connect' of '_socket.socket' objects}
     2415    0.079    0.000    0.079    0.000 {method 'translate' of 'unicode' objects}
       12    0.061    0.005    0.745    0.062 /usr/local/lib/python2.6/HTMLParser.py:132(goahead)
     3428    0.060    0.000    0.202    0.000 /usr/local/lib/python2.6/site-packages/BeautifulSoup.py:1306(endData)
     1698    0.055    0.000    0.068    0.000 /usr/local/lib/python2.6/site-packages/BeautifulSoup.py:1351(_smartPop)
     4125    0.053    0.000    0.056    0.000 /usr/local/lib/python2.6/site-packages/BeautifulSoup.py:118(setup)
     1698    0.042    0.000    0.358    0.000 /usr/local/lib/python2.6/HTMLParser.py:224(parse_starttag)
     1698    0.042    0.000    0.275    0.000 /usr/local/lib/python2.6/site-packages/BeautifulSoup.py:1397(unknown_starttag)
War es hilfreich?

Lösung

Bearbeiten : Ich bin die Antwort expandierende ein etwas besseres Beispiel aufzunehmen. Ich habe viel Feindseligkeit und Fehlinformationen in diesem Beitrag in Bezug auf Threading v.s. gefunden async I / O. Deshalb Hinzufügen ich mehr Argument auch bestimmte ungültige Behauptung zu widerlegen. Ich hoffe, dies wird den Menschen helfen, das richtige Werkzeug für den richtigen Job zu wählen.

Dies ist ein dup auf eine Frage vor 3 Tagen.

Python urllib2.open langsam ist, müssen einen besseren Weg, um mehrere URLs zu lesen - Stapelüberlauf    Python urllib2 .urlopen () ist langsam, brauchen eine bessere Art und Weise mehrere Urls

zu lesen

Ich bin Polier den Code zu zeigen, wie mehrere Webseiten parallel mit Threads zu holen.

import time
import threading
import Queue

# utility - spawn a thread to execute target for each args
def run_parallel_in_threads(target, args_list):
    result = Queue.Queue()
    # wrapper to collect return value in a Queue
    def task_wrapper(*args):
        result.put(target(*args))
    threads = [threading.Thread(target=task_wrapper, args=args) for args in args_list]
    for t in threads:
        t.start()
    for t in threads:
        t.join()
    return result

def dummy_task(n):
    for i in xrange(n):
        time.sleep(0.1)
    return n

# below is the application code
urls = [
    ('http://www.google.com/',),
    ('http://www.lycos.com/',),
    ('http://www.bing.com/',),
    ('http://www.altavista.com/',),
    ('http://achewood.com/',),
]

def fetch(url):
    return urllib2.urlopen(url).read()

run_parallel_in_threads(fetch, urls)

Wie Sie sehen können, die anwendungsspezifische Code hat nur 3 Zeilen, die in 1 Zeile ausgeblendet werden können, wenn Sie aggressiv sind. Ich glaube nicht, dass jemand ihre Behauptung rechtfertigen, dass dies ist komplex und wartbaren.

Leider sind die meisten anderen Threading-Code hier gepostet hat einige Mängel. Viele von ihnen tun aktiven Polling für den Code bis zum Ende zu warten. join() ist ein besserer Weg, um den Code zu synchronisieren. Ich denke, dass dieser Code auf alle Threading Beispiele verbessert hat bisher.

Keep-Alive-Verbindung

Wolph Vorschlag über Keep-Alive-Verbindung sein könnte sehr nützlich, wenn alle URLs auf den gleichen Server zeigen.

verdreht

Aaron Gallagher ist ein Fan von twisted Rahmen und er ist feindlich alle Menschen, den Thread vorschlagen. Leider sind viele seiner Ansprüche Fehlinformationen. Zum Beispiel sagte er, „-1 für Threads darauf hindeutet Dies ist IO-gebunden;.. Themen sind hier nutzlos“ Dies im Gegensatz zu Beweisen, da beide Nick T und ich habe von dem mit Gewinde demonstriert Geschwindigkeitsgewinn. In der Tat hat I / O-gebundene Anwendung den am meisten von Python Thread zu gewinnen (V. S. keine Verstärkung in CPU gebundener Anwendung). Aarons verfehlten Kritik an Faden zeigt er ziemlich verwirrt über die parallele Programmierung im Allgemeinen.

richtiges Werkzeug für den richtigen Job

Ich bin mir sehr wohl bewusst, die Themen betreffen Programmierung mit Threads, Python parallel async I / O und so weiter. Jedes Werkzeug hat ihre Vor- und Nachteile. Für jede Situation gibt es ein geeignetes Instrument. Ich bin nicht gegen verdreht (obwohl ich nicht ein, mich eingesetzt haben). Aber ich glaube nicht, können wir flach sagen, dass Faden ist schlecht und verdreht ist gut in allen Situationen.

Zum Beispiel, wenn die Anforderung des OP ist 10.000 Website parallel zu holen, asynchroner I / O prefereable sein wird. Threading nicht appropriable (es sei denn vielleicht mit stackless Python).

Aarons gegen Fäden sind meist Verallgemeinerungen. Er nicht erkennen, dass dies eine triviale Parallelisierung Aufgabe. Jede Aufgabe ist unabhängig und keine Ressourcen teilen. So sind die meisten seines Angriffs nicht gelten.

Da mein Code hat keine externe Abhängigkeit, werde ich es nennen richtige Werkzeug für den richtigen Job.

Performance

Ich denke, die meisten Menschen, dass die Leistung dieser Aufgabe zustimmen würde, ist weitgehend von der Netzwerk-Code und dem externen Server, wo die Leistung der Plattform Code sollte einen vernachlässigbaren Effekt haben. Doch Aarons Benchmark zeigt einen 50% Geschwindigkeitsgewinn gegenüber dem Gewindecode. Ich denke, es zu Reaktion auf diese scheinbare Geschwindigkeitsverstärkung notwendig ist.

In Nicks Code, gibt es einen offensichtlichen Fehler, die Ineffizienz verursacht. Aber wie erklären Sie sich den 233ms Geschwindigkeitsgewinn über meinen Code? Ich glaube sogar, verdrehte Fans aus Springen in Schlussfolgerung verzichten wird dies auf die Effizienz der verdrehten zuzuschreiben. Es gibt immerhin eine riesige Menge an Variable außerhalb des Systemcodes, wie die Leistung des Remote-Servers, Netzwerk, Caching und Differenz Implementierung zwischen urllib2 und Twisted Web-Client und so weiter.

Nur um sicher Einfädeln des Python zu machen keinet incur eine riesige Menge an Ineffizienz, kann ich eine schnelle Benchmark 5 Threads und dann 500 Threads erstellen. Ich bin recht komfortabel die Overhead zu sagen Laich 5 Faden vernachlässigbar ist und kann die 233ms Geschwindigkeitsdifferenz nicht erklären.

In [274]: %time run_parallel_in_threads(dummy_task, [(0,)]*5)
CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
Wall time: 0.00 s
Out[275]: <Queue.Queue instance at 0x038B2878>

In [276]: %time run_parallel_in_threads(dummy_task, [(0,)]*500)
CPU times: user 0.16 s, sys: 0.00 s, total: 0.16 s
Wall time: 0.16 s

In [278]: %time run_parallel_in_threads(dummy_task, [(10,)]*500)
CPU times: user 1.13 s, sys: 0.00 s, total: 1.13 s
Wall time: 1.13 s       <<<<<<<< This means 0.13s of overhead

Weitere Tests auf meinem parallel fetching zeigt eine große Variabilität in der Reaktionszeit in 17 verläuft. (Leider habe ich nicht verdreht Aarons Code zu überprüfen).

0.75 s
0.38 s
0.59 s
0.38 s
0.62 s
1.50 s
0.49 s
0.36 s
0.95 s
0.43 s
0.61 s
0.81 s
0.46 s
1.21 s
2.87 s
1.04 s
1.72 s

Meine Prüfung nicht unterstützt Aarons Schluss, dass threading langsamer ist konsequent als asynchrones I / O durch eine meßbare Marge. Bedenkt man die Anzahl der Variablen, muss ich sagen, dass dies kein gültiger Test ist die systematische Leistungsunterschied zwischen asynchroner I / O und Einfädeln zu messen.

Andere Tipps

Verwenden Sie verdrehten ! Es macht diese Art der Sache absurd einfach im Vergleich zu, sagen wir, Threads.

from twisted.internet import defer, reactor
from twisted.web.client import getPage
import time

def processPage(page, url):
    # do somewthing here.
    return url, len(page)

def printResults(result):
    for success, value in result:
        if success:
            print 'Success:', value
        else:
            print 'Failure:', value.getErrorMessage()

def printDelta(_, start):
    delta = time.time() - start
    print 'ran in %0.3fs' % (delta,)
    return delta

urls = [
    'http://www.google.com/',
    'http://www.lycos.com/',
    'http://www.bing.com/',
    'http://www.altavista.com/',
    'http://achewood.com/',
]

def fetchURLs():
    callbacks = []
    for url in urls:
        d = getPage(url)
        d.addCallback(processPage, url)
        callbacks.append(d)

    callbacks = defer.DeferredList(callbacks)
    callbacks.addCallback(printResults)
    return callbacks

@defer.inlineCallbacks
def main():
    times = []
    for x in xrange(5):
        d = fetchURLs()
        d.addCallback(printDelta, time.time())
        times.append((yield d))
    print 'avg time: %0.3fs' % (sum(times) / len(times),)

reactor.callWhenRunning(main)
reactor.run()

Dieser Code auch besser abschneidet als alle anderen Lösungen gepostet (herausgegeben, nachdem ich einige Dinge geschlossen, die viel Bandbreite bewertet wurden):

Success: ('http://www.google.com/', 8135)
Success: ('http://www.lycos.com/', 29996)
Success: ('http://www.bing.com/', 28611)
Success: ('http://www.altavista.com/', 8378)
Success: ('http://achewood.com/', 15043)
ran in 0.518s
Success: ('http://www.google.com/', 8135)
Success: ('http://www.lycos.com/', 30349)
Success: ('http://www.bing.com/', 28611)
Success: ('http://www.altavista.com/', 8378)
Success: ('http://achewood.com/', 15043)
ran in 0.461s
Success: ('http://www.google.com/', 8135)
Success: ('http://www.lycos.com/', 30033)
Success: ('http://www.bing.com/', 28611)
Success: ('http://www.altavista.com/', 8378)
Success: ('http://achewood.com/', 15043)
ran in 0.435s
Success: ('http://www.google.com/', 8117)
Success: ('http://www.lycos.com/', 30349)
Success: ('http://www.bing.com/', 28611)
Success: ('http://www.altavista.com/', 8378)
Success: ('http://achewood.com/', 15043)
ran in 0.449s
Success: ('http://www.google.com/', 8135)
Success: ('http://www.lycos.com/', 30349)
Success: ('http://www.bing.com/', 28611)
Success: ('http://www.altavista.com/', 8378)
Success: ('http://achewood.com/', 15043)
ran in 0.547s
avg time: 0.482s

Und mit Nick T Code, zurechtgebastelt auch den Durchschnitt von fünf geben und die Ausgabe zeigen, besser:

Starting threaded reads:
...took 1.921520 seconds ([8117, 30070, 15043, 8386, 28611])
Starting threaded reads:
...took 1.779461 seconds ([8135, 15043, 8386, 30349, 28611])
Starting threaded reads:
...took 1.756968 seconds ([8135, 8386, 15043, 30349, 28611])
Starting threaded reads:
...took 1.762956 seconds ([8386, 8135, 15043, 29996, 28611])
Starting threaded reads:
...took 1.654377 seconds ([8117, 30349, 15043, 8386, 28611])
avg time: 1.775s

Starting sequential reads:
...took 1.389803 seconds ([8135, 30147, 28611, 8386, 15043])
Starting sequential reads:
...took 1.457451 seconds ([8135, 30051, 28611, 8386, 15043])
Starting sequential reads:
...took 1.432214 seconds ([8135, 29996, 28611, 8386, 15043])
Starting sequential reads:
...took 1.447866 seconds ([8117, 30028, 28611, 8386, 15043])
Starting sequential reads:
...took 1.468946 seconds ([8153, 30051, 28611, 8386, 15043])
avg time: 1.439s

Und Wai Yip Tung Code verwendet:

Fetched 8117 from http://www.google.com/
Fetched 28611 from http://www.bing.com/
Fetched 8386 from http://www.altavista.com/
Fetched 30051 from http://www.lycos.com/
Fetched 15043 from http://achewood.com/
done in 0.704s
Fetched 8117 from http://www.google.com/
Fetched 28611 from http://www.bing.com/
Fetched 8386 from http://www.altavista.com/
Fetched 30114 from http://www.lycos.com/
Fetched 15043 from http://achewood.com/
done in 0.845s
Fetched 8153 from http://www.google.com/
Fetched 28611 from http://www.bing.com/
Fetched 8386 from http://www.altavista.com/
Fetched 30070 from http://www.lycos.com/
Fetched 15043 from http://achewood.com/
done in 0.689s
Fetched 8117 from http://www.google.com/
Fetched 28611 from http://www.bing.com/
Fetched 8386 from http://www.altavista.com/
Fetched 30114 from http://www.lycos.com/
Fetched 15043 from http://achewood.com/
done in 0.647s
Fetched 8135 from http://www.google.com/
Fetched 28611 from http://www.bing.com/
Fetched 8386 from http://www.altavista.com/
Fetched 30349 from http://www.lycos.com/
Fetched 15043 from http://achewood.com/
done in 0.693s
avg time: 0.715s

Ich muss sagen, ich mag, dass die sequentielle Fetches ausgeführt besser für mich.

Hier ist ein Beispiel mit Python Threads. Die anderen Gewinde Beispiele hier starten einen Thread pro URL, die nicht sehr freundlich Verhalten ist, wenn es zu viele Treffer für den Server Griff bewirkt (zum Beispiel ist es üblich, Spinnen viele URLs auf dem gleichen Host haben)

from threading import Thread
from urllib2 import urlopen
from time import time, sleep

WORKERS=1
urls = ['http://docs.python.org/library/threading.html',
        'http://docs.python.org/library/thread.html',
        'http://docs.python.org/library/multiprocessing.html',
        'http://docs.python.org/howto/urllib2.html']*10
results = []

class Worker(Thread):
    def run(self):
        while urls:
            url = urls.pop()
            results.append((url, urlopen(url).read()))

start = time()
threads = [Worker() for i in range(WORKERS)]
any(t.start() for t in threads)

while len(results)<40:
    sleep(0.1)
print time()-start

Hinweis: Die Zeiten, die hier angegeben sind für 40 Urls und wird viel von der Geschwindigkeit Ihrer Internetverbindung und die Latenz auf den Server ab. Als in Australien, mein ping ist> 300 ms

Mit WORKERS=1 dauerte es 86 Sekunden laufen
Mit WORKERS=4 dauerte es 23 Sekunden laufen
mit WORKERS=10 dauerte es 10 Sekunden laufen

10 so Fäden, das Herunterladen ist 8,6-mal so schnell wie ein einzelner Thread.

Hier ist eine verbesserte Version, die eine Warteschlange verwendet. Es gibt mindestens ein paar Vorteile.
1. Die URLs werden in der Reihenfolge beantragt, dass sie in der Liste erscheinen
2. Kann q.join() zu erkennen, verwenden, wenn die Anfragen alle abgeschlossen haben
3. Die Ergebnisse sind in der gleichen Reihenfolge wie die URL-Liste gehalten

from threading import Thread
from urllib2 import urlopen
from time import time, sleep
from Queue import Queue

WORKERS=10
urls = ['http://docs.python.org/library/threading.html',
        'http://docs.python.org/library/thread.html',
        'http://docs.python.org/library/multiprocessing.html',
        'http://docs.python.org/howto/urllib2.html']*10
results = [None]*len(urls)

def worker():
    while True:
        i, url = q.get()
        # print "requesting ", i, url       # if you want to see what's going on
        results[i]=urlopen(url).read()
        q.task_done()

start = time()
q = Queue()
for i in range(WORKERS):
    t=Thread(target=worker)
    t.daemon = True
    t.start()

for i,url in enumerate(urls):
    q.put((i,url))
q.join()
print time()-start

Die tatsächliche Wartezeit ist wahrscheinlich nicht in urllib2 sondern auf dem Server und / oder die Netzwerkverbindung zum Server.

Es gibt zwei Möglichkeiten, diese bis zu beschleunigen.

  1. Halten Sie die Verbindung am Leben (siehe diese Frage auf, wie das tun: Python urllib2 mit am Leben erhalten )
  2. Verwenden Sie multiplle Verbindungen können Sie Gewinde oder einen Asynchron-Ansatz als Aaron Gallagher verwenden vorgeschlagen. Dazu einfach ein beliebiges Threading Beispiel verwenden und Sie sollten in Ordnung tun :) Sie auch den multiprocessing lib verwenden können, um die Dinge ziemlich einfach zu machen.

Die meisten Antworten konzentrierten sich auf mehreren Seiten von verschiedenen Servern in der gleichen Zeit zu holen (Gewinde), aber nicht auf bereits geöffnet HTTP-Verbindung wiederzuverwenden. Wenn OP mehrere Anforderung an die gleichen Server / Website zu machen.

In urlib2 eine separate Verbindung mit jeder Anforderung erstellt wird, die Auswirkungen Leistung und und als Folge geringerer Geschwindigkeit des Abrufens Seiten. urllib3 löst dieses Problem durch einen Verbindungspool verwenden. Kann mehr lesen hier urllib3 [Auch thread-safe]

Es gibt auch Anfragen eine HTTP-Bibliothek, dass Anwendungen urllib3

Das mit einem Gewinde kombiniert sollte die Geschwindigkeit des Abrufen Seiten

erhöhen

Heute gibt es ausgezeichnete Python lib, die dies für Sie tun Anfragen genannt .

Verwenden Sie Standard-api von Anfragen, wenn Sie Lösung auf Themen oder Asynchron-api basierend wollen (mit GEVENT unter der Haube), wenn Sie wollen Lösung basierend auf nicht-blockierende IO.

Da diese Frage gepostet sieht es aus, als gäbe es eine höhere Abstraktionsebene zur Verfügung, ThreadPoolExecutor:

https://docs.python.org/3/ Bibliothek / concurrent.futures.html # ThreadPoolExecutor-Beispiel

Das Beispiel von dort hier der Einfachheit halber eingefügt:

import concurrent.futures
import urllib.request

URLS = ['http://www.foxnews.com/',
        'http://www.cnn.com/',
        'http://europe.wsj.com/',
        'http://www.bbc.co.uk/',
        'http://some-made-up-domain.com/']

# Retrieve a single page and report the url and contents
def load_url(url, timeout):
    with urllib.request.urlopen(url, timeout=timeout) as conn:
        return conn.read()

# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    # Start the load operations and mark each future with its URL
    future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
    for future in concurrent.futures.as_completed(future_to_url):
        url = future_to_url[future]
        try:
            data = future.result()
        except Exception as exc:
            print('%r generated an exception: %s' % (url, exc))
        else:
            print('%r page is %d bytes' % (url, len(data)))

Es gibt auch map die ich denke, der Code leichter macht: https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.Executor.map

Ray bietet eine elegante Art und Weise, dies zu tun (in beiden Python 2 und Python 3) . Ray ist eine Bibliothek für das Schreiben von parallelen und verteilten Python.

definieren Sie einfach die fetch Funktion mit dem @ray.remote Dekorateur. Dann können Sie eine URL im Hintergrund holen durch fetch.remote(url) aufrufen.

import ray
import sys

ray.init()

@ray.remote
def fetch(url):
    if sys.version_info >= (3, 0):
        import urllib.request
        return urllib.request.urlopen(url).read()
    else:
        import urllib2
        return urllib2.urlopen(url).read()

urls = ['https://en.wikipedia.org/wiki/Donald_Trump',
        'https://en.wikipedia.org/wiki/Barack_Obama',
        'https://en.wikipedia.org/wiki/George_W._Bush',
        'https://en.wikipedia.org/wiki/Bill_Clinton',
        'https://en.wikipedia.org/wiki/George_H._W._Bush']

# Fetch the webpages in parallel.
results = ray.get([fetch.remote(url) for url in urls])

Wenn Sie auch die Seiten parallel verarbeiten möchten, können Sie entweder den Verarbeitungscode setzen direkt in fetch, oder Sie können eine neue Remote-Funktion definieren und komponieren sie zusammen.

@ray.remote
def process(html):
    tokens = html.split()
    return set(tokens)

# Fetch and process the pages in parallel.
results = []
for url in urls:
    results.append(process.remote(fetch.remote(url)))
results = ray.get(results)

Wenn Sie eine sehr lange Liste von URLs haben, dass Sie holen wollen, möchten Sie vielleicht einige Aufgaben erteilen und sie dann in der Reihenfolge verarbeiten, dass sie vervollständigen. Sie können dies mit ray.wait tun.

urls = 100 * urls  # Pretend we have a long list of URLs.
results = []

in_progress_ids = []

# Start pulling 10 URLs in parallel.
for _ in range(10):
    url = urls.pop()
    in_progress_ids.append(fetch.remote(url))

# Whenever one finishes, start fetching a new one.
while len(in_progress_ids) > 0:
    # Get a result that has finished.
    [ready_id], in_progress_ids = ray.wait(in_progress_ids)
    results.append(ray.get(ready_id))
    # Start a new task.
    if len(urls) > 0:
        in_progress_ids.append(fetch.remote(urls.pop()))

Auf dem Ray Dokumentation .

Fetching-Webseiten nehmen offensichtlich eine Weile, wie Sie nichts lokalen Zugriff. Wenn Sie den Zugriff auf mehrere haben, könnten Sie die threading Modul ein paar laufen auf einmal.

Hier ist ein sehr grobes Beispiel

import threading
import urllib2
import time

urls = ['http://docs.python.org/library/threading.html',
        'http://docs.python.org/library/thread.html',
        'http://docs.python.org/library/multiprocessing.html',
        'http://docs.python.org/howto/urllib2.html']
data1 = []
data2 = []

class PageFetch(threading.Thread):
    def __init__(self, url, datadump):
        self.url = url
        self.datadump = datadump
        threading.Thread.__init__(self)
    def run(self):
        page = urllib2.urlopen(self.url)
        self.datadump.append(page.read()) # don't do it like this.

print "Starting threaded reads:"
start = time.clock()
for url in urls:
    PageFetch(url, data2).start()
while len(data2) < len(urls): pass # don't do this either.
print "...took %f seconds" % (time.clock() - start)

print "Starting sequential reads:"
start = time.clock()
for url in urls:
    page = urllib2.urlopen(url)
    data1.append(page.read())
print "...took %f seconds" % (time.clock() - start)

for i,x in enumerate(data1):
    print len(data1[i]), len(data2[i])

Dies war die Ausgabe, wenn ich es lief:

Starting threaded reads:
...took 2.035579 seconds
Starting sequential reads:
...took 4.307102 seconds
73127 19923
19923 59366
361483 73127
59366 361483

aus dem Thread die Daten Grabbing durch eine Liste angehängt ist wahrscheinlich schlecht beraten (Queue wäre besser), aber es zeigt, dass es einen Unterschied gibt.

Hier ist eine Standardbibliothek Lösung. Es ist nicht ganz so schnell, aber es benötigt weniger Speicher als die Gewindelösungen.

try:
    from http.client import HTTPConnection, HTTPSConnection
except ImportError:
    from httplib import HTTPConnection, HTTPSConnection
connections = []
results = []

for url in urls:
    scheme, _, host, path = url.split('/', 3)
    h = (HTTPConnection if scheme == 'http:' else HTTPSConnection)(host)
    h.request('GET', '/' + path)
    connections.append(h)
for h in connections:
    results.append(h.getresponse().read())

Auch wenn die meisten Ihrer Anfragen an den gleichen Host, dann die gleiche HTTP-Verbindung Wiederverwendung Hilfe würde wahrscheinlich mehr als die Dinge parallel zu tun.

Sie finden Python Netzwerk-Benchmark-Skript für einzelne Verbindung Langsamkeit Identifikation:

"""Python network test."""
from socket import create_connection
from time import time

try:
    from urllib2 import urlopen
except ImportError:
    from urllib.request import urlopen

TIC = time()
create_connection(('216.58.194.174', 80))
print('Duration socket IP connection (s): {:.2f}'.format(time() - TIC))

TIC = time()
create_connection(('google.com', 80))
print('Duration socket DNS connection (s): {:.2f}'.format(time() - TIC))

TIC = time()
urlopen('http://216.58.194.174')
print('Duration urlopen IP connection (s): {:.2f}'.format(time() - TIC))

TIC = time()
urlopen('http://google.com')
print('Duration urlopen DNS connection (s): {:.2f}'.format(time() - TIC))

Und Beispiel von Ergebnissen mit Python 3.6:

Duration socket IP connection (s): 0.02
Duration socket DNS connection (s): 75.51
Duration urlopen IP connection (s): 75.88
Duration urlopen DNS connection (s): 151.42

Python 2.7.13 hat sehr ähnliche Ergebnisse.

In diesem Fall DNS und urlopen Langsamkeit sind leicht zu identifizieren.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top