非同期でリクエストを使用するにはどうすればよいですか?


126

で並列http要求タスクを実行したいのですがasyncio、それpython-requestsによってのイベントループがブロックされますasyncioaiohttpを見つけましたが、httpプロキシを使用したhttpリクエストのサービスを提供できませんでした。

ので非同期のhttpリクエストを行う方法があるかどうかを知りたいですasyncio


1
リクエストを送信するだけの場合はsubprocess、コードを並列化するために使用できます。
WeaselFox 14

この方法は洗練されていないようです……
チラシ

リクエストの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では、新しい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
これがどのように機能するか正確に説明できますか?これがどのようにブロックしないのか分かりません。
Scott Coates 14年

32
@christianしかし、別のスレッドで同時に実行されている場合、それはasyncioのポイントを打ち負かしていませんか?
スコットコーツ2014年

21
@scoarescoareここで、「正しく実行した場合」の部分が出てきます。エグゼキュータで実行するメソッドは、自己完結型である必要があります(上記の例では、requests.getのように)。そうすれば、共有メモリやロックなどを処理する必要がなくなり、プログラムの複雑な部分は、asyncioのおかげでシングルスレッドのままです。
クリスチャン2014年

5
@scoarescoare主な使用例は、asyncioをサポートしていないIOライブラリとの統合です。たとえば、私は本当に古くからあるSOAPインターフェースを使っていくつかの作業を行っており、「最悪の」ソリューションとしてsuds-jurkoライブラリを使用しています。私はそれをasyncioサーバーと統合しようとしているので、run_in_executorを使用して、非同期に見える方法でブロッキングSUD呼び出しを行っています。
Lucretiel、2015

10
これが機能することは本当にクールであり、レガシーのものにとってはとても簡単ですが、これはOSスレッドプールを使用するため、aiohttpのような真の非同期指向のライブラリとしてはスケールアップしません
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
Python> = 3.5の場合、@ asyncio.coroutineを「async」に、「yield from」を「await」に置き換えます
James

40

上記の答えは、古い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が1つだけであっても意味がありません。
匿名

伝説。ありがとうございました!すばらしい作品
アダム

@ospiderこのコードを変更して、100個のリクエストを並行して使用して、1万のURLを配信する方法を教えてください。アイデアは、100が次の100を開始するために配信されるのを待つためではなく、同時にすべての100個のスロットを使用することです
Antoan Milkov

@AntoanMilkovこれは、コメント領域で回答できない別の質問です。
ospider

あなたは正しいです@ospider、ここで質問です:stackoverflow.com/questions/56523043/...
Antoan Milkov

11

リクエストは現在サポートされておらずasyncio、そのようなサポートを提供する予定はありません。使用方法を知っているカスタムの「トランスポートアダプタ」(ここで説明)を実装できる可能性がありますasyncio

少し時間があると気が付いたら、実際に調べてみるかもしれませんが、何も約束できません。


404へのリンクリード
CodeBiker

8

Pythonおよびasyncioを使用したPimin Konstantin Kefaloukos Easy parallel 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)1つのリクエストが失敗したりタイムアウトしたりする可能性があるので、私はできませんか?
Sanandrea

1
ThreadPoolExecutorを使用するだけで同じことができるのに、なぜasyncioが必要なのか説明できますか?
Asaf Pinhassi

@lya Rusin何に基づいて、max_workersの数を設定しますか?CPUとスレッドの数に関係がありますか?
alt-f4
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.