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.