Как я могу использовать запросы в 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())
Другие вопросы по тегам