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

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

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

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

запрос пользователя -> web.py - > скачать 10 страниц в параллельно или асинхронно - > анализировать содержимое, возвращать результаты

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

10 ответов


используйте async http-клиент, который использует asynchat и asyncore. http://sourceforge.net/projects/asynchttp/files/asynchttp-production/asynchttp.py-1.0/asynchttp.py/download


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

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-клиент без внешних зависимостей. ... Этот класс все еще находится в разработке и еще не рекомендован к использованию в производстве."


одним из вариантов было бы разместить работу в какой-то очереди (вы могли бы использовать что-то предприимчивое, как в частности, ActiveMQ С pyactivemq или топайте в качестве разъема или вы можете использовать что-то легкое, как Пустельга который написан на Scala и говорит тот же протокл, что и memcache, поэтому вы можете просто использовать клиент Python memcache для разговора с ним).

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

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


вы могли бы использовать 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

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


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


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

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

Я не знаю, будет ли это точно работать, но похоже, что это может:EvServer: Python асинхронный сервер WSGI имеет web.py интерфейс и может сделать Comet style push для клиента браузера.

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


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


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