Je rassemble des statistiques sur une liste de sites Web et j'utilise les demandes pour plus de simplicité. Voici mon 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())) )
Maintenant, je veux que requests.get
expire au bout de 10 secondes pour que la boucle ne reste pas bloquée.
Cette question a eu un intérêt avant aussi mais aucune des réponses n'est nette. Je vais mettre une prime sur cela pour obtenir une bonne réponse.
J'entends dire que peut-être que ne pas utiliser les demandes est une bonne idée, mais ensuite, comment puis-je obtenir l'offre de demandes de Nice Things (ceux du tuple)
Qu'en est-il d'utiliser Eventlet? Si vous souhaitez que la requête expire au bout de 10 secondes, même si des données sont en cours de réception, cet extrait fonctionnera pour vous:
import requests
import eventlet
eventlet.monkey_patch()
with eventlet.Timeout(10):
requests.get("http://ipv4.download.thinkbroadband.com/1GB.Zip", verify=False)
Définissez le paramètre timeout :
r = requests.get(w, verify=False, timeout=10)
Tant que vous ne définissez pas stream=True
sur cette demande, l'appel de requests.get()
expire si la connexion prend plus de dix secondes ou si le serveur n'envoie pas de données pendant plus de dix secondes.
UPDATE: http://docs.python-requests.org/fr/master/user/advanced/#timeouts
Dans la nouvelle version de requests
:
Si vous spécifiez une seule valeur pour le délai d'attente, procédez comme suit:
r = requests.get('https://github.com', timeout=5)
La valeur de délai d'attente sera appliquée à la fois aux délais d'expiration connect
et read
. Spécifiez un tuple si vous souhaitez définir les valeurs séparément:
r = requests.get('https://github.com', timeout=(3.05, 27))
Si le serveur distant est très lent, vous pouvez indiquer aux demandes d'attendre une réponse pour toujours, en transmettant Aucune comme valeur de délai d'attente, puis en récupérant une tasse de café.
r = requests.get('https://github.com', timeout=None)
Mon ancienne réponse (probablement obsolète) (qui a été postée il y a longtemps):
Il existe d'autres moyens de résoudre ce problème:
1. Utilisez la classe TimeoutSauce
internal
De: 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
Ce code doit nous amener à définir le délai de lecture comme étant égal à connect timeout, qui correspond à la valeur de timeout que vous transmettez à votre Session.get () appelle. (Notez que je n'ai pas vraiment testé ce code, donc Il peut nécessiter un débogage rapide, je l'ai simplement écrit directement dans la fenêtre GitHub.)
2. Utilisez une fourchette de requêtes provenant de kevinburke:https://github.com/kevinburke/requests/tree/connect-timeout
À partir de sa documentation: https://github.com/kevinburke/requests/blob/connect-timeout/docs/user/advanced.rst
Si vous spécifiez une seule valeur pour le délai d'attente, procédez comme suit:
r = requests.get('https://github.com', timeout=5)
La valeur de délai d'attente sera appliquée à la fois à la connexion et à la lecture délais d'attente. Spécifiez un tuple si vous souhaitez définir les valeurs séparément:
r = requests.get('https://github.com', timeout=(3.05, 27))
kevinburke a demandé il doit être intégré au projet principal, mais il n’a pas encore été accepté.
Pour créer un délai d'attente, vous pouvez utiliser signaux .
La meilleure façon de résoudre ce cas est probablement de
try-except-finally
.Voici un exemple de code:
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)
Il y a quelques mises en garde à cela:
Mais tout se trouve dans la bibliothèque standard Python! À l'exception de l'importation de la fonction de veille, il ne s'agit que d'une importation. Si vous comptez utiliser des timeouts à plusieurs endroits, vous pouvez facilement insérer TimeoutException, _timeout et le singaling dans une fonction et simplement appeler cela. Ou vous pouvez faire un décorateur et le mettre sur des fonctions, voir la réponse liée ci-dessous.
Vous pouvez également configurer cela en tant que "gestionnaire de contexte" afin que vous puissiez l'utiliser avec l'instruction with
:
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')
Un inconvénient possible avec cette approche de gestionnaire de contexte est que vous ne pouvez pas savoir si le code a expiré ou non.
Sources et lectures recommandées:
À partir de janvier 2019, vous pouvez utiliser l'argument timeout
de requests
, à savoir:
requests.get(url, timeout=10)
Remarque:
timeout
n'est pas une limite de temps pour le téléchargement complet de la réponse; plutôt, une exception est déclenchée si le serveur n'a pas émis de réponse pour timeout secondes (plus précisément, si aucun octet n'a été reçu sur le socket sous-jacent pendant les secondes écoulées). Si aucun délai n'est spécifié explicitement, les demandes ne pas expirer.
C'est peut-être excessif, mais la file d'attente des tâches distribuées du céleri prend en charge les délais d'expiration.
Vous pouvez notamment définir une limite temporelle souple qui ne déclenche qu'une exception dans votre processus (afin que vous puissiez la nettoyer) et/ou une limite temporelle dure qui termine la tâche lorsque la limite temporelle est dépassée.
Sous les couvertures, cela utilise la même approche de signaux que celle mentionnée dans votre message "avant", mais d'une manière plus utilisable et plus facile à gérer. Et si la liste des sites Web que vous surveillez est longue, vous pourrez tirer parti de sa fonctionnalité principale - toutes sortes de moyens de gérer l'exécution d'un grand nombre de tâches.
Je crois que vous pouvez utiliser multiprocessing
et ne pas dépendre d'un package tiers:
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)
Le délai d'attente passé à kwargs
est le délai d'attente pour obtenir la réponse toute du serveur, l'argument timeout
est le délai d'attente pour obtenir la réponse complète.
Essayez cette requête avec timeout et traitement des erreurs:
import requests
try:
url = "http://google.com"
r = requests.get(url, timeout=10)
except requests.exceptions.Timeout as e:
print e
ce code fonctionne pour socketError 11004 et 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_()
Bien que la question concerne les demandes, je trouve cela très facile à faire avec pycurlCURLOPT_TIMEOUT ou CURLOPT_TIMEOUT_MS.
Aucun filetage ni signalisation requis:
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
Si cela se produit, créez un watchdog thread qui perturbe l’état interne des requêtes après 10 secondes, par exemple:
Notez que, selon les bibliothèques système, il peut être impossible de définir une échéance pour la résolution DNS.
Si vous utilisez l'option stream=True
, vous pouvez le faire:
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')
La solution n'a pas besoin de signaux ni de multitraitement.
Définissez stream=True
et utilisez r.iter_content(1024)
. Oui, eventlet.Timeout
ne fonctionne tout simplement pas pour moi.
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()]
La discussion est ici https://redd.it/80kp1h
timeout = (délai de connexion, délai de lecture des données) ou donner un seul argument (timeout = 1)
import requests
try:
req = requests.request('GET', 'https://www.google.com',timeout=(1,1))
print(req)
except requests.ReadTimeout:
print("READ TIME OUT")
Eh bien, j'ai essayé de nombreuses solutions sur cette page et je faisais face à des instabilités, des blocages aléatoires, des performances de connexion médiocres.
J'utilise maintenant Curl et je suis vraiment content de sa fonctionnalité "max time" et de ses performances globales, même avec une mise en œuvre aussi médiocre:
content=commands.getoutput('curl -m6 -Ss "http://mywebsite.xyz"')
Ici, j'ai défini un paramètre de temps maximum de 6 secondes, englobant à la fois le temps de connexion et le temps de transfert.
Je suis sûr que Curl a une liaison Nice en python, si vous préférez vous en tenir à la syntaxe Pythonic :)
Il existe un paquet appelé timeout-decorator que vous pouvez utiliser pour mettre fin à toute fonction python.
@timeout_decorator.timeout(5)
def mytest():
print("Start")
for i in range(1,10):
time.sleep(1)
print("{} seconds have passed".format(i))
Il utilise l'approche des signaux que certaines réponses suggèrent ici. Vous pouvez également lui dire d’utiliser le multitraitement au lieu de signaux (par exemple, si vous êtes dans un environnement multi-thread).
excusez-moi, mais je me demande pourquoi personne n'a suggéré la solution plus simple suivante. : -o
## request
requests.get('www.mypage.com', timeout=20)
Juste une autre solution (obtenue de http://docs.python-requests.org/en/master/user/advanced/#streaming-uploads )
Avant de télécharger, vous pouvez connaître la taille du contenu:
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
Attention, un expéditeur peut définir une valeur incorrecte dans le champ de réponse 'longueur du contenu'.