117 votes

Pourquoi requests.get() ne revient-il pas ? Quel est le délai d'attente par défaut utilisé par requests.get() ?

Dans mon script, requests.get ne revient jamais :

import requests

print ("requesting..")

# This call never returns!
r = requests.get(
    "http://www.some-site.example",
    proxies = {'http': '222.255.169.74:8080'},
)

print(r.ok)

Quelle pourrait être la ou les raisons possibles ? Un remède ? Quel est le délai d'attente par défaut que get utilise ?

170voto

ron.rothman Points 2970

Quel est le délai d'attente par défaut utilisé par Get ?

Le délai par défaut est de None ce qui signifie qu'il attendra (suspendra) jusqu'à ce que la connexion soit fermée.

Juste spécifier une valeur de délai d'attente comme ceci :

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

43voto

Hieu Points 473

De demande de la documentation :

Vous pouvez demander aux requêtes de cesser d'attendre une réponse après un délai donné. nombre de secondes donné avec le paramètre timeout :

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

Note :

Le délai d'attente n'est pas une limite de temps pour l'ensemble du téléchargement de la réponse, une exception est levée si le serveur n'a pas émis de réponse pendant les secondes (plus précisément, si aucun octet n'a été reçu sur le socket sous-jacent pendant sous-jacente pendant les secondes du délai d'attente).

Il m'arrive souvent que requests.get() prenne un temps très long à retourner même si le timeout est de 1 seconde. Il existe plusieurs façons de surmonter ce problème :

1. Utilisez le TimeoutSauce classe interne

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

Ce code devrait nous amener à définir le délai de lecture comme étant égal au timeout de connexion, qui est la valeur de timeout que vous passez sur votre appel Session.get(). (Notez que je n'ai pas encore testé ce code, donc il peut il peut nécessiter un débogage rapide, je l'ai juste écrit directement dans la fenêtre fenêtre GitHub).

2. Utiliser une fourchette de demandes de kevinburke : https://github.com/kevinburke/requests/tree/connect-timeout

Extrait 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, comme ceci :

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

La valeur du délai d'attente sera appliquée à la fois à la connexion et à la lecture. de lecture. Spécifiez un tuple si vous souhaitez définir les valeurs séparément :

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

NOTE : La modification a depuis été intégrée au projet principal Requests. .

3. Utilisation de evenlet o signal comme déjà mentionné dans la question similaire : Délai d'attente pour les demandes python.get réponse entière

7voto

Tim Richardson Points 307

Je voulais un délai d'attente par défaut facile à ajouter à un tas de code (en supposant que le délai d'attente résout votre problème).

C'est la solution que j'ai récupérée d'un ticket soumis au dépôt pour les demandes.

crédit : https://github.com/kennethreitz/requests/issues/2011#issuecomment-477784399

La solution se trouve dans les deux dernières lignes ici, mais je montre plus de code pour un meilleur contexte. J'aime utiliser une session pour le comportement de relance.

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

alors vous pouvez faire quelque chose comme ça :

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

4voto

Alex Polekha Points 1331

J'ai examiné toutes les réponses et je suis arrivé à la conclusion que le problème existe toujours. Sur certains sites, les requêtes peuvent être suspendues à l'infini et l'utilisation du multiprocesseur semble être une surcharge. Voici mon approche (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)

UPDATE

Si vous recevez un avertissement de dépréciation concernant l'utilisation de conn_timeout et read_timeout, vérifiez vers le bas de la page CE pour savoir comment utiliser la structure de données ClientTimeout. Une façon simple d'appliquer cette structure de données selon la référence liée au code original ci-dessus serait :

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

4voto

林果皞 Points 4216

Dans mon cas, la raison de "requests.get never returns" est que requests.get() tentative de se connecter à l'hôte résolu avec ipv6 ip d'abord . Si quelque chose s'est mal passé pour connecter cette ipv6 et qu'elle est bloquée, alors elle réessaie. ipv4 ip seulement si j'ai explicitement fixé timeout=<N seconds> et a frappé le temps mort.

Ma solution est la suivante singe-Parcheando le python socket à ignorer ipv6 (ou ipv4 si ipv4 ne fonctionne pas), soit cette réponse o cette réponse fonctionnent pour moi.

Vous vous demandez peut-être pourquoi curl fonctionne, car curl connecter ipv4 sans attendre la fin de ipv6. Vous pouvez tracer les syscalls de la socket avec strace -ff -e network -s 10000 -- curl -vLk '<your url>' commande. Pour python, strace -ff -e network -s 10000 -- python3 <your python script> peut être utilisée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X