Асинхронные вызовы HTTP в Python

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

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


person kasceled    schedule 10.02.2011    source источник
comment
Путь перебор. Все, что мне нужно, это одновременные http-вызовы из скрипта (мне не нужно вызывать процесс из командной строки и т. д.). Мне просто нужно иметь функцию обратного вызова, но я не могу найти процесс для этого в python. Дальнейшие исследования ведут меня к urllib2.   -  person kasceled    schedule 11.02.2011
comment
Перебор? Потоки не имеют ничего общего с вызовом процессов из командной строки.   -  person Falmarri    schedule 11.02.2011
comment
tippytop, да конечно urllib2 для транспорта.. но их все равно нужно спавнить параллельно. так что вы можете использовать многопоточность, многопроцессорность, concurrent.futures или решение на основе асинхронного ввода-вывода.   -  person Corey Goldberg    schedule 11.02.2011
comment
@Falmarri Потому что потоки Python ужасны.   -  person Marcin    schedule 25.07.2013


Ответы (4)


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

person Keith    schedule 10.02.2011
comment
В итоге я вернулся к подходу pycurl, когда опубликовал это (извините за позднее принятие). - person kasceled; 03.04.2012
comment
@типпитоп Круто. Кроме того, вас может заинтересовать моя упрощающая оболочка. pycopia.WWW.clientмодуль. - person Keith; 03.04.2012

Начиная с 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)))
person Corey Goldberg    schedule 10.02.2011

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

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

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)
person Raj    schedule 11.02.2011

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

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

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

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

WaterFall( Parallel(TaskA, TaskB, TaskC) -> PostParallelTask)

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

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

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

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

person Venkatt Guhesan    schedule 06.02.2014
comment
Это не тема — это вопрос. И это, кажется, не отвечает на него... - person Tomerikoo; 16.12.2020