Pythonリクエストを使用した非同期リクエスト


142

Pythonのリクエストライブラリのドキュメントに含まれているサンプルを試しました。

ではasync.map(rs)、応答コードを取得していますが、要求された各ページのコンテンツを取得したいと考えています。たとえば、これは機能しません。

out = async.map(rs)
print out[0].content

多分あなたが得ている応答は空の本文を持っていますか?
Mariusz Jamro

私のために働く。発生している完全なエラーを投稿してください。
チューイー

エラーはありません。提供されたテストURLによって永久に実行されるだけです。
trbck

httpsでURLを使用すると明らかに表示されます。httpは
正常に

requests-threads現在存在しているようです。
OrangeDog

回答:


154

注意

以下の回答は、v0.13.0以降のリクエストに適用されません。この質問が書かれた後、非同期機能はgrequestsに移動しました。しかし、あなただけ置き換えることができrequestsgrequests、以下の、それが動作するはずです。

v0.13.0未満のリクエストの使用に関する元の質問を反映するために、この回答はそのままにしておきます。


async.map 非同期で複数のタスクを実行するには、以下を行う必要があります。

  1. 各オブジェクト(タスク)で実行したい機能を定義します
  2. その関数をリクエストのイベントフックとして追加します
  3. async.mapすべてのリクエスト/アクションのリストを呼び出す

例:

from requests import async
# If using requests > v0.13.0, use
# from grequests import async

urls = [
    'http://python-requests.org',
    'http://httpbin.org',
    'http://python-guide.org',
    'http://kennethreitz.com'
]

# A simple task to do to each response object
def do_something(response):
    print response.url

# A list to hold our things to do via async
async_list = []

for u in urls:
    # The "hooks = {..." part is where you define what you want to do
    # 
    # Note the lack of parentheses following do_something, this is
    # because the response will be used as the first argument automatically
    action_item = async.get(u, hooks = {'response' : do_something})

    # Add the task to our list of things to do via async
    async_list.append(action_item)

# Do our list of things to do via async
async.map(async_list)

2
コメントを残したのはいい考えです:最新のリクエストとgrequestsの間の互換性の問題(リクエスト1.1.0のmax_retriesオプションの欠如)のため、非同期を取得するためにリクエストをダウングレードする必要があり、非同期機能がバージョン0.13以降で移動していることがわかりました(pypi.python.org/pypi/requests
outforawhile

1
ばかげた質問:単なるリクエストとは対照的に、grequestsを使用する速度の向上は何ですか?リクエストに関してどのような制限がありますか?たとえば、3500リクエストをasync.mapに入れても大丈夫でしょうか?
垂れ下がる2014年

10
from grequests import async動作しません..そしてこの何かの定義が私にとってはうまくいきますdef do_something(response, **kwargs):、私はstackoverflow.com/questions/15594015/…
Allan Ruin

3
それでもasync.map呼び出しがブロックする場合、これはどのように非同期ですか?リクエスト自体が非同期で送信されることに加えて、取得はまだ同期ですか?
ブライアンフ

3
交換from requests import asyncによってimport grequests as async私のために働きました。
Martin Thoma、

80

asyncは現在、独立したモジュールですgrequests

ここを参照してください:https//github.com/kennethreitz/grequests

そしてそこに:Python経由で複数のHTTPリクエストを送信するための理想的な方法は?

インストール:

$ pip install grequests

使用法:

スタックを構築します。

import grequests

urls = [
    'http://www.heroku.com',
    'http://tablib.org',
    'http://httpbin.org',
    'http://python-requests.org',
    'http://kennethreitz.com'
]

rs = (grequests.get(u) for u in urls)

スタックを送る

grequests.map(rs)

結果は次のようになります

[<Response [200]>, <Response [200]>, <Response [200]>, <Response [200]>, <Response [200]>]

grequestsは同時リクエスト、つまり複数のリクエストが同じサーバーに送信される場合の制限を設定していないようです。


11
同時リクエストの制限に関して-map()/ imap()の実行時にプールサイズを指定できます。つまり、grequests.map(rs、size = 20)は、20の同時グラブを持ちます。
シンセサイザ

1
現在、これはpython3-capableではありません(geventはpy3.4でv2.6をビルドできません)。
saarp 2014

1
非同期部分がよくわかりません。私が許可すればresults = grequests.map(rs)、コードをこの行がブロックされた後、私は非同期効果を見ることができますか?
Allan Ruin

47

requests-futuresgrequestsの両方をテストしました。Grequestsはより高速ですが、サルのパッチと依存関係に関する追加の問題をもたらします。requests-futuresはgrequestsより数倍遅いです。私は独自のリクエストをThreadPoolExecutorに単純にラップすることを決定し、grequestsとほぼ同じ速さでしたが、外部の依存関係はありませんでした。

import requests
import concurrent.futures

def get_urls():
    return ["url1","url2"]

def load_url(url, timeout):
    return requests.get(url, timeout = timeout)

with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:

    future_to_url = {executor.submit(load_url, url, 10): url for url in     get_urls()}
    for future in concurrent.futures.as_completed(future_to_url):
        url = future_to_url[future]
        try:
            data = future.result()
        except Exception as exc:
            resp_err = resp_err + 1
        else:
            resp_ok = resp_ok + 1

ここではどのような種類の例外が発生する可能性がありますか?
スローハリー

requests.exceptions.Timeout
Hodza 2015年

2
すみません、あなたの質問が理解できません。複数のスレッドで単一のURLのみを使用しますか?1つのケースのDDoS攻撃のみ))
Hodza 2015年

1
この回答がなぜ多くの賛成票を獲得したのか理解できません。OPの質問は非同期リクエストに関するものでした。ThreadPoolExecutorはスレッドを実行します。はい、複数のスレッドで要求を出すことができますが、それは非同期プログラムになることはないので、元の質問に対する答えはどうでしょうか?
nagylzs

1
実際、問題はURLを並列でロードする方法についてでした。そして、はい、スレッドプールエグゼキューターは最良のオプションではありません。非同期ioを使用する方が良いですが、Pythonではうまく機能します。そして、なぜスレッドを非同期に使用できなかったのかわかりませんか?CPUバウンドタスクを非同期で実行する必要がある場合はどうなりますか?
Hodza

29

多分requests-futuresは別の選択肢です。

from requests_futures.sessions import FuturesSession

session = FuturesSession()
# first request is started in background
future_one = session.get('http://httpbin.org/get')
# second requests is started immediately
future_two = session.get('http://httpbin.org/get?foo=bar')
# wait for the first request to complete, if it hasn't already
response_one = future_one.result()
print('response one status: {0}'.format(response_one.status_code))
print(response_one.content)
# wait for the second request to complete, if it hasn't already
response_two = future_two.result()
print('response two status: {0}'.format(response_two.status_code))
print(response_two.content)

オフィス文書にもオススメです。geventを使用したくない場合は、それが適切です。


1
最も簡単なソリューションの1つ。max_workersパラメータを定義することで、同時リクエストの数を増やすことができます
Jose Cherian

1
スケーリングされたこの例を見るとよいので、ループするためにアイテムごとに1つの変数名を使用していません。
user1717828 2017年

リクエストごとに1つのスレッドを持つことは、リソースの浪費です!たとえば500リクエストを同時に実行することはできません。CPUが強制終了されます。これは決して良い解決策と考えるべきではありません。
Corneliu Maftuleac、2018

@CorneliuMaftuleac良い点。スレッドの使用に関しては、必ずスレッドに注意する必要があり、ライブラリにはスレッドプールまたは処理プールを有効にするオプションが用意されています。ThreadPoolExecutor(max_workers=10)
Dreampuf 2018

@Dreampuf処理プールはさらに悪いと思いますか?
Corneliu Maftuleac 2018

11

私は投稿された回答のほとんどに多くの問題があります-それらは制限された機能で移植された非推奨のライブラリを使用するか、またはリクエストの実行にあまりにも多くの魔法のあるソリューションを提供し、エラー処理を困難にします。上記のカテゴリのいずれにも該当しない場合は、サードパーティのライブラリであるか、廃止されています。

一部のソリューションは純粋にhttpリクエストで問題なく機能しますが、ソリューションは他の種類のリクエストには不十分です。ここでは高度にカスタマイズされたソリューションは必要ありません。

Python組み込みライブラリを使用するだけでasyncio、あらゆるタイプの非同期リクエストを実行できるだけでなく、複雑でユースケース固有のエラー処理に十分な流動性を提供できます。

import asyncio

loop = asyncio.get_event_loop()

def do_thing(params):
    async def get_rpc_info_and_do_chores(id):
        # do things
        response = perform_grpc_call(id)
        do_chores(response)

    async def get_httpapi_info_and_do_chores(id):
        # do things
        response = requests.get(URL)
        do_chores(response)

    async_tasks = []
    for element in list(params.list_of_things):
       async_tasks.append(loop.create_task(get_chan_info_and_do_chores(id)))
       async_tasks.append(loop.create_task(get_httpapi_info_and_do_chores(ch_id)))

    loop.run_until_complete(asyncio.gather(*async_tasks))

仕組みは簡単です。非同期で発生させたい一連のタスクを作成し、それらのタスクを実行して完了時に終了するようループに要求しています。メンテナンスが不足したり、必要な機能が不足したりすることのない追加のライブラリはありません。


2
私が正しく理解している場合、これはGRPCおよびHTTP呼び出しを実行しているときにイベントループをブロックしますか?これらの呼び出しが完了するまで数秒かかる場合、イベントループ全体が数秒間ブロックされますか?これを回避するには、GRPCまたはHTTPライブラリであるを使用する必要がありasyncます。次に、たとえば、行うことができます await response = requests.get(URL)。番号?
Coder Nr 23

残念ながら、これを試してみるとrequests、URLのリストを同期的に呼び出すよりも、ラッパーを作成する方がやや高速(場合によっては低速)であることがわかりました。たとえば、上記の戦略を使用して10回応答するために3秒かかるエンドポイントを要求すると、約30秒かかります。真のasyncパフォーマンスが必要な場合は、などを使用する必要がありますaiohttp
DragonBobZ

8

私はこれがしばらくクローズされていることを知っていますが、要求ライブラリに基づいて構築された別の非同期ソリューションを促進することは有用であると思いました。

list_of_requests = ['http://moop.com', 'http://doop.com', ...]

from simple_requests import Requests
for response in Requests().swarm(list_of_requests):
    print response.content

ドキュメントはこちらです:http : //pythonhosted.org/simple-requests/


@YSY問題を投稿してください:github.com/ctheiss/simple-requests/issues ; 文字通り、このライブラリを1日に何千回も使用しています。
Monkey Boson

ボストン、404/500エラーをどのように処理しますか?https URLはどうですか?数千のURLをサポートするスニッピングを評価します。例を貼り付けてもらえますか?感謝
YSY

@YSYデフォルトでは、404/500エラーは例外を発生させます。この動作はオーバーライドできます(pythonhosted.org/simple-requests/…を参照)。HTTPSのURLはgeventに依存しているためトリッキーですが、現在これには未解決のバグがあります(github.com/gevent/gevent/issues/477)。そこが実行できるチケットでシムはあるが、それはまだSNIサーバー用の警告がスローされます(それがされます仕事します)。スニッピングに関しては、私の使用法はすべて私の会社にあり、閉鎖されていると思います。しかし、数十のジョブで数千のリクエストを実行することを保証します。
モンキーボソン2015

インタラクションに関してライブラリは洗練されているように見えます。Python3 +は使用可能ですか?申し訳ありませんでした。
アイザックフィリップ

@Jethro絶対的に正しい、ライブラリが基盤となる技術は今のためのPython 3でかなり異なっているので、トータルの再書き込みが必要になり、ライブラリが「完全」であるだけPythonの2のために働く
モンキーボソン

4
threads=list()

for requestURI in requests:
    t = Thread(target=self.openURL, args=(requestURI,))
    t.start()
    threads.append(t)

for thread in threads:
    thread.join()

...

def openURL(self, requestURI):
    o = urllib2.urlopen(requestURI, timeout = 600)
    o...

4
これはスレッド内の「通常の」要求です。悪い例ではない購入はトピック外です。
Nick


2

私はしばらくの間、githubのgist APIに対する非同期呼び出しにpythonリクエストを使用しています。

例については、こちらのコードをご覧ください。

https://github.com/davidthewatson/flasgist/blob/master/views.py#L60-72

このスタイルのpythonは明確な例ではないかもしれませんが、コードが機能することを保証できます。これがあなたを混乱させるかどうか教えてください、私はそれを文書化します。


2

httpxそのために使用できます。

import httpx

async def get_async(url):
    async with httpx.AsyncClient() as client:
        return await client.get(url)

urls = ["http://google.com", "http://wikipedia.org"]

# Note that you need an async context to use `await`.
await asyncio.gather(*map(get_async, urls))

関数構文が必要な場合、gamla libはこれをにラップしget_asyncます。

その後、行うことができます


await gamla.map(gamla.get_async(10), ["http://google.com", "http://wikipedia.org"])

これ10は秒単位のタイムアウトです。

(免責事項:私はその著者です)


そしてrespx:)テスト/あざけるために
RLAT

0

また、Pythonの非同期メソッドを使用していくつかのことを試しましたが、非同期プログラミングにツイストを使用した方がずっとうまくいきました。問題が少なく、十分に文書化されています。これは、あなたがねじれで試みているものに類似した何かのリンクです。

http://pythonquirks.blogspot.com/2011/04/twisted-asynchronous-http-request.html


ツイストは昔ながらです。代わりにHTTPXを使用してください。
AmirHossein
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.