Python: простая асинхронная загрузка содержимого URL-адреса?

У меня есть сервер web.py, который отвечает на различные пользовательские запросы. Одна из этих запросов включает загрузку и анализ серии веб-страниц.

Есть ли простой способ настроить механизм загрузки URL-адресов на основе async/callback в web.py? Низкое использование ресурсов особенно важно, поскольку каждый инициированный пользователем запрос может привести к загрузке нескольких страниц.

Поток будет выглядеть так:

Пользовательский запрос → web.py → Загрузка 10 страниц параллельно или асинхронно → Анализ содержимого, возврат результатов

Я понимаю, что Twisted будет хорошим способом сделать это, но я уже в web.py, поэтому меня особенно интересует то, что может поместиться в web.py.

Ответ 2

Вот интересный фрагмент кода. Я не использовал его сам, но он выглядит хорошо;)

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

Низкий уровень AsyncHTTPClient:

"Неблокирующий HTTP-клиент, поддерживаемый pycurl. Пример использования:"

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()

" fetch() может принимать строковый URL или экземпляр HTTPRequest, который предлагает больше параметров, например, выполнение запросов POST/PUT/DELETE.

Ключевой аргумент max_clients для конструктора AsyncHTTPClient определяет максимальное количество одновременных операций fetch(), которые могут выполняться параллельно на каждом IOLoop. "

Выполняется также новая реализация: https://github.com/facebook/tornado/blob/master/tornado/simple_httpclient.py "Неблокирующий HTTP-клиент без внешних зависимостей... Этот класс все еще находится в разработке и еще не рекомендуется для использования в качестве продукта".

Ответ 3

Один из вариантов заключается в том, чтобы опубликовать работу в какой-то очереди (вы могли бы использовать что-то предпринимаемое как ActiveMQ с pyactivemq или STOMP в качестве соединителя, или вы может использовать что-то легкое, например Kestrel, которое написано в Scala и говорит тот же самый проток, что и memcache, поэтому вы можете просто использовать memcache python клиент, чтобы поговорить с ним).

После установки механизма очередей вы можете создать столько или несколько рабочих задач, которые подписаны на очередь, и выполнить фактическую загрузку, как вы хотите. Вы даже можете заставить их жить на других машинах, чтобы они не мешали скорости обслуживания вашего сайта вообще. Когда рабочие завершены, они отправляют результаты обратно в базу данных или в другую очередь, где веб-сервер может их поднять.

Если вы не хотите управлять внешними рабочими процессами, вы можете сделать рабочие потоки в одном и том же процессе python, на котором запущен веб-сервер, но тогда, очевидно, он будет иметь больший потенциал для воздействия на производительность вашей веб-страницы.

Ответ 4

Возможно, вы сможете использовать urllib для загрузки файлов и Queue для управления несколькими рабочими потоками. например:

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

Ответ 5

Я бы просто создал сервис в twisted, который сделал эту параллельную выборку и анализ и доступ к ней из web.py как простой HTTP-запрос.

Ответ 6

В настоящее время есть отличные библиотеки Python, которые вы можете использовать - urllib3 (использует пулы потоков) и requests (использует пулы потоков через urllib3 или не блокирует IO через gevent)

Ответ 7

Я не уверен, что понимаю ваш вопрос, поэтому я дам несколько частичных ответов, чтобы начать с.

  • Если вы обеспокоены тем, что web.py приходится загружать данные откуда-то и анализировать результаты перед ответом, и вы опасаетесь, что запрос может истечь до того, как результаты будут готовы, вы можете использовать ajax для разделения работы. Вернитесь немедленно со страницы контейнера (чтобы сохранить результаты) и немного javascript, чтобы опросить ранг для результатов, пока у клиента не будет их всех. Таким образом, клиент никогда не ждет сервер, хотя пользователю все равно придется ждать результатов.
  • Если ваша проблема связана с сервером, ожидающим, пока клиент получит результаты, я сомневаюсь, что это действительно будет проблемой. Сетевые уровни не должны требовать от вас ожидания на запись
  • Если вы беспокоитесь о ожидающем себя сервере, когда клиент загружает статический контент из другого места, либо ajax, либо умное использование переадресаций должно решить вашу проблему.

Ответ 8

Я не знаю, будет ли это работать, но похоже, что это может быть: EvServer: Python Asynchronous WSGI Server имеет web.py interface и может сделать push-стиль кометы клиенту браузера.

Если это не так, возможно, вы можете использовать Concurrence HTTP client для асинхронной загрузки страниц и выяснить, как обслуживать их в браузер через ajax или комету.

Ответ 9

В соответствии с ответами MarkusQ MochiKit является хорошей библиотекой JavaScript с надежными асинхронными методами, вдохновленными Twisted.

Ответ 10

На самом деле вы можете интегрировать скрученные с web.py. Я не совсем уверен, как, как я только делал это с django (используется с ним скрученный).