我怎么能在asyncio中使用请求?

时间:2014-03-05 06:36:52

标签: python python-requests python-3.4 aiohttp

我想在asyncio中执行并行的http请求任务,但我发现python-requests会阻止asyncio的事件循环。我找到aiohttp但它无法使用http代理提供http请求服务。

所以我想知道是否有办法在asyncio的帮助下进行异步http请求。

5 个答案:

答案 0 :(得分:153)

要将请求(或任何其他阻塞库)与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

答案 1 :(得分:66)

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

答案 2 :(得分:9)

请求目前不支持asyncio,并且没有计划提供此类支持。您可能可以实现知道如何使用asyncio的自定义“传输适配器”(如所讨论的here)。

如果我发现自己有一段时间,我可能会真正研究,但我不能保证任何事情。

答案 3 :(得分:7)

在Pimin Konstantin Kefaloukos的文章中有一个很好的async / await循环和线程的例子 Easy parallel HTTP requests with Python and asyncio

  

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

答案 4 :(得分:0)

免责声明: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 !!")