Neden request.get () dönmüyor? Request.get () 'ın kullandığı varsayılan zaman aşımı nedir?


95

Benim senaryomda requests.getasla şunu döndürmez:

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)

Olası neden (ler) ne olabilir? Herhangi bir çare var mı? Kullanılan varsayılan zaman aşımı nedir get?


1
@ user2357112: Fark eder mi? Şüpheliyim.
Nawaz

Kesinlikle önemli. Erişmeye çalıştığınız URL'yi ve kullanmaya çalıştığınız proxy'yi sağlarsanız, benzer istekler göndermeye çalıştığımızda ne olacağını görebiliriz.
user2357112, Monica

1
@ user2357112: Pekala. Soruyu düzenledi.
Nawaz

2
Vekiliniz de yanlış. Sen o kadar seviyorum belirtmek gerekir: proxies={'http': 'http://222.255.169.74:8080'}. Zaman aşımı olmadan tamamlanmamasının nedeni bu olabilir.
Ian Stapleton Cordasco

Yanıtlar:


134

Kullanan varsayılan zaman aşımı nedir?

Varsayılan zaman aşımı, Nonebağlantı kapanana kadar bekleyeceği (askıda kalacağı) anlamına gelir.

Bir zaman aşımı değerini geçtiğinizde ne olur?

r = requests.get(
    'http://www.justdial.com',
    proxies={'http': '222.255.169.74:8080'},
    timeout=5
)

3
Bence haklısın. Nonesonsuz anlamına gelir (veya "bağlantı kapanana kadar bekleyin"). Zaman aşımını kendim geçersem geri döner!
Nawaz

14
@Kullanıcı zaman aşımı, https'de olduğu kadar http ile de iyi çalışıyor
jaapz

Bu, Google'da arama yaparak veya başka bir şekilde belgelerde bulmak gerçekten zor görünüyor. Bunun belgelerde nerede göründüğünü bilen var mı?
wordsforthewise


Teşekkürler, print(requests.request.__doc__)IPython'da yapmak aradığımdan daha fazlası. Başka hangi isteğe bağlı argümanların olduğunu merak ediyordum request.get().
wordsforthewise

40

Gönderen istekleri belgeler :

Zaman aşımı parametresiyle İsteklere, belirli bir saniye sayısının ardından yanıt beklemeyi bırakmasını söyleyebilirsiniz:

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

Not:

zaman aşımı, tüm yanıt yüklemesi için bir zaman sınırı değildir; bunun yerine, sunucu zaman aşımı saniyeleri için bir yanıt yayınlamazsa bir istisna oluşur (daha doğrusu, zaman aşımı saniyeleri için temeldeki sokette bayt alınmadıysa).

Bana çok şey oluyor ki request.get () timeout1 saniye bile dönmesi çok uzun sürüyor . Bu sorunun üstesinden gelmenin birkaç yolu var:

1. TimeoutSauceDahili sınıfı kullanın

Gönderen: 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

Bu kod, okuma zaman aşımını, Session.get () çağrınıza ilettiğiniz zaman aşımı değeri olan bağlanma zaman aşımına eşit olarak ayarlamamıza neden olmalıdır. (Bu kodu gerçekten test etmediğime dikkat edin, bu yüzden bazı hızlı hata ayıklamaya ihtiyaç duyabilir, doğrudan GitHub penceresine yazdım.)

2. Kevinburke'den gelen bir istek çatalını kullanın: https://github.com/kevinburke/requests/tree/connect-timeout

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

Zaman aşımı için aşağıdaki gibi tek bir değer belirtirseniz:

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

Zaman aşımı değeri hem bağlanma hem de okuma zaman aşımlarına uygulanacaktır. Değerleri ayrı ayrı ayarlamak isterseniz bir demet belirleyin:

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

NOT: Değişiklik o zamandan beri ana İstekler projesiyle birleştirildi .

3. evenletveya signalbenzer soruda daha önce belirtildiği gibi kullanın: Python istekleri için zaman aşımı. Tüm yanıtı hedefleyin


8
Varsayılanın ne olduğunu hiç yanıtlamadınız
Kullanıcı

Alıntı: İsteklere, zaman aşımı parametresi ile belirli bir saniye sonra yanıt beklemeyi bırakmalarını söyleyebilirsiniz. Neredeyse tüm üretim kodu bu parametreyi neredeyse tüm taleplerde kullanmalıdır. Bunun yapılmaması, programınızın süresiz olarak askıda kalmasına neden olabilir: Zaman aşımının tüm yanıt yüklemesi için bir zaman sınırı olmadığını unutmayın; bunun yerine, sunucu zaman aşımı saniyeleri için bir yanıt yayınlamazsa bir istisna oluşur (daha doğrusu, zaman aşımı saniyeleri için temeldeki sokette bayt alınmadıysa). Açıkça hiçbir zaman aşımı belirtilmezse, istekler zaman aşımına uğramaz.
DDay

Kodda bir yazım hatası var: isteklerden <yeni satır burada> içe aktarma istekleri. Adapters içe aktarma Zaman AşımıSauce
Sinan Çetinkaya

4

Varsayılan bir zaman aşımının bir grup koda kolayca eklenmesini istedim (zaman aşımının sorununuzu çözeceğini varsayarak)

Bu, Talepler için arşive gönderilen bir biletten aldığım çözüm.

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

Çözüm, buradaki son birkaç satırdır, ancak daha iyi bağlam için daha fazla kod gösteriyorum. Yeniden deneme davranışı için bir oturum kullanmayı seviyorum.

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

o zaman bunun gibi bir şey yapabilirsiniz:

requests_session = requests_retry_session()
r = requests_session.get(url=url,...

4

Tüm cevapları inceledik ve sorunun hala var olduğu sonucuna vardık. Bazı sitelerde istekler sonsuza kadar askıda kalabilir ve çoklu işlemenin kullanılması aşırıya kaçmış gibi görünebilir. İşte benim yaklaşımım (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)

GÜNCELLEME

Conn_timeout ve read_timeout kullanımıyla ilgili bir kullanımdan kaldırma uyarısı alırsanız , ClientTimeout veri yapısının nasıl kullanılacağını öğrenmek için BU referansın altına yakın bir yere bakın. Yukarıdaki orijinal koda bağlı referans başına bu veri yapısını uygulamanın basit bir yolu şu olacaktır:

async def get_http(url):
    timeout = aiohttp.ClientTimeout(total=60)
    async with aiohttp.ClientSession(timeout=timeout) as client:
        try:
            etc.

2
@Nawaz Python 3.5+. Soru için teşekkürler, cevabı Python sürümü ile güncelledi. Yasal Python kodu. Aiohttp belgeler bakmak Lütfen aiohttp.readthedocs.io/en/stable/index.html
Alex Polekha

Bu, diğer yöntemler çözülmediğinde sorunlarımı çözdü. Py 3.7. Deprications nedeniyle, istemci olarak aiohttp.ClientSession (zaman aşımı = zaman aşımı) ile ... timeout = aiohttp.ClientTimeout (total = 60) async kullanmak zorunda kaldı:
Thom Ives

2

Belgelenen "gönder" işlevine yama uygulamak, birçok bağımlı kitaplıkta ve sdk'de bile bunu tüm istekler için düzeltecektir. Kitaplıklara yama uygularken, TimeoutSauce yerine desteklenen / belgelenen işlevleri yamaladığınızdan emin olun - aksi takdirde yamanızın etkisini sessizce kaybedebilirsiniz.

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

Herhangi bir zaman aşımına sahip olmamanın etkileri oldukça şiddetlidir ve varsayılan zaman aşımının kullanılması neredeyse hiçbir şeyi kesintiye uğratmaz - çünkü TCP de varsayılan zaman aşımlarına sahiptir.


1

Benim durumumda, "request.get asla dönmüyor" nedeni, önce ipv6 ip ile çözülen ana bilgisayara bağlanmarequests.get() girişimidir . Bu ipv6 ip'i bağlamak ve takılmak için bir şeyler ters giderse, ipv4 ip'i yalnızca açık bir şekilde ayarlayıp zaman aşımına ulaşırsam yeniden dener .timeout=<N seconds>

Benim çözüm maymun yama piton socketiçin ipv6 görmezden ya (ya ipv4 ipv4 çalışmıyor ise) Bu cevap veya bu cevabı benim için işlerdir.

curlKomutun neden işe yaradığını merak ediyor olabilirsiniz , çünkü curlipv6'nın tamamlanmasını beklemeden ipv4'ü bağlayın. Soket sistem çağrılarını strace -ff -e network -s 10000 -- curl -vLk '<your url>'komut ile takip edebilirsiniz . Python için strace -ff -e network -s 10000 -- python3 <your python script>komut kullanılabilir.

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.