Как я могу использовать запросы ввода-вывода?


Я хочу выполнять параллельные задачи http-запроса в asyncio, но я считаю, что python-requests будет блокировать цикл обработки событий от asyncio. Я нашел aiohttp но он не мог предоставить услугу http-запроса с помощью HTTP-прокси.

поэтому я хочу знать, есть ли способ сделать асинхронные http-запросы с помощью asyncio.

5 81

5 ответов:

чтобы использовать запросы (или любые другие библиотеки блокировки) с 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())

просит в настоящее время не поддерживает 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())

ответы выше по-прежнему используют старые сопрограммы стиля 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())