如何在异步中使用请求?

我想在 asyncio中做并行的 http 请求任务,但是我发现 python-requests会阻塞 asyncio的事件循环。我找到了 哎哟喂,但是它不能使用 http 代理提供 http 请求服务。

因此,我想知道是否有一种方法可以在 asyncio的帮助下进行异步 http 请求。

170809 次浏览

请求目前不支持 asyncio,也没有计划提供这种支持。很可能您可以实现一个知道如何使用 asyncio的自定义“传输适配器”(如所讨论的 给你)。

如果我发现自己有时间,我可能会去调查,但我不能保证什么。

要将请求(或任何其他阻塞库)与异步一起使用,可以使用 Run _ in _ execute BaseEventLoop.run _ in _ execute在另一个线程中运行函数并从中生成结果。例如:

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())

Pimin Konstantin Kefaloukos 的一篇文章中有一个很好的异步/等待循环和线程的例子 使用 Python 和 syncio 实现简单的并行 HTTP 请求:

为了最小化总完成时间,我们可以增加线程池的大小以匹配必须发出的请求数。幸运的是,这很容易做到,我们将看到下一步。下面的代码清单是如何使用20个工作线程的线程池发出20个异步 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())

上面的答案仍然使用旧的 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())

还有一个 httpx库,它可以用来替换支持 async/await的请求。

免责声明: Following code creates different threads for each function.

这对于某些情况可能很有用,因为它使用起来更简单。但是要知道,它不是异步的,而是使用多个线程产生异步的错觉,尽管 Decorator 建议这样做。

要使任何函数不受阻塞,只需复制装饰符并使用回调函数作为参数装饰任何函数即可。回调函数将接收函数返回的数据。

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 !!")

考虑到 aiohttp 是一个功能齐全的 web 框架,我建议使用像 httpx (https://www.python-httpx.org/)这样的轻量级框架,它支持异步请求。它拥有与请求几乎相同的 API:

>>> async with httpx.AsyncClient() as client:
...     r = await client.get('https://www.example.com/')
...
>>> r
<Response [200 OK]>