Warum kehrt request.get () nicht zurück? Was ist das Standardzeitlimit, das request.get () verwendet?

92

Gibt in meinem Skript requests.getniemals zurück:

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)

Was könnte der mögliche Grund sein? Irgendein Mittel? Was ist das Standardzeitlimit, das getverwendet wird?

Nawaz
quelle
1
@ user2357112: Ist das wichtig? Ich bezweifle.
Nawaz
Es ist definitiv wichtig. Wenn Sie die URL angeben, auf die Sie zugreifen möchten, und den Proxy, den Sie verwenden möchten, können wir sehen, was passiert, wenn wir versuchen, ähnliche Anforderungen zu senden.
user2357112 unterstützt Monica
1
@ user2357112: In Ordnung. Die Frage wurde bearbeitet.
Nawaz
2
Ihr Proxy ist ebenfalls falsch. Sie müssen es wie folgt angeben : proxies={'http': 'http://222.255.169.74:8080'}. Das könnte der Grund sein, warum es nicht ohne Zeitüberschreitung abgeschlossen wird.
Ian Stapleton Cordasco

Antworten:

126

Was ist das Standardzeitlimit, das verwendet wird?

Das Standardzeitlimit ist None, was bedeutet, dass es wartet (hängt), bis die Verbindung geschlossen wird.

Was passiert, wenn Sie einen Timeout-Wert übergeben?

r = requests.get(
    'http://www.justdial.com',
    proxies={'http': '222.255.169.74:8080'},
    timeout=5
)
Ron Rothman
quelle
3
Ich denke, du hast recht. Nonebedeutet unendlich (oder "warten, bis die Verbindung geschlossen ist"). Wenn ich selbst eine Zeitüberschreitung habe, kehrt sie zurück!
Nawaz
14
@ Benutzer Timeout funktioniert mit https genauso gut wie mit http
jaapz
Dies scheint wirklich schwer in den Dokumenten durch googeln oder auf andere Weise zu finden. Weiß jemand, wo dies in den Dokumenten auftaucht?
Worte für den
Vielen Dank, print(requests.request.__doc__)IPython ist mehr das, wonach ich gesucht habe. Ich habe mich gefragt, welche anderen optionalen Argumente es request.get()gibt.
Worte für den
39

Aus der Anforderungsdokumentation :

Mit dem Parameter timeout können Sie Requests anweisen, nach einer bestimmten Anzahl von Sekunden nicht mehr auf eine Antwort zu warten:

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

Hinweis:

Das Zeitlimit ist kein Zeitlimit für den gesamten Antwortdownload. Stattdessen wird eine Ausnahme ausgelöst, wenn der Server für Timeout-Sekunden keine Antwort ausgegeben hat (genauer gesagt, wenn für Timeout-Sekunden keine Bytes auf dem zugrunde liegenden Socket empfangen wurden).

Es passiert mir sehr oft, dass die Rückkehr von request.get () sehr lange dauert, selbst wenn die timeout1 Sekunde beträgt. Es gibt einige Möglichkeiten, um dieses Problem zu lösen:

1. Verwenden Sie die TimeoutSauceinterne Klasse

Von: 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

Dieser Code sollte dazu führen, dass wir das Lesezeitlimit gleich dem Verbindungszeitlimit setzen. Dies ist das Zeitlimit, das Sie bei Ihrem Aufruf von Session.get () übergeben. (Beachten Sie, dass ich diesen Code noch nicht getestet habe, sodass möglicherweise ein schnelles Debuggen erforderlich ist. Ich habe ihn einfach direkt in das GitHub-Fenster geschrieben.)

2. Verwenden Sie eine Reihe von Anfragen von Kevin Burke: https://github.com/kevinburke/requests/tree/connect-timeout

Aus der Dokumentation: https://github.com/kevinburke/requests/blob/connect-timeout/docs/user/advanced.rst

Wenn Sie einen einzelnen Wert für das Zeitlimit wie folgt angeben:

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

Der Zeitüberschreitungswert wird sowohl auf die Verbindungszeit als auch auf die Lesezeitüberschreitung angewendet. Geben Sie ein Tupel an, wenn Sie die Werte separat festlegen möchten:

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

HINWEIS: Die Änderung wurde inzwischen mit dem Hauptanforderungsprojekt zusammengeführt .

3. Verwenden evenletoder signalwie bereits in der ähnlichen Frage erwähnt: Zeitüberschreitung für Python-Anforderungen. Erhalten Sie die gesamte Antwort

Hieu
quelle
7
Sie haben nie geantwortet, was die Standardeinstellung ist
Benutzer
Quote: Mit dem Parameter timeout können Sie Requests anweisen, nach einer bestimmten Anzahl von Sekunden nicht mehr auf eine Antwort zu warten. Nahezu jeder Produktionscode sollte diesen Parameter in fast allen Anforderungen verwenden. Andernfalls kann Ihr Programm auf unbestimmte Zeit hängen bleiben: Beachten Sie, dass das Zeitlimit keine zeitliche Begrenzung für den gesamten Antwortdownload darstellt. Stattdessen wird eine Ausnahme ausgelöst, wenn der Server für Timeout-Sekunden keine Antwort ausgegeben hat (genauer gesagt, wenn für Timeout-Sekunden keine Bytes auf dem zugrunde liegenden Socket empfangen wurden). Wenn kein Zeitlimit explizit angegeben wird, tritt bei Anforderungen kein Zeitlimit auf.
DDay
Code hat einen Tippfehler: Importieren von Anfragen <neue Zeile hier> von request.adapters importieren TimeoutSauce
Sinan Çetinkaya
4

Ich wollte ein Standard-Timeout, das einfach zu einer Reihe von Codes hinzugefügt werden kann (vorausgesetzt, das Timeout löst Ihr Problem).

Dies ist die Lösung, die ich von einem Ticket erhalten habe, das an das Repository für Anfragen gesendet wurde.

Gutschrift: https://github.com/kennethreitz/requests/issues/2011#issuecomment-477784399

Die Lösung sind die letzten paar Zeilen hier, aber ich zeige mehr Code für einen besseren Kontext. Ich verwende gerne eine Sitzung für Wiederholungsverhalten.

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

dann kannst du so etwas machen:

requests_session = requests_retry_session()
r = requests_session.get(url=url,...
Tim Richardson
quelle
4

Überprüfte alle Antworten und kam zu dem Schluss, dass das Problem immer noch besteht. Auf einigen Websites können Anfragen unendlich hängen bleiben und die Verwendung von Multiprocessing scheint übertrieben zu sein. Hier ist mein Ansatz (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)

AKTUALISIEREN

Wenn Sie eine deprecation Warnung conn_timeout und read_timeout, Scheck nahe der Unterseite über die Verwendung erhalten DIESE Referenz für die ClientTimeout Datenstruktur zu verwenden. Eine einfache Möglichkeit, diese Datenstruktur gemäß dem verknüpften Verweis auf den obigen Originalcode anzuwenden, wäre:

async def get_http(url):
    timeout = aiohttp.ClientTimeout(total=60)
    async with aiohttp.ClientSession(timeout=timeout) as client:
        try:
            etc.
Alex Polekha
quelle
2
@Nawaz Python 3.5+. Vielen Dank für die Frage, aktualisiert die Antwort mit Python-Version. Es ist legaler Python-Code. Bitte werfen Sie einen Blick auf aiohttp Dokumentation aiohttp.readthedocs.io/en/stable/index.html
Alex Polekha
Dies löste meine Probleme, wenn andere Methoden dies nicht tun würden. Py 3.7. Aufgrund von Deprivationen musste ... timeout = aiohttp.ClientTimeout (total = 60) asynchron mit aiohttp.ClientSession (timeout = timeout) als Client verwendet werden:
Thom Ives
2

Durch das Patchen der dokumentierten "Sende" -Funktion wird dies für alle Anforderungen behoben - auch in vielen abhängigen Bibliotheken und SDKs. Stellen Sie beim Patchen von Bibliotheken sicher, dass Sie unterstützte / dokumentierte Funktionen patchen, nicht TimeoutSauce. Andernfalls verlieren Sie möglicherweise stillschweigend die Wirkung Ihres Patches.

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

Die Auswirkungen einer fehlenden Zeitüberschreitung sind sehr schwerwiegend, und die Verwendung einer Standardzeitüberschreitung kann fast nie zu einer Unterbrechung führen, da TCP selbst auch Standardzeitüberschreitungen aufweist.

Erik Aronesty
quelle
0

In meinem Fall liegt der Grund für "Anfragen.get kehrt nie zurück" darin, dass zuerst requests.get()versucht wurde, eine Verbindung zu dem mit IPv6 IP aufgelösten Host herzustellen . Wenn etwas schief gelaufen ist, um diese IPv6-IP zu verbinden und hängen zu bleiben, wird die IPv4-IP nur dann wiederholt, wenn ich timeout=<N seconds>das Timeout explizit festgelegt und erreicht habe.

Meine Lösung besteht darin, die Python- Affen zu patchensocket , um IPv6 zu ignorieren (oder IPv4, wenn IPv4 nicht funktioniert). Entweder diese Antwort oder diese Antwort funktioniert für mich.

Sie fragen sich vielleicht, warum der curlBefehl funktioniert, weil Sie curlipv4 verbinden, ohne auf den Abschluss von ipv6 zu warten. Sie können die Socket-Systemaufrufe mit dem strace -ff -e network -s 10000 -- curl -vLk '<your url>'Befehl verfolgen . Für Python kann der strace -ff -e network -s 10000 -- python3 <your python script>Befehl verwendet werden.

Obst
quelle