なぜrequests.get()が返らないのですか?requests.get()が使用するデフォルトのタイムアウトは何ですか?


92

私のスクリプトでrequests.getは、決して返しません:

import requests

print ("requesting..")

# This call never returns!
r = requests.get(
    "http://www.some-site.com",
    proxies = {'http': '222.255.169.74:8080'},
)

print(r.ok)

考えられる理由は何ですか?何か治療法は?get使用するデフォルトのタイムアウトは何ですか?


1
@ user2357112:それは重要ですか?私は疑う。
Nawaz 2013

それは間違いなく重要です。アクセスしようとしているURLと使用しようとしているプロキシを指定すると、同様のリクエストを送信しようとするとどうなるかを確認できます。
user2357112は2013

1
@ user2357112:よし。質問を編集しました。
Nawaz 2013

2
プロキシも正しくありません。次のように指定する必要がありますproxies={'http': 'http://222.255.169.74:8080'}。それがタイムアウトなしでは完了しない理由かもしれません。
Ian Stapleton Cordasco 2013

回答:


127

getが使用するデフォルトのタイムアウトは何ですか?

デフォルトのタイムアウトは None。つまり、接続が閉じるまで待機(ハング)します。

タイムアウト値を渡すとどうなりますか?

r = requests.get(
    'http://www.justdial.com',
    proxies={'http': '222.255.169.74:8080'},
    timeout=5
)

3
私はあなたが正しいと思います。None無限(または「接続が閉じるまで待つ」)を意味します。タイムアウトを自分で渡すと、戻ります!
Nawaz 2013

14
@Userタイムアウトは、httpと同様にhttpsでも同じように機能します
jaapz

これは、グーグルなどでドキュメントで見つけるのが本当に難しいようです。これがドキュメントのどこに表示されるか誰でも知っていますか?
ワードワード、2017年


おかげでprint(requests.request.__doc__)、IPython での作業は、私が探していたものよりも多くなりました。他にどんなオプションの引数があったのかと思ってrequest.get()いました。
ワードフォーザワイズ

39

リクエストドキュメントから:

タイムアウトパラメータを使用して、指定された秒数後に応答の待機を停止するようリクエストに要求できます。

>>> requests.get('http://github.com', timeout=0.001)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
requests.exceptions.Timeout: HTTPConnectionPool(host='github.com', port=80): Request timed out. (timeout=0.001)

注意:

タイムアウトは、応答ダウンロード全体の時間制限ではありません。むしろ、サーバーがタイムアウト秒数にわたって応答を発行しなかった場合(より正確には、基礎となるソケットでタイムアウト秒数の間バイトが受信されなかった場合)、例外が発生します。

request.get()が戻るのに非常に長い時間がかかることは、私にはよく起こります。 timeout 1秒でます。この問題を解決する方法はいくつかあります。

1.を使用 TimeoutSauce内部クラスを

送信元:https : //github.com/kennethreitz/requests/issues/1928#issuecomment-35811896

import requests from requests.adapters import TimeoutSauce

class MyTimeout(TimeoutSauce):
    def __init__(self, *args, **kwargs):
        if kwargs['connect'] is None:
            kwargs['connect'] = 5
        if kwargs['read'] is None:
            kwargs['read'] = 5
        super(MyTimeout, self).__init__(*args, **kwargs)

requests.adapters.TimeoutSauce = MyTimeout

このコードにより、読み取りタイムアウトが接続タイムアウトと同じに設定されます。これは、Session.get()呼び出しで渡すタイムアウト値です。(私は実際にこのコードをテストしていないため、簡単なデバッグが必要になる可能性があるので、GitHubウィンドウに直接書き込んだだけです。)

2. kevinburkeからのリクエストのフォークを使用します: https : //github.com/kevinburke/requests/tree/connect-timeout

そのドキュメントから:https : //github.com/kevinburke/requests/blob/connect-timeout/docs/user/advanced.rst

次のように、タイムアウトに単一の値を指定した場合:

r = requests.get('https://github.com', timeout=5)

タイムアウト値は、接続タイムアウトと読み取りタイムアウトの両方に適用されます。値を個別に設定する場合は、タプルを指定します。

r = requests.get('https://github.com', timeout=(3.05, 27))

注:変更はメインのリクエストプロジェクトにマージされていますます。

3. evenletまたはsignal同様の質問ですでに述べたように、 Pythonリクエストのタイムアウト。応答全体を取得する


7
あなたはデフォルトが何であるか答えたことはありません
ユーザー

引用:タイムアウトパラメータを使用して、指定された秒数後に応答の待機を停止するように要求に指示できます。ほぼすべての製品コードは、ほぼすべてのリクエストでこのパラメーターを使用する必要があります。そうしないと、プログラムが無期限にハングする可能性があります。タイムアウトは、応答のダウンロード全体の時間制限ではないことに注意してください。むしろ、サーバーがタイムアウト秒数にわたって応答を発行しなかった場合(より正確には、基礎となるソケットでタイムアウト秒数の間バイトが受信されなかった場合)、例外が発生します。タイムアウトが明示的に指定されていない場合、リクエストはタイムアウトしません。
Dデイ2017

コードは、タイプミスがありますrequests.adaptersからのインポート要求<ここに新しい行が> TimeoutSauceをインポート
シナンÇetinkaya

4

コードの束にデフォルトのタイムアウトを簡単に追加したかった(そのタイムアウトで問題が解決すると仮定して)

これは、リクエストのリポジトリに送信されたチケットからピックアップしたソリューションです。

クレジット: https //github.com/kennethreitz/requests/issues/2011#issuecomment-477784399

解決策はここの最後の数行ですが、より良いコンテキストのために、より多くのコードを示しています。セッションを再試行動作に使用するのが好きです。

import requests
import functools
from requests.adapters import HTTPAdapter,Retry


def requests_retry_session(
        retries=10,
        backoff_factor=2,
        status_forcelist=(500, 502, 503, 504),
        session=None,
        ) -> requests.Session:
    session = session or requests.Session()
    retry = Retry(
            total=retries,
            read=retries,
            connect=retries,
            backoff_factor=backoff_factor,
            status_forcelist=status_forcelist,
            )
    adapter = HTTPAdapter(max_retries=retry)
    session.mount('http://', adapter)
    session.mount('https://', adapter)
    # set default timeout
    for method in ('get', 'options', 'head', 'post', 'put', 'patch', 'delete'):
        setattr(session, method, functools.partial(getattr(session, method), timeout=30))
    return session

それからあなたはこのようなことをすることができます:

requests_session = requests_retry_session()
r = requests_session.get(url=url,...

4

すべての回答を確認し、問題がまだ存在していると結論付けました。一部のサイトではリクエストが無限にハングし、マルチプロセッシングを使用するのはやり過ぎのようです。これが私のアプローチです(Python 3.5+):

import asyncio

import aiohttp


async def get_http(url):
    async with aiohttp.ClientSession(conn_timeout=1, read_timeout=3) as client:
        try:
            async with client.get(url) as response:
                content = await response.text()
                return content, response.status
        except Exception:
            pass


loop = asyncio.get_event_loop()
task = loop.create_task(get_http('http://example.com'))
loop.run_until_complete(task)
result = task.result()
if result is not None:
    content, status = task.result()
    if status == 200:
        print(content)

更新

conn_timeoutとread_timeoutの使用に関する非推奨の警告を受け取った場合、ClientTimeoutデータ構造の使用方法について、このリファレンスの最後近くを確認してください。上記の元のコードへのリンクされた参照ごとにこのデータ構造を適用する1つの簡単な方法は、次のとおりです。

async def get_http(url):
    timeout = aiohttp.ClientTimeout(total=60)
    async with aiohttp.ClientSession(timeout=timeout) as client:
        try:
            etc.

2
@Nawaz Python 3.5以降。質問ありがとうございました。Pythonバージョンで回答を更新しました。これは合法的なPythonコードです。aiohttpのドキュメントaiohttp.readthedocs.io/en/stable/index.html
Alex Polekha

これで他の方法では解決できない問題が解決しました。Py 3.7。depricationsのため、使用する必要があった...タイムアウト= aiohttp.ClientTimeout(total = 60)aiohttp.ClientSession(timeout = timeout)とクライアントとして非同期:
Thom Ives

2

文書化された「送信」関数にパッチを適用すると、すべての要求に対してこれが修正されます-多くの依存ライブラリやSDKでも同様です。libsにパッチを適用するときは、必ずTimeoutSauceではなく、サポートされている/ドキュメント化された関数にパッチを適用してください。そうしないと、パッチの効果が失われて静かに失われる可能性があります。

import requests

DEFAULT_TIMEOUT = 180

old_send = requests.Session.send

def new_send(*args, **kwargs):
     if kwargs.get("timeout", None) is None:
         kwargs["timeout"] = DEFAULT_TIMEOUT
     return old_send(*args, **kwargs)

requests.Session.send = new_send

タイムアウトがないことの影響は非常に深刻であり、デフォルトのタイムアウトを使用しても、TCP自体にもデフォルトのタイムアウトがあるため、ほとんど何も壊れることはありません。


0

私の場合、「requests.get never戻りません」の理由は、最初にipv6 ipで解決されたホストrequests.get()接続しようとするためです。そのipv6 ipの接続に問題が発生してスタックする場合、明示的に設定してタイムアウトに達した場合にのみ、ipv4 ipを再試行しtimeout=<N seconds>ます。

私の解決策は、Python をサルにパッチしipv6(またはipv4が機能しない場合はipv4)socket無視することです。この答えまたはこの答えは私にとってはうまくいきます。

ipv6の完了を待たずにipv4に接続するcurlため、なぜコマンドが機能するのか疑問に思われるかもしれませんcurlstrace -ff -e network -s 10000 -- curl -vLk '<your url>'コマンドを使用して、ソケットのsyscallをトレースできます。Pythonの場合、strace -ff -e network -s 10000 -- python3 <your python script>コマンドを使用できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.