it-swarm.com.de

Timeout für Python-Anfragen. Die gesamte Antwort erhalten

Ich sammle Statistiken in einer Liste von Websites und verwende Anfragen zur Vereinfachung. Hier ist mein Code:

data=[]
websites=['http://google.com', 'http://bbc.co.uk']
for w in websites:
    r= requests.get(w, verify=False)
    data.append( (r.url, len(r.content), r.elapsed.total_seconds(), str([(l.status_code, l.url) for l in r.history]), str(r.headers.items()), str(r.cookies.items())) )

Jetzt möchte ich, dass requests.get nach 10 Sekunden abläuft, damit die Schleife nicht hängen bleibt. 

Diese Frage war auch von Interesse vorher , aber keine der Antworten ist sauber. Ich werde etwas Kopfgeld darauf setzen, um eine schöne Antwort zu erhalten.

Ich höre, dass es vielleicht eine gute Idee ist, Anfragen nicht zu verwenden. (die im Tupel)

85
Kiarash

Was ist mit dem Eventlet? Wenn Sie die Anforderung nach 10 Sekunden abbrechen möchten, funktioniert das Snippet auch dann, wenn Daten empfangen werden.

import requests
import eventlet
eventlet.monkey_patch()

with eventlet.Timeout(10):
    requests.get("http://ipv4.download.thinkbroadband.com/1GB.Zip", verify=False)
92
Alvaro

Stellen Sie den Parameter timeout ein :

r = requests.get(w, verify=False, timeout=10)

Solange Sie stream=True für diese Anforderung nicht festlegen, führt dies zu einem Timeout des Aufrufs von requests.get(), wenn die Verbindung länger als zehn Sekunden dauert oder der Server keine Daten mehr als zehn Sekunden sendet.

129
Lukasa

UPDATE: http://docs.python-requests.org/de/master/user/advanced/#timeouts

In der neuen Version von requests:

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

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

Der Timeout-Wert wird auf die Timeouts connect und read angewendet. Geben Sie ein Tuple an, wenn Sie die Werte separat einstellen möchten:

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

Wenn der Remote-Server sehr langsam ist, können Sie Requests anweisen, für immer auf eine Antwort zu warten, indem Sie None als Timeout-Wert übergeben und dann eine Tasse Kaffee abrufen.

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

Meine alte (wahrscheinlich veraltete) Antwort (die vor langer Zeit gepostet wurde):

Es gibt andere Wege, um dieses Problem zu überwinden:

1. Verwenden Sie die TimeoutSauce interne 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):
        connect = kwargs.get('connect', 5)
        read = kwargs.get('read', connect)
        super(MyTimeout, self).__init__(connect=connect, read=read)

requests.adapters.TimeoutSauce = MyTimeout

Dieser Code sollte dazu führen, dass das Lesezeitlimit dem Wert von .__ entspricht. Verbindungszeitüberschreitung, der Zeitüberschreitungswert, den Sie in Ihrem .__ übergeben haben. Session.get () aufrufen. (Beachten Sie, dass ich diesen Code nicht wirklich getestet habe. Daher kann es etwas schnelles Debuggen erforderlich sein. Ich habe ihn gerade direkt in das GitHub-Fenster geschrieben.)

2. Verwenden Sie einen Fork von Anfragen von kevinburke: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 Timeout angeben, wie folgt:

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

Der Zeitüberschreitungswert wird sowohl auf die Verbindung als auch auf die Leseoperation angewendet. Timeouts. Geben Sie ein Tuple an, wenn Sie die Werte festlegen möchten separat:

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

kevinburke hat beantragt es wird in das Hauptanforderungsprojekt eingebunden, aber es wurde noch nicht akzeptiert. 

45
Hieu

Um ein Timeout zu erstellen, können Sie Signale verwenden.

Der beste Weg, um diesen Fall zu lösen, ist wahrscheinlich zu

  1. Legen Sie eine Ausnahme als Handler für das Alarmsignal fest
  2. Rufen Sie das Alarmsignal mit einer Verzögerung von zehn Sekunden auf
  3. Rufen Sie die Funktion innerhalb eines try-except-finally-Blocks auf.
  4. Der Ausnahmeblock ist erreicht, wenn die Funktion abgelaufen ist.
  5. Im letzten Block wird der Alarm abgebrochen, so dass er später nicht mehr angezeigt wird.

Hier ist ein Beispielcode:

import signal
from time import sleep

class TimeoutException(Exception):
    """ Simple Exception to be called on timeouts. """
    pass

def _timeout(signum, frame):
    """ Raise an TimeoutException.

    This is intended for use as a signal handler.
    The signum and frame arguments passed to this are ignored.

    """
    # Raise TimeoutException with system default timeout message
    raise TimeoutException()

# Set the handler for the SIGALRM signal:
signal.signal(signal.SIGALRM, _timeout)
# Send the SIGALRM signal in 10 seconds:
signal.alarm(10)

try:    
    # Do our code:
    print('This will take 11 seconds...')
    sleep(11)
    print('done!')
except TimeoutException:
    print('It timed out!')
finally:
    # Abort the sending of the SIGALRM signal:
    signal.alarm(0)

Hier gibt es einige Einschränkungen:

  1. Es ist nicht threadsicher, Signale werden immer an den Haupt-Thread geliefert. Sie können dies also nicht in einen anderen Thread einfügen.
  2. Nach der Zeitplanung des Signals und der Ausführung des eigentlichen Codes tritt eine kurze Verzögerung auf. Dies bedeutet, dass das Beispiel selbst dann ausfällt, wenn es nur zehn Sekunden lang geschlafen hat.

Aber es ist alles in der Standard-Python-Bibliothek! Mit Ausnahme des Sleep-Funktion-Imports ist es nur ein Import. Wenn Sie Timeouts an vielen Stellen verwenden, können Sie TimeoutException, _timeout und das Singen in eine Funktion einfügen und einfach aufrufen. Oder Sie können einen Dekorateur erstellen und ihn auf Funktionen setzen, siehe Antwort unten.

Sie können dies auch als "context manager" einrichten, damit Sie es mit der with-Anweisung verwenden können:

import signal
class Timeout():
    """ Timeout for use with the `with` statement. """

    class TimeoutException(Exception):
        """ Simple Exception to be called on timeouts. """
        pass

    def _timeout(signum, frame):
        """ Raise an TimeoutException.

        This is intended for use as a signal handler.
        The signum and frame arguments passed to this are ignored.

        """
        raise Timeout.TimeoutException()

    def __init__(self, timeout=10):
        self.timeout = 10
        signal.signal(signal.SIGALRM, Timeout._timeout)

    def __enter__(self):
        signal.alarm(self.timeout)

    def __exit__(self, exc_type, exc_value, traceback):
        signal.alarm(0)
        return exc_type is Timeout.TimeoutException

# Demonstration:
from time import sleep

print('This is going to take maximum 10 seconds...')
with Timeout(10):
    sleep(15)
    print('No timeout?')
print('Done')

Ein möglicher Nachteil dieses Kontextmanagers ist, dass Sie nicht wissen können, ob der Code tatsächlich abgelaufen ist oder nicht.

Quellen und empfohlene Lektüre:

19
totokaka

Ab Januar 2019 können Sie das Argument timeout von requests verwenden, d.h.

requests.get(url, timeout=10)

Hinweis:

timeout ist keine zeitliche Begrenzung für den gesamten Antwortdownload. lieber, Eine Ausnahme wird ausgelöst, wenn der Server keine Antwort für .__ ausgegeben hat. Timeout-Sekunden (genauer gesagt, wenn auf dem -Dockel keine Bytes für Timeout-Sekunden empfangen wurden). Wenn kein Timeout angegeben ist Anforderungen haben keine Zeitüberschreitung.

16
Pedro Lobito

Dies kann ein Overkill sein, aber die verteilte Celery-Task-Warteschlange bietet eine gute Unterstützung für Timeouts.

Insbesondere können Sie ein weiches Zeitlimit definieren, das eine Ausnahme in Ihrem Prozess auslöst (damit Sie bereinigen können) und/oder ein hartes Zeitlimit, das die Task beendet, wenn das Zeitlimit überschritten wurde.

Unter den Umschlägen verwendet dies den gleichen Signalansatz wie in Ihrem "Vorher" -Posten, jedoch auf benutzerfreundliche und überschaubare Weise. Und wenn die Liste der Websites, die Sie überwachen, lang ist, könnten Sie von seiner Hauptfunktion profitieren - allen Arten von Möglichkeiten, die Ausführung einer großen Anzahl von Aufgaben zu verwalten.

4
Chris Johnson

Ich glaube, Sie können multiprocessing verwenden und müssen nicht von einem Drittanbieter-Paket abhängig sein:

import multiprocessing
import requests

def call_with_timeout(func, args, kwargs, timeout):
    manager = multiprocessing.Manager()
    return_dict = manager.dict()

    # define a wrapper of `return_dict` to store the result.
    def function(return_dict):
        return_dict['value'] = func(*args, **kwargs)

    p = multiprocessing.Process(target=function, args=(return_dict,))
    p.start()

    # Force a max. `timeout` or wait for the process to finish
    p.join(timeout)

    # If thread is still active, it didn't finish: raise TimeoutError
    if p.is_alive():
        p.terminate()
        p.join()
        raise TimeoutError
    else:
        return return_dict['value']

call_with_timeout(requests.get, args=(url,), kwargs={'timeout': 10}, timeout=60)

Das an kwargs übergebene Timeout ist das Zeitlimit für das Abrufen von any von dem Server. Das Argument timeout ist das Zeitlimit, um die vollständige response zu erhalten.

2
Jorge Leitão

Probieren Sie diese Anfrage mit Timeout und Fehlerbehandlung aus:

import requests
try: 
    url = "http://google.com"
    r = requests.get(url, timeout=10)
except requests.exceptions.Timeout as e: 
    print e
2
DaWe

dieser Code funktioniert für socketError 11004 und 10060 ......

# -*- encoding:UTF-8 -*-
__author__ = 'ACE'
import requests
from PyQt4.QtCore import *
from PyQt4.QtGui import *


class TimeOutModel(QThread):
    Existed = pyqtSignal(bool)
    TimeOut = pyqtSignal()

    def __init__(self, fun, timeout=500, parent=None):
        """
        @param fun: function or lambda
        @param timeout: ms
        """
        super(TimeOutModel, self).__init__(parent)
        self.fun = fun

        self.timeer = QTimer(self)
        self.timeer.setInterval(timeout)
        self.timeer.timeout.connect(self.time_timeout)
        self.Existed.connect(self.timeer.stop)
        self.timeer.start()

        self.setTerminationEnabled(True)

    def time_timeout(self):
        self.timeer.stop()
        self.TimeOut.emit()
        self.quit()
        self.terminate()

    def run(self):
        self.fun()


bb = lambda: requests.get("http://ipv4.download.thinkbroadband.com/1GB.Zip")

a = QApplication([])

z = TimeOutModel(bb, 500)
print 'timeout'

a.exec_()
1
ACEE

Trotz der Frage nach Anfragen finde ich das sehr einfach mit pycurlCURLOPT_TIMEOUT oder CURLOPT_TIMEOUT_MS.

Kein Einfädeln oder Signalisieren erforderlich:

import pycurl
import StringIO

url = 'http://www.example.com/example.Zip'
timeout_ms = 1000
raw = StringIO.StringIO()
c = pycurl.Curl()
c.setopt(pycurl.TIMEOUT_MS, timeout_ms)  # total timeout in milliseconds
c.setopt(pycurl.WRITEFUNCTION, raw.write)
c.setopt(pycurl.NOSIGNAL, 1)
c.setopt(pycurl.URL, url)
c.setopt(pycurl.HTTPGET, 1)
try:
    c.perform()
except pycurl.error:
    traceback.print_exc() # error generated on timeout
    pass # or just pass if you don't want to print the error
1
John Smith

Wenn es dazu kommt, erstelle einen Watchdog Thread, der den internen Status von Anfragen nach 10 Sekunden durcheinander bringt, z.

  • schließt den zugrunde liegenden Socket und im Idealfall
  • löst eine Ausnahme aus, wenn Requests den Vorgang wiederholen

Beachten Sie, dass Sie abhängig von den Systembibliotheken möglicherweise keine Frist für die DNS-Auflösung festlegen können.

0
Dima Tisnek

Wenn Sie die Option stream=True verwenden, können Sie Folgendes tun:

r = requests.get(
    'http://url_to_large_file',
    timeout=1,  # relevant only for underlying socket
    stream=True)

with open('/tmp/out_file.txt'), 'wb') as f:
    start_time = time.time()
    for chunk in r.iter_content(chunk_size=1024):
        if chunk:  # filter out keep-alive new chunks
            f.write(chunk)
        if time.time() - start_time > 8:
            raise Exception('Request took longer than 8s')

Die Lösung benötigt keine Signale oder Multiprocessing.

0
ub_marco

Stellen Sie stream=True ein und verwenden Sie r.iter_content(1024). Ja, eventlet.Timeout funktioniert für mich einfach nicht.

try:
    start = time()
    timeout = 5
    with get(config['source']['online'], stream=True, timeout=timeout) as r:
        r.raise_for_status()
        content = bytes()
        content_gen = r.iter_content(1024)
        while True:
            if time()-start > timeout:
                raise TimeoutError('Time out! ({} seconds)'.format(timeout))
            try:
                content += next(content_gen)
            except StopIteration:
                break
        data = content.decode().split('\n')
        if len(data) in [0, 1]:
            raise ValueError('Bad requests data')
except (exceptions.RequestException, ValueError, IndexError, KeyboardInterrupt,
        TimeoutError) as e:
    print(e)
    with open(config['source']['local']) as f:
        data = [line.strip() for line in f.readlines()]

Die Diskussion ist hier https://redd.it/80kp1h

0
Polv

zeitüberschreitung = (Verbindungszeitüberschreitung, Datenlesezeitüberschreitung) oder ein einzelnes Argument angeben (Zeitüberschreitung = 1)

import requests

try:
    req = requests.request('GET', 'https://www.google.com',timeout=(1,1))
    print(req)
except requests.ReadTimeout:
    print("READ TIME OUT")
0
Fayzan qureshi

Nun, ich habe viele Lösungen auf dieser Seite ausprobiert und habe immer noch Instabilitäten, zufällige Hänge und schlechte Verbindungsleistung.

Ich benutze jetzt Curl und bin wirklich glücklich über die "Max Time" -Funktionalität und über die globalen Leistungen, selbst bei einer so schlechten Implementierung:

content=commands.getoutput('curl -m6 -Ss "http://mywebsite.xyz"')

Hier habe ich einen maximalen Zeitparameter von 6 Sekunden definiert, der sowohl die Verbindungs- als auch die Übertragungszeit berücksichtigt.

Ich bin mir sicher, dass Curl eine Nice-Python-Bindung hat, wenn Sie sich lieber an die Pythonic-Syntax halten wollen :)

0
technico

Es gibt ein Paket namens timeout-decorator , mit dem Sie eine Python-Funktion zeitlich abbrechen können.

@timeout_decorator.timeout(5)
def mytest():
    print("Start")
    for i in range(1,10):
        time.sleep(1)
        print("{} seconds have passed".format(i))

Es verwendet den Signalansatz, den einige Antworten hier suggerieren. Alternativ können Sie es anweisen, Multiprocessing anstelle von Signalen zu verwenden (z. B. wenn Sie sich in einer Multithread-Umgebung befinden).

0
Christian Long

entschuldigung, aber ich frage mich, warum niemand die folgende einfachere Lösung vorgeschlagen hat? :-O

## request
requests.get('www.mypage.com', timeout=20)
0
comiventor

Nur eine andere Lösung (von http://docs.python-requests.org/de/master/user/advanced/#streaming-uploads )

Vor dem Hochladen können Sie die Inhaltsgröße ermitteln:

TOO_LONG = 10*1024*1024  # 10 Mb
big_url = "http://ipv4.download.thinkbroadband.com/1GB.Zip"
r = requests.get(big_url, stream=True)
print (r.headers['content-length'])
# 1073741824  

if int(r.headers['content-length']) < TOO_LONG:
    # upload content:
    content = r.content

Aber Vorsicht, ein Absender kann einen falschen Wert im Antwortfeld "Inhaltslänge" festlegen.

0
Denis Kuzin