Por que request.get () não retorna? Qual é o tempo limite padrão que o requests.get () usa?

92

No meu script, requests.getnunca retorna:

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)

Quais poderiam ser os possíveis motivos? Qualquer remédio? Qual é o tempo limite padrão getusado?

Nawaz
fonte
1
@ user2357112: Isso importa? Eu duvido.
Nawaz
Definitivamente importa. Se você fornecer o URL que está tentando acessar e o proxy que está tentando usar, podemos ver o que acontece quando tentamos enviar solicitações semelhantes.
usuário2357112 oferece suporte a Monica de
1
@ user2357112: Tudo bem. Editou a pergunta.
Nawaz
2
Seu proxy também está incorreto. Você deve especificá-lo assim: proxies={'http': 'http://222.255.169.74:8080'}. Pode ser por isso que ele não está sendo concluído sem um tempo limite.
Ian Stapleton Cordasco

Respostas:

127

Qual é o tempo limite padrão para obter usos?

O tempo limite padrão é None, o que significa que ele aguardará (travará) até que a conexão seja fechada.

O que acontece quando você passa um valor de tempo limite?

r = requests.get(
    'http://www.justdial.com',
    proxies={'http': '222.255.169.74:8080'},
    timeout=5
)
Ron Rothman
fonte
3
Eu acho que você está certo. Nonesignifica infinito (ou "espere até que a conexão seja fechada"). Se eu mesmo passar o tempo limite, ele retorna!
Nawaz
14
O tempo limite de @User funciona tão bem com https quanto com http
jaapz
Isso parece muito difícil de encontrar nos documentos pesquisando no Google ou de outra forma. Alguém sabe onde isso aparece nos documentos?
wordsforthewise
Obrigado, fazer print(requests.request.__doc__)em IPython é mais do que eu estava procurando. Eu estava me perguntando quais outros argumentos opcionais request.get()existiam.
wordsforthewise
39

Da documentação dos pedidos :

Você pode dizer aos Requests para parar de esperar por uma resposta após um determinado número de segundos com o parâmetro de tempo limite:

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

Nota:

o tempo limite não é um limite de tempo para o download da resposta inteira; em vez disso, uma exceção é levantada se o servidor não emitiu uma resposta para segundos de tempo limite (mais precisamente, se nenhum byte foi recebido no soquete subjacente para segundos de tempo limite).

Acontece muito comigo que o request.get () leva muito tempo para retornar, mesmo que timeoutseja 1 segundo. Existem algumas maneiras de superar esse problema:

1. Use a TimeoutSauceclasse interna

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

Esse código deve fazer com que definamos o tempo limite de leitura como igual ao tempo limite de conexão, que é o valor de tempo limite que você passa em sua chamada Session.get (). (Observe que, na verdade, não testei este código, portanto, pode ser necessário uma depuração rápida, apenas o escrevi diretamente na janela do GitHub.)

2. Use uma bifurcação de solicitações de kevinburke: https://github.com/kevinburke/requests/tree/connect-timeout

De sua documentação: https://github.com/kevinburke/requests/blob/connect-timeout/docs/user/advanced.rst

Se você especificar um único valor para o tempo limite, como este:

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

O valor de tempo limite será aplicado aos tempos limite de conexão e leitura. Especifique uma tupla se quiser definir os valores separadamente:

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

OBSERVAÇÃO: A alteração foi mesclada ao projeto principal de Solicitações .

3. Usando evenletou signalconforme já mencionado na pergunta semelhante: Tempo limite para solicitações python.get resposta inteira

Hieu
fonte
7
Você nunca respondeu qual é o padrão
Usuário
Citação: você pode dizer aos Requests para parar de esperar por uma resposta após um determinado número de segundos com o parâmetro de tempo limite. Quase todo código de produção deve usar esse parâmetro em quase todas as solicitações. Não fazer isso pode fazer com que seu programa trave indefinidamente: Observe que o tempo limite não é um limite de tempo para o download de toda a resposta; em vez disso, uma exceção é levantada se o servidor não emitiu uma resposta para segundos de tempo limite (mais precisamente, se nenhum byte foi recebido no soquete subjacente para segundos de tempo limite). Se nenhum tempo limite for especificado explicitamente, as solicitações não expiram.
DDay
O código tem um erro de digitação: import requests <nova linha aqui> from requests.adapters import TimeoutSauce
Sinan Çetinkaya
4

Eu queria um tempo limite padrão facilmente adicionado a um monte de código (presumindo que o tempo limite resolva seu problema)

Esta é a solução que peguei em um tíquete enviado ao repositório de solicitações.

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

A solução está no último par de linhas aqui, mas mostro mais código para um melhor contexto. Gosto de usar uma sessão para repetir o comportamento.

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

então você pode fazer algo assim:

requests_session = requests_retry_session()
r = requests_session.get(url=url,...
Tim Richardson
fonte
4

Reviu todas as respostas e chegou à conclusão que o problema ainda existe. Em alguns sites, as solicitações podem travar infinitamente e usar o multiprocessamento parece ser um exagero. Esta é minha abordagem (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)

ATUALIZAR

Se você receber um aviso de depreciação sobre o uso conn_timeout e read_timeout, verificação perto da parte inferior ESTE referência para como usar a estrutura de dados ClientTimeout. Uma maneira simples de aplicar essa estrutura de dados de acordo com a referência vinculada ao código original acima seria:

async def get_http(url):
    timeout = aiohttp.ClientTimeout(total=60)
    async with aiohttp.ClientSession(timeout=timeout) as client:
        try:
            etc.
Alex Polekha
fonte
2
@Nawaz Python 3.5+. Obrigado pela pergunta, atualizou a resposta com a versão Python. É um código Python legal. Por favor, dê uma olhada na documentação do aiohttp aiohttp.readthedocs.io/en/stable/index.html
Alex Polekha
Isso resolveu meus problemas quando outros métodos não. Py 3.7. Devido a depricações, tive que usar ... timeout = aiohttp.ClientTimeout (total = 60) async com aiohttp.ClientSession (timeout = timeout) como cliente:
Thom Ives
2

Corrigir a função "enviar" documentada irá consertar isso para todos os pedidos - mesmo em muitas bibliotecas dependentes e sdk's. Ao corrigir as libs, certifique-se de corrigir as funções suportadas / documentadas, não o TimeoutSauce - caso contrário, você pode acabar perdendo silenciosamente o efeito do seu patch.

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

Os efeitos de não haver nenhum tempo limite são bastante graves, e o uso de um tempo limite padrão quase nunca pode interromper nada - porque o próprio TCP também tem tempos limite padrão.

Erik Aronesty
fonte
0

No meu caso, o motivo de "requests.get never Return" é porque a requests.get()tentativa de conectar ao host resolveu primeiro com ipv6 . Se algo deu errado ao conectar esse ipv6 e travar, ele tentará novamente o ipv4 apenas se eu definir explicitamente timeout=<N seconds>e atingir o tempo limite.

Minha solução é fazer um monkey-patching no python socketpara ignorar o ipv6 (ou ipv4 se o ipv4 não funcionar), ou esta resposta ou esta resposta funcionam para mim.

Você deve estar se perguntando por que o curlcomando funciona, porque curlconecte o ipv4 sem esperar que o ipv6 seja concluído. Você pode rastrear as syscalls de soquete com o strace -ff -e network -s 10000 -- curl -vLk '<your url>'comando. Para python, o strace -ff -e network -s 10000 -- python3 <your python script>comando pode ser usado.

Fruta
fonte