Несколько (асинхронных) соединений с 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 для этого и как? Если нет, какую другую библиотеку я должен использовать? Благодарю.
7 ответов
Итак, это 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
Вы можете использовать асинхронный ввод-вывод для этого.
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)
Взгляните на 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)
Ответ 2021 года с использованием современных асинхронных библиотек
Ответ 2016 года хорош, но я подумал, что добавлю другой ответ с httpx вместо aiohttp, поскольку httpx является только клиентом и поддерживает разные асинхронные среды. Я опускаю цикл for OP с URL-адресами, построенными из числа, соединенного со строкой, для того, что я считаю более общим ответом.
import asyncio
import httpx
# you can have synchronous code here
async def getURL(url):
async with httpx.AsyncClient() as client:
response = await client.get(url)
# we could have some synchronous code here too
# to do CPU bound tasks on what we just fetched for instance
return response
# more synchronous code can go here
async def main():
response1, response2 = await asyncio.gather(getURL(url1),getURL(url2))
# do things with the responses
# you can also have synchronous code here
asyncio.run(main())
Код после любого ожидания в блоке async with будет запущен, как только ожидаемая задача будет выполнена. Это хорошее место, чтобы проанализировать ваш ответ, не дожидаясь завершения всех ваших запросов.
Код после asyncio.gather будет запущен после завершения всех задач. Это хорошее место для выполнения операций, требующих информации из всех запросов, возможно, предварительно обработанных в асинхронной функции, вызываемой функцией gather.
Я знаю, что этот вопрос немного устарел, но я подумал, что было бы полезно продвинуть другое асинхронное решение, основанное на библиотеке запросов.
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/
Возможно, используя многопроцессорность и разделите вашу работу на 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)
Либо вы выясняете темы, либо используете Twisted ( что является асинхронным).