Как я могу использовать запросы в Asyncio?
Я хочу сделать параллельные задачи HTTP-запроса в asyncio
но я нахожу это python-requests
будет блокировать цикл событий asyncio
, Я нашел aiohttp, но он не смог обеспечить службу HTTP-запроса с использованием http-прокси.
Поэтому я хочу знать, есть ли способ выполнять асинхронные HTTP-запросы с помощью asyncio
,
8 ответов
Чтобы использовать запросы (или любые другие блокирующие библиотеки) с asyncio, вы можете использовать BaseEventLoop.run_in_executor, чтобы запустить функцию в другом потоке и получить из нее результат, чтобы получить результат. Например:
import asyncio
import requests
@asyncio.coroutine
def main():
loop = asyncio.get_event_loop()
future1 = loop.run_in_executor(None, requests.get, 'http://www.google.com')
future2 = loop.run_in_executor(None, requests.get, 'http://www.google.co.uk')
response1 = yield from future1
response2 = yield from future2
print(response1.text)
print(response2.text)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Это позволит получить оба ответа параллельно.
С Python 3.5 вы можете использовать новый await
/async
синтаксис:
import asyncio
import requests
async def main():
loop = asyncio.get_event_loop()
future1 = loop.run_in_executor(None, requests.get, 'http://www.google.com')
future2 = loop.run_in_executor(None, requests.get, 'http://www.google.co.uk')
response1 = await future1
response2 = await future2
print(response1.text)
print(response2.text)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Смотрите PEP0492 для получения дополнительной информации.
aiohttp уже можно использовать с HTTP-прокси:
import asyncio
import aiohttp
@asyncio.coroutine
def do_request():
proxy_url = 'http://localhost:8118' # your proxy address
response = yield from aiohttp.request(
'GET', 'http://google.com',
proxy=proxy_url,
)
return response
loop = asyncio.get_event_loop()
loop.run_until_complete(do_request())
Ответы выше все еще используют старые сопрограммы в стиле Python 3.4. Вот что вы написали бы, если бы получили Python 3.5+.
aiohttp
теперьподдерживает http прокси
import aiohttp
import asyncio
async def fetch(session, url):
async with session.get(url) as response:
return await response.text()
async def main():
urls = [
'http://python.org',
'https://google.com',
'http://yifei.me'
]
tasks = []
async with aiohttp.ClientSession() as session:
for url in urls:
tasks.append(fetch(session, url))
htmls = await asyncio.gather(*tasks)
for html in htmls:
print(html[:100])
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Запросы не поддерживает asyncio
и нет планов оказывать такую поддержку. Вероятно, вы могли бы реализовать собственный "Транспортный адаптер" (как обсуждено здесь), который знает, как использовать asyncio
,
Если я обнаружу, что через какое-то время это то, на что я мог бы действительно посмотреть, но я ничего не могу обещать.
Хороший случай асинхронных / ожидающих циклов и потоков в статье Пимина Константина Кефалукоса. Простые параллельные HTTP-запросы с Python и asyncio:
Чтобы минимизировать общее время завершения, мы могли бы увеличить размер пула потоков, чтобы соответствовать количеству запросов, которые мы должны сделать. К счастью, это легко сделать, как мы увидим дальше. Приведенный ниже код является примером того, как сделать двадцать асинхронных HTTP-запросов с пулом из двадцати рабочих потоков:
# Example 3: asynchronous requests with larger thread pool
import asyncio
import concurrent.futures
import requests
async def main():
with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:
loop = asyncio.get_event_loop()
futures = [
loop.run_in_executor(
executor,
requests.get,
'http://example.org/'
)
for i in range(20)
]
for response in await asyncio.gather(*futures):
pass
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Учитывая, что aiohttp - это полнофункциональная веб-платформа, я бы предложил использовать httpx (https://www.python-httpx.org/), который поддерживает асинхронные запросы и почти идентичный api для запросов:
async с httpx.AsyncClient () в качестве клиента: ... r = await client.get('https://www.example.com/') ... r <Response [200 OK]>
ОТКАЗ:
Following code creates different threads for each function.
Это может быть полезно в некоторых случаях, поскольку его проще использовать. Но знайте, что это не асинхронный режим, но он дает иллюзию асинхронности с использованием нескольких потоков, даже если декоратор предлагает это.
Чтобы сделать любую функцию неблокирующей, просто скопируйте декоратор и украсьте любую функцию функцией обратного вызова в качестве параметра. Функция обратного вызова получит данные, возвращенные функцией.
import asyncio
import requests
def run_async(callback):
def inner(func):
def wrapper(*args, **kwargs):
def __exec():
out = func(*args, **kwargs)
callback(out)
return out
return asyncio.get_event_loop().run_in_executor(None, __exec)
return wrapper
return inner
def _callback(*args):
print(args)
# Must provide a callback function, callback func will be executed after the func completes execution !!
@run_async(_callback)
def get(url):
return requests.get(url)
get("https://google.com")
print("Non blocking code ran !!")
пока не поддерживает asyncio. Наиболее выгодным подходом будет использование библиотеки, которая изначально поддерживает asyncio, например httpx .
Однако, если ваши варианты использования в значительной степени зависят от использованияpython-requests
вы можете обернуть вызовы синхронизации с помощьюasyncio.to_thread
иasyncio.gather
и следуйте шаблонам программирования asyncio.
import asyncio
import requests
async def main():
res = await asyncio.gather(asyncio.to_thread(requests.get("YOUR_URL"),)
if __name__ == "__main__":
asyncio.run(main())
Для параллелизма/распараллеливания сетевых запросов:
import asyncio
import requests
urls = ["URL_1", "URL_2"]
async def make_request(url: string):
response = await asyncio.gather(asyncio.to_thread(requests.get(url),)
return response
async def main():
responses = await asyncio.gather((make_request(url) for url in urls))
for response in responses:
print(response)
if __name__ == "__main__":
asyncio.run(main())