Requests.request için max_retries ayarlayabilir miyim?


182

Python istek modülü basit ve zarif ama bir şey beni rahatsız ediyor. Aşağıdaki gibi bir mesajla requests.exception.ConnectionError almak mümkündür :

Max retries exceeded with url: ...

Bu, isteklerin verilere birkaç kez erişmeye çalışabileceği anlamına gelir. Ancak dokümanın herhangi bir yerinde bu olasılıktan tek bir söz yok. Kaynak koduna baktığımda varsayılan (muhtemelen 0) değeri değiştirebileceğim bir yer bulamadım.

Peki, bir şekilde istekler için maksimum yeniden deneme sayısını ayarlamak mümkün müdür?


9
Bu konuda 2.x istekleri ile herhangi bir güncelleme var mı? Bir requests.get (url, max_retries = num_max_retries)) uygulamasını isterdim.
paragbaxi

11
@paragbaxi: ve daha da iyisirequests.get(url, max_retries=num_max_retries, dely_between_retries=3))
WoJ

1
@ Örneklerimi aldım ve gerçeğe dönüştürdüm;) github.com/kootenpv/just içinde just.getve just.postiçinde
PascalVKooten

2
İstekler ile yeniden denemeler
Gokul

Yanıtlar:


161

urllib3Yeniden denemeyi yapan temel kütüphane. Farklı bir maksimum yeniden deneme sayısı ayarlamak için alternatif taşıma adaptörleri kullanın :

from requests.adapters import HTTPAdapter

s = requests.Session()
s.mount('http://stackoverflow.com', HTTPAdapter(max_retries=5))

max_retriesBağımsız değişken bir tamsayı veya bir alır Retry()nesne ; ikincisi, ne tür hataların yeniden deneneceği üzerinde ayrıntılı kontrol sağlar (tamsayı değeri Retry()yalnızca bağlantı hatalarını işleyen bir örneğe dönüştürülür ; bağlantı yapıldıktan sonraki hatalar varsayılan olarak bunlar yan etkilere yol açabileceğinden ele alınmaz) .


Eski cevap, isteklerin serbest bırakılmasından önce 1.2.1 :

requestsKütüphane gerçekten bu yapılandırılabilir yapmak, ne de (bkz niyetinde yapar gelmez bu çekme isteği ). Şu anda (istek 1.1), yeniden deneme sayısı 0 olarak ayarlanmıştır. Gerçekten daha yüksek bir değere ayarlamak istiyorsanız, bunu global olarak ayarlamanız gerekir:

import requests

requests.adapters.DEFAULT_RETRIES = 5

Bu sabit belgelenmemiştir; gelecekteki sürümler bunun nasıl ele alınacağını değiştirebileceği için bunu kendi sorumluluğunuzda kullanın.

Güncelleme : ve bu yaptığımız değişikliği; Sürüm 1.2.1'de ayarlamak için seçenek max_retriesparametre üzerinde HTTPAdapter()sınıf artık alternatif ulaşım adaptör kullanmak zorunda böylece eklenmiş, yukarıya bakın. Maymun yama yaklaşımı, HTTPAdapter.__init__()varsayılanları da yamalamazsanız (çok önerilmez) artık çalışmaz.


9
Bu gerekli değilse, her site için bunu belirtmeniz gerekmez. Bunu session.mount('http://', HTTPAdapter(max_retries=10))tüm http bağlantıları için çalıştırabilirsiniz. Https ile aynı şey tüm https bağlantıları için çalışır.
user136036

1
@ user136036: evet, adaptörler en uzun önek eşleşmesi ile aranır; bunun tüm URL'lere uygulanmasını istiyorsanız http://ve https://kullanılacak minimum önekler ise, yanıtın bağlantı verdiği belgelere bakın.
Martijn Pieters

1
HTTPAdapter(max_retries=5)Bunun yalnızca belirli senaryolarda işe yarayacağını unutmayın . Gönderen istekleri doc , 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.herhangi bir durum kodları için kuvvet yeniden deneme için aşağıdaki datashaman cevabı @ görüyoruz.
Steven Xu

@StevenXu: evet, Retry()hangi hata senaryolarının yeniden deneneceğini değiştirmek için yapılandırabilirsiniz .
Martijn Pieters

228

Bu, yalnızca max_retries değerini değiştirmekle kalmayacak, aynı zamanda yeniden denemeden önce tüm http: // adreslerine bir süre uyku toplamayı (toplam 5 defa) yapan bir geri çekilme stratejisini de etkinleştirecektir :

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

Gereğince belgeler içinRetry : backoff_factor ise 0.1 , o zaman uyku () için uyku [0,1, 0.2s, 0.4s, ...] denemeler arasındaki. Ayrıca döndürülen durum kodu 500 , 502 , 503 veya 504 ise yeniden denemeyi zorlar .

RetryDaha ayrıntılı kontrol sağlamak için diğer çeşitli seçenekler :

  • total - İzin verilecek toplam yeniden deneme sayısı.
  • connect - Yeniden denenecek bağlantıyla ilgili hata sayısı.
  • read - Okuma hatalarını kaç kez yeniden dener.
  • redirect - Gerçekleştirilecek kaç yönlendirme.
  • method_whitelist - Yeniden denememiz gereken büyük harfli HTTP yöntemi fiillerinin kümesi.
  • status_forcelist - Tekrar denemeyi zorlamamız gereken bir dizi HTTP durum kodu.
  • backoff_factor - Denemeler arasında uygulanacak bir geri çekilme faktörü.
  • elev_on_redirect - Yeniden yönlendirme sayısının tükenmesi durumunda, 3xx aralığında MaxRetryErrorbir yanıt kodu içeren bir yanıtı yükseltmek veya döndürmek için .
  • raise_on_status - benzer anlam raise_on_redirect : bir istisna yükseltmek veya durum düşerse, bir yanıt dönüp dönmemesi konusundaki status_forcelist aralığı ve yeniden denemeler tükenmiş bulunuyor.

Not : elev_on_status nispeten yenidir ve henüz urllib3 veya isteklerin bir sürümü haline getirmemiştir. Raise_on_status kelime argümanı piton sürümü 3.6'da en fazla standart kütüphaneye yapmış görünüyor.

İsteklerin belirli HTTP durum kodlarında yeniden denenmesini sağlamak için status_forcelist kullanın . Örneğin, status_forcelist = [503] durum kodu yeniden olacak 503 (hizmet yok).

Varsayılan olarak, yeniden deneme yalnızca şu koşullar için tetiklenir:

  • Havuzdan bağlantı alınamadı.
  • TimeoutError
  • HTTPException(yükseltilmiştir http.client Python 3 başka yer httplib ). URL veya protokol düzgün oluşturulmamış gibi düşük düzeyli HTTP istisnaları gibi görünüyor.
  • SocketError
  • ProtocolError

Bunların hepsinin, düzenli bir HTTP yanıtının alınmasını engelleyen istisnalar olduğuna dikkat edin. Herhangi bir düzenli yanıt oluşturulursa, yeniden deneme yapılmaz. Status_forcelist kullanılmadan, 500 durumu ile bir yanıt bile yeniden denenmeyecektir .

Uzak bir API veya web sunucusuyla çalışmak için daha sezgisel bir şekilde davranmasını sağlamak için , tümü nadir olmayan 500 , 502 , 503 ve 504 durumlarında yeniden denemeleri zorlayan yukarıdaki kod snippet'ini kullanırdım . yeterince geri çekilme süresi göz önüne alındığında web ve (muhtemelen) kurtarılabilir.

REDAKTE : İthalat Retrydoğrudan sınıf urllib3 .


1
Mantığınızı uygulamaya çalışıyorum, ancak çalışmanın olup olmadığını bilmiyorum çünkü günlük sadece bir durum gösteriyor bile res durumu 503'tür. Kodu görün: pastebin.com/rty4bKTw
Danilo Oliveira

1
Ekli kod beklendiği gibi çalışıyor. Hile status_forcelist parametresidir. Bu, urllib3 paketine belirli durum kodlarını yeniden denemesini söyler. Kod: pastebin.com/k2bFbH7Z
datashaman

1
urllib3, durum 503'ün bir istisna olduğunu düşünmez (ve etmemelidir) (varsayılan olarak).
datashaman

1
@Connor no, bağdaştırıcı oturuma bağlı.
datashaman

1
urlib3.Retry artık isteklerin bir parçası değil. bu doğrudan ithal etmek zorunda. Önerilen düzenleme
user2390183

59

Dikkatli olun, Martijn Pieters'ın yanıtı 1.2.1+ sürümü için uygun değildir. Kütüphaneyi yamalamadan global olarak ayarlayamazsınız.

Bunun yerine şunları yapabilirsiniz:

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

22
Güzel bir çözüm ama yeniden denemeler arasında gecikme olmadığını unutmayın. Eğer denemeler arasında uyumak istiyorsanız, kendinizinkini yuvarlamanız gerekir.
nofinator

18

Buradaki bazı cevaplarla biraz uğraştıktan sonra, durumum için daha iyi çalışan geri çekilme adlı bir kütüphane buldum . Temel bir örnek:

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

Yine de kütüphanenin yerel işlevselliğini denemenizi tavsiye ederim, ancak herhangi bir sorunla karşılaşırsanız veya daha geniş kontrole ihtiyacınız varsa, geri çekilme bir seçenektir.


1
harika kütüphane, teşekkürler! Bu işlevsellikten başka bir şey için ihtiyacım vardı requests, bu yüzden mükemmel çalışıyor!
Dennis Golomazov

3

Daha yüksek kontrol elde etmenin daha temiz bir yolu, yeniden deneme öğelerini bir işleve paketlemek ve bu işlevi bir dekoratör kullanarak ve istisnaları beyaz listeye almak için erişilebilir hale getirmek olabilir.

Aynı şeyi burada yarattım: http://www.praddy.in/retry-decorator-whitelisted-exceptions/

Bu bağlantıdaki kodu yeniden oluşturma:

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():
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.