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-запрос.
Я не уверен, что понимаю ваш вопрос, поэтому я дам несколько частичных ответов для начала.
- Если ваша забота это 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 с ним).