Асинхронные HTTP-вызовы в Python - PullRequest
12 голосов
/ 11 февраля 2011

Мне нужна функция обратного вызова в Python, где я отправляю запрос в веб-сервис несколько раз, с изменением параметра каждый раз.Я хочу, чтобы эти запросы выполнялись одновременно, а не последовательно, поэтому я хочу, чтобы функция вызывалась асинхронно.

Похоже, я хотел бы использовать asyncore, но примеры, которые я видел, как это работает, выглядят как излишнее, поэтому мне интересно, есть ли другой путь, по которому я должен идти.Есть предложения по модулям / процессу?В идеале я хотел бы использовать их процедурно вместо создания классов, но я не смогу обойти это.

Ответы [ 4 ]

16 голосов
/ 11 февраля 2011

Начиная с Python 3.2, вы можете использовать concurrent.futures для запуска параллельных задач.

Проверьте этот ThreadPoolExecutor пример:

http://docs.python.org/dev/library/concurrent.futures.html#threadpoolexecutor-example

Он порождает потоки для извлечения HTML и воздействует на ответы по мере их поступления.

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):
    conn = urllib.request.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('%r page is %d bytes' % (url, len(data)))

В приведенном выше примере используется многопоточность. Существует также аналогичный ProcessPoolExecutor, который использует пул процессов, а не потоков:

http://docs.python.org/dev/library/concurrent.futures.html#processpoolexecutor-example

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):
    conn = urllib.request.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('%r page is %d bytes' % (url, len(data)))
16 голосов
/ 11 февраля 2011

Знаете ли вы о eventlet ?Он позволяет вам писать то, что кажется синхронным кодом, но он работает асинхронно по сети.

Вот пример супер минимального сканера:

urls = ["http://www.google.com/intl/en_ALL/images/logo.gif",
     "https://wiki.secondlife.com/w/images/secondlife.jpg",
     "http://us.i1.yimg.com/us.yimg.com/i/ww/beta/y3.gif"]

import eventlet
from eventlet.green import urllib2

def fetch(url):

  return urllib2.urlopen(url).read()

pool = eventlet.GreenPool()

for body in pool.imap(fetch, urls):
  print "got body", len(body)
8 голосов
/ 11 февраля 2011

Витая структура - только билет для этого.Но если вы не хотите брать это на себя, вы также можете использовать pycurl , обертку для libcurl, которая имеет свой собственный цикл асинхронных событий и поддерживает обратные вызовы.

0 голосов
/ 07 февраля 2014

(хотя этот поток посвящен серверному Python. Поскольку этот вопрос задавался некоторое время назад. Другие могут наткнуться на это, когда ищут аналогичный ответ на стороне клиента)

Для решения на стороне клиента вы можете обратиться к библиотеке Async.js, особенно к разделу «Control-Flow».

https://github.com/caolan/async#control-flow

Комбинируя «Параллель» с «Водопадом», вы можете достичь желаемого результата.

WaterFall (Параллельно (TaskA, TaskB, TaskC) -> PostParallelTask)

Если вы изучите пример под Control-Flow - «Auto», то он даст вам пример выше: https://github.com/caolan/async#autotasks-callback где «write-file» зависит от «get_data» и «make_folder», а «email_link» зависит от write-file ».

Обратите внимание, что все это происходит на стороне клиента (если вы не используете Node.JS - на стороне сервера)

Для Python на стороне сервера посмотрите PyCURL @ https://github.com/pycurl/pycurl/blob/master/examples/basicfirst.py

Комбинируя приведенный ниже пример с pyCurl, вы можете добиться неблокирующей многопоточной функциональности.

Надеюсь, это поможет. Удачи.

Venkatt @ http://MyThinkpond.com

...