Несколько (асинхронных) соединений с urllib2 или другой библиотекой http?
У меня есть такой код.
for p in range(1,1000):
result = False
while result is False:
ret = urllib2.Request('http://server/?'+str(p))
try:
result = process(urllib2.urlopen(ret).read())
except (urllib2.HTTPError, urllib2.URLError):
pass
results.append(result)
Я хотел бы сделать два или три запроса одновременно, чтобы ускорить это. Могу ли я использовать urllib2 для этого и как? Если нет, какую другую библиотеку я должен использовать? Спасибо.
Ответы
Ответ 1
Для этого вы можете использовать асинхронный ввод-вывод.
requests + gevent= grequests
GRequests позволяет использовать запросы с Gevent для упрощения асинхронных запросов HTTP.
import grequests
urls = [
'http://www.heroku.com',
'http://tablib.org',
'http://httpbin.org',
'http://python-requests.org',
'http://kennethreitz.com'
]
rs = (grequests.get(u) for u in urls)
grequests.map(rs)
Ответ 2
Взгляните на gevent - сетевую библиотеку Python на основе сопрограмм, которая использует greenlet для обеспечения высокоуровневого синхронного API сверху цикла событий libevent.
Пример:
#!/usr/bin/python
# Copyright (c) 2009 Denis Bilenko. See LICENSE for details.
"""Spawn multiple workers and wait for them to complete"""
urls = ['http://www.google.com', 'http://www.yandex.ru', 'http://www.python.org']
import gevent
from gevent import monkey
# patches stdlib (including socket and ssl modules) to cooperate with other greenlets
monkey.patch_all()
import urllib2
def print_head(url):
print 'Starting %s' % url
data = urllib2.urlopen(url).read()
print '%s: %s bytes: %r' % (url, len(data), data[:50])
jobs = [gevent.spawn(print_head, url) for url in urls]
gevent.joinall(jobs)
Ответ 3
Итак, это 2016 😉, и у нас есть Python 3.4+ со встроенным модулем asyncio для асинхронного ввода-вывода. Мы можем использовать aiohttp в качестве HTTP-клиента для параллельной загрузки нескольких URL-адресов.
import asyncio
from aiohttp import ClientSession
async def fetch(url):
async with ClientSession() as session:
async with session.get(url) as response:
return await response.read()
async def run(loop, r):
url = "http://localhost:8080/{}"
tasks = []
for i in range(r):
task = asyncio.ensure_future(fetch(url.format(i)))
tasks.append(task)
responses = await asyncio.gather(*tasks)
# you now have all response bodies in this variable
print(responses)
loop = asyncio.get_event_loop()
future = asyncio.ensure_future(run(loop, 4))
loop.run_until_complete(future)
Источник: скопировано из http://pawelmhm.github.io/asyncio/python/aiohttp/2016/04/22/asyncio-aiohttp.html
Ответ 4
Я знаю, что этот вопрос немного устарел, но я подумал, что было бы полезно продвинуть еще одно асинхронное решение, построенное на библиотеке запросов.
list_of_requests = ['http://moop.com', 'http://doop.com', ...]
from simple_requests import Requests
for response in Requests().swarm(list_of_requests):
print response.content
Документы находятся здесь: http://pythonhosted.org/simple-requests/
Ответ 5
Либо вы выясните threads, либо используйте Twisted (который является асинхронным).
Ответ 6
возможно, используя multiprocessing и разделите работу на 2 процесса или около того.
Вот пример (он не тестировался)
import multiprocessing
import Queue
import urllib2
NUM_PROCESS = 2
NUM_URL = 1000
class DownloadProcess(multiprocessing.Process):
"""Download Process """
def __init__(self, urls_queue, result_queue):
multiprocessing.Process.__init__(self)
self.urls = urls_queue
self.result = result_queue
def run(self):
while True:
try:
url = self.urls.get_nowait()
except Queue.Empty:
break
ret = urllib2.Request(url)
res = urllib2.urlopen(ret)
try:
result = res.read()
except (urllib2.HTTPError, urllib2.URLError):
pass
self.result.put(result)
def main():
main_url = 'http://server/?%s'
urls_queue = multiprocessing.Queue()
for p in range(1, NUM_URL):
urls_queue.put(main_url % p)
result_queue = multiprocessing.Queue()
for i in range(NUM_PROCESS):
download = DownloadProcess(urls_queue, result_queue)
download.start()
results = []
while result_queue:
result = result_queue.get()
results.append(result)
return results
if __name__ == "__main__":
results = main()
for res in results:
print(res)