Główny przykład w concurrent.futures
robi wszystko, co chcesz, o wiele prostszy. Co więcej, może obsłużyć ogromną liczbę adresów URL, robiąc tylko 5 jednocześnie i znacznie lepiej radzi sobie z błędami.
Oczywiście ten moduł jest zbudowany tylko z Pythonem 3.2 lub nowszym ... ale jeśli używasz 2.5-3.1, możesz po prostu zainstalować backport, futures
, z PyPI. Aby zmienić kod przykładowy, wystarczy wyszukać i zastąpić concurrent.futures
z futures
, a dla 2.x, urllib.request
z urllib2
.
Oto próbka przeniesiona do 2.x, zmodyfikowane do wykorzystania lista adresów URL i dodać razy:
import concurrent.futures
import urllib2
import time
start = time.time()
urls = ["http://www.google.com", "http://www.apple.com", "http://www.microsoft.com", "http://www.amazon.com", "http://www.facebook.com"]
# Retrieve a single page and report the url and contents
def load_url(url, timeout):
conn = urllib2.urlopen(url, timeout=timeout)
return conn.readall()
# 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 '"%s" fetched in %ss' % (url,(time.time() - start))
print "Elapsed Time: %ss" % (time.time() - start)
Ale można zrobić to jeszcze prostsze. Naprawdę, wszystko czego potrzebujesz to:
def load_url(url):
conn = urllib2.urlopen(url, timeout)
data = conn.readall()
print '"%s" fetched in %ss' % (url,(time.time() - start))
return data
with futures.ThreadPoolExecutor(max_workers=5) as executor:
pages = executor.map(load_url, urls)
print "Elapsed Time: %ss" % (time.time() - start)
tylko dodać: w przypadku Pythona wielowątkowość nie jest natywna dla rdzenia z powodu GIL. – akshayb
To wygląda na to, że pobieranie równoległych adresów URL jest szybsze niż robienie tego serialnie. Dlaczego? jest to spowodowane tym, że (zakładam) interpreter Pythona nie działa ciągle podczas żądania HTTP? –
Co zrobić, jeśli chcę przeanalizować zawartość tych stron internetowych, które pobieram? Czy lepiej jest parsować w każdym wątku, czy też powinienem to robić sekwencyjnie po dołączeniu wątków roboczych do głównego wątku? –