asyncio에서 요청을 어떻게 사용할 수 있습니까?


128

에서 병렬 http 요청 작업을 수행하고 asyncio싶지만 python-requests이벤트 루프를 차단할 수 asyncio있습니다. aiohttp를 찾았 지만 http 프록시를 사용하여 http 요청 서비스를 제공 할 수 없습니다.

따라서 .NET의 도움으로 비동기 http 요청을 수행하는 방법이 있는지 알고 싶습니다 asyncio.


1
요청 만 보내는 경우 subprocess코드를 병렬화 하는 데 사용할 수 있습니다 .
— WeaselFox 2014 년

이 방법은 우아하지 보인다 ...
— 우대

이제 요청의 asyncio 포트가 있습니다. github.com/rdbhost/yieldfromRequests
— Rdbhost

답변:


181

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에서는 new 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 를 참조하십시오 .


5
이것이 정확히 어떻게 작동하는지 설명 할 수 있습니까? 이것이 차단되지 않는 방법을 이해하지 못합니다.
— 스콧 Coates는

32
@christian이지만 다른 스레드에서 동시에 실행되는 경우 asyncio의 요점을 물리 치지 않습니까?
— 스콧 Coates는

21
@scoarescoare 여기에 '만약 당신이 제대로한다면'부분이 들어옵니다. 실행 프로그램에서 실행하는 메서드는 독립적이어야합니다 (위의 예에서 requests.get처럼 (대부분)). 이렇게하면 공유 메모리, 잠금 등을 처리 할 필요가 없으며 프로그램의 복잡한 부분은 asyncio 덕분에 여전히 단일 스레드입니다.
— christian

5
@scoarescoare 주요 사용 사례는 asyncio를 지원하지 않는 IO 라이브러리와 통합하는 것입니다. 예를 들어, 저는 진정으로 오래된 SOAP 인터페이스로 작업을하고 있으며, "최소한의"솔루션으로 suds-jurko 라이브러리를 사용하고 있습니다. 나는 그것을 asyncio 서버와 통합하려고 시도하고 있으므로 run_in_executor를 사용하여 비동기식 으로 보이는 방식으로 차단 suds 호출을 만들고 있습니다.
— Lucretiel 2015

10
정말 멋지다. 레거시 작업에는 너무 쉽다. 그러나 이것은 OS 스레드 풀을 사용하므로 aiohttp처럼 진정한 asyncio 지향 lib로 확장되지 않는다는 점을 강조해야한다.
— jsalter

78

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

여기서 커넥터는 무엇을합니까?
— Markus Meskanen 2015 년

이 프록시 서버를 통해 연결을 제공합니다
— mindmaster

16
이것은 별도의 스레드에서 요청을 사용하는 것보다 훨씬 더 나은 솔루션입니다. 진정한 비동기식이므로 오버 헤드가 적고 메모리 사용량이 적습니다.
— Thom

14
파이썬> = 3.5의 경우 @ asyncio.coroutine을 "async"로, "yield from"을 "await"로 교체
— James

41

위의 답변은 여전히 ​​오래된 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())

1
더 많은 URL로 자세히 설명해 주시겠습니까? 질문이 병렬 http 요청에 관한 것일 때 하나의 URL 만 갖는 것은 의미가 없습니다.
— 익명의

전설. 감사합니다! 훌륭한 작품
— Adam

@ospider 100 개의 요청을 병렬로 사용하여 10k URL을 전달하도록이 코드를 어떻게 수정할 수 있습니까? 아이디어는 다음 (100)을 시작하기 위해 전달 될 수 없습니다 (100)를 기다리는 동시에 100 개 개의 슬롯을 사용하는 것입니다
— Antoan Milkov

@AntoanMilkov 댓글 영역에서 답변 할 수없는 다른 질문입니다.
— ospider

자네 말이 맞아 @ospider, 여기에 질문 : stackoverflow.com/questions/56523043/...
— Antoan Milkov

11

요청은 현재 지원되지 않으며 asyncio그러한 지원을 제공 할 계획이 없습니다. 사용 방법을 알고 있는 사용자 정의 "전송 어댑터"( 여기 에서 설명 )를 구현할 수 있습니다 asyncio.

시간이 지나면 실제로 들여다 볼 수 있지만 약속 할 수는 없습니다.


링크는 404로 연결됩니다.
— CodeBiker

8

Pimin Konstantin Kefaloukos의 기사에 async / await 루프 및 스레딩의 좋은 사례가 있습니다. Python 및 asyncio를 사용한 쉬운 병렬 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())

2
이것의 문제는 내가 20 개의 실행자 청크로 10000 개의 요청을 실행해야한다면 다음 20 개로 시작하기 위해 20 개의 실행자가 모두 끝날 때까지 기다려야한다는 것입니다. 맞죠? for i in range(10000)하나의 요청이 실패하거나 시간 초과 될 수 있기 때문에 할 수 없습니다 .
— Sanandrea

1
ThreadPoolExecutor를 사용하여 똑같이 할 수 있는데 왜 asyncio가 필요한지 설명해 주시겠습니까?
— Asaf Pinhassi

@lya Rusin 무엇을 기반으로 max_workers 수를 설정합니까? CPU 및 스레드 수와 관련이 있습니까?
— alt-f4
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.