Python: простая асинхронная загрузка содержимого URL? - PullRequest
9 голосов
/ 21 марта 2009

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

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

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

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

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

Ответы [ 10 ]

6 голосов
/ 19 декабря 2010

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

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

4 голосов
/ 21 марта 2009

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

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

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

3 голосов
/ 23 апреля 2010

Возможно, вы сможете использовать 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
2 голосов
/ 26 января 2012

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

2 голосов
/ 23 апреля 2010

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

2 голосов
/ 21 марта 2009

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

0 голосов
/ 21 марта 2009

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

0 голосов
/ 21 марта 2009

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

0 голосов
/ 21 марта 2009

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

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

0 голосов
/ 21 марта 2009

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

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