Le module de requêtes Python est simple et élégant mais une chose me dérange. Il est possible d'obtenir une request.exception.ConnectionError avec un message comme:
Max retries exceeded with url: ...
Cela implique que les demandes peuvent tenter d'accéder aux données plusieurs fois. Mais il n'y a aucune mention de cette possibilité nulle part dans la documentation. En regardant le code source, je n'ai trouvé aucun endroit où je pourrais modifier la valeur par défaut (probablement 0).
Est-il donc possible de définir en quelque sorte le nombre maximum de tentatives pour les demandes?
python
python-requests
Kirill Zaitsev
la source
la source
requests.get(url, max_retries=num_max_retries, dely_between_retries=3))
just.get
etjust.post
dans github.com/kootenpv/justRéponses:
C'est la
urllib3
bibliothèque sous-jacente qui effectue la nouvelle tentative. Pour définir un nombre maximal de tentatives différent, utilisez d' autres adaptateurs de transport :from requests.adapters import HTTPAdapter s = requests.Session() s.mount('http://stackoverflow.com', HTTPAdapter(max_retries=5))
L'
max_retries
argument prend un entier ou unRetry()
objet ; ce dernier vous donne un contrôle précis sur les types d'échecs réessayés (une valeur entière est transformée en uneRetry()
instance qui ne gère que les échecs de connexion; les erreurs après qu'une connexion est établie ne sont par défaut pas gérées car elles pourraient entraîner des effets secondaires) .Ancienne réponse, antérieure à la publication des demandes 1.2.1 :
La
requests
bibliothèque ne rend pas vraiment cela configurable, ni n'en a l'intention (voir cette pull request ). Actuellement (demandes 1.1), le nombre de tentatives est défini sur 0. Si vous voulez vraiment le définir sur une valeur plus élevée, vous devrez le définir globalement:import requests requests.adapters.DEFAULT_RETRIES = 5
Cette constante n'est pas documentée; utilisez-le à vos risques et périls car les futures versions pourraient changer la façon dont cela est géré.
Mise à jour : et cela a changé; dans la version 1.2.1, l'option pour définir le
max_retries
paramètre sur laHTTPAdapter()
classe a été ajoutée, de sorte que vous devez maintenant utiliser des adaptateurs de transport alternatifs, voir ci-dessus. L'approche monkey-patch ne fonctionne plus, à moins que vous ne corrigiez également lesHTTPAdapter.__init__()
valeurs par défaut (ce n'est vraiment pas recommandé).la source
session.mount('http://', HTTPAdapter(max_retries=10))
cela fonctionnera pour toutes les connexions http. La même chose avec https fonctionnera alors pour toutes les connexions https.http://
et que cehttps://
sont les préfixes minimaux à utiliser, consultez la documentation vers laquelle la réponse renvoie.HTTPAdapter(max_retries=5)
cela ne fonctionnera que pour certains scénarios. À partir de la documentation des demandes ,Note, this applies only to failed DNS lookups, socket connections and connection timeouts, never to requests where data has made it to the server. By default, Requests does not retry failed connections.
pour forcer une nouvelle tentative pour tout code d'état, consultez la réponse de @ datashaman ci-dessous.Retry()
pour modifier les scénarios d'échec réessayés.Cela changera non seulement les max_retries, mais activera également une stratégie d'interruption qui fait que les requêtes à toutes les adresses http: // dorment pendant un certain temps avant de réessayer (jusqu'à un total de 5 fois):
import requests from urllib3.util.retry import Retry from requests.adapters import HTTPAdapter s = requests.Session() retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[ 500, 502, 503, 504 ]) s.mount('http://', HTTPAdapter(max_retries=retries)) s.get('http://httpstat.us/500')
Selon la documentation pour
Retry
: si le backoff_factor est 0.1 , alors sleep () dormira pendant [0.1s, 0.2s, 0.4s, ...] entre les tentatives. Cela forcera également une nouvelle tentative si le code d'état renvoyé est 500 , 502 , 503 ou 504 .Diverses autres options pour
Retry
permettre un contrôle plus granulaire:MaxRetryError
, ou pour renvoyer une réponse avec un code de réponse dans la plage 3xx .NB : rise_on_status est relativement nouveau, et n'en a pas encore fait une version d'urllib3 ou de requêtes.L'argument de mot-clé rise_on_status semble avoir été intégré dans la bibliothèque standard tout au plus dans la version 3.6 de python.Pour faire des requêtes réessayer sur des codes d'état HTTP spécifiques, utilisez status_forcelist . Par exemple, status_forcelist = [503] réessayera sur le code d'état 503 (service non disponible).
Par défaut, la nouvelle tentative se déclenche uniquement pour ces conditions:
TimeoutError
HTTPException
soulevé (à partir de http.client dans Python 3 sinon httplib ). Cela semble être des exceptions HTTP de bas niveau, comme une URL ou un protocole mal formé.SocketError
ProtocolError
Notez que ce sont toutes des exceptions qui empêchent la réception d'une réponse HTTP régulière. Si une réponse régulière est générée, aucune nouvelle tentative n'est effectuée. Sans utiliser le status_forcelist , même une réponse avec le statut 500 ne sera pas retentée.
Pour qu'il se comporte d'une manière plus intuitive pour travailler avec une API ou un serveur Web distant, j'utiliserais l'extrait de code ci-dessus, qui force les tentatives sur les statuts 500 , 502 , 503 et 504 , qui ne sont pas rares sur le web et (éventuellement) récupérable étant donné une période d'attente suffisamment longue.
EDITED : Importez la
Retry
classe directement depuis urllib3 .la source
Attention, la réponse de Martijn Pieters n'est pas adaptée à la version 1.2.1+. Vous ne pouvez pas le définir globalement sans patcher la bibliothèque.
Vous pouvez le faire à la place:
import requests from requests.adapters import HTTPAdapter s = requests.Session() s.mount('http://www.github.com', HTTPAdapter(max_retries=5)) s.mount('https://www.github.com', HTTPAdapter(max_retries=5))
la source
Après avoir lutté un peu avec certaines des réponses ici, j'ai trouvé une bibliothèque appelée backoff qui fonctionnait mieux pour ma situation. Un exemple basique:
import backoff @backoff.on_exception( backoff.expo, requests.exceptions.RequestException, max_tries=5, giveup=lambda e: e.response is not None and e.response.status_code < 500 ) def publish(self, data): r = requests.post(url, timeout=10, json=data) r.raise_for_status()
Je recommanderais toujours de donner une chance à la fonctionnalité native de la bibliothèque, mais si vous rencontrez des problèmes ou avez besoin d'un contrôle plus large, la réduction est une option.
la source
requests
, donc cela fonctionne parfaitement!Un moyen plus propre d'obtenir un contrôle plus élevé pourrait être de regrouper les éléments de nouvelle tentative dans une fonction et de rendre cette fonction récupérable à l'aide d'un décorateur et de mettre les exceptions en liste blanche.
J'ai créé la même chose ici: http://www.praddy.in/retry-decorator-whitelisted-exceptions/
Reproduire le code dans ce lien:
def retry(exceptions, delay=0, times=2): """ A decorator for retrying a function call with a specified delay in case of a set of exceptions Parameter List ------------- :param exceptions: A tuple of all exceptions that need to be caught for retry e.g. retry(exception_list = (Timeout, Readtimeout)) :param delay: Amount of delay (seconds) needed between successive retries. :param times: no of times the function should be retried """ def outer_wrapper(function): @functools.wraps(function) def inner_wrapper(*args, **kwargs): final_excep = None for counter in xrange(times): if counter > 0: time.sleep(delay) final_excep = None try: value = function(*args, **kwargs) return value except (exceptions) as e: final_excep = e pass #or log it if final_excep is not None: raise final_excep return inner_wrapper return outer_wrapper @retry(exceptions=(TimeoutError, ConnectTimeoutError), delay=0, times=3) def call_api():
la source