requests.get ()이 반환되지 않는 이유는 무엇입니까? requests.get ()이 사용하는 기본 시간 제한은 무엇입니까?


93

내 스크립트에서 requests.get절대 반환하지 않습니다.

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)

가능한 이유는 무엇입니까? 치료법이 있습니까? get사용 하는 기본 제한 시간은 무엇입니까 ?


1
@ user2357112 : 중요한가요? 의심 스럽다.
Nawaz

확실히 중요합니다. 액세스하려는 URL과 사용하려는 프록시를 제공하면 유사한 요청을 보내려고 할 때 어떤 일이 발생하는지 확인할 수 있습니다.
user2357112 모니카 지원

1
@ user2357112 : 좋습니다. 질문을 수정했습니다.
Nawaz

2
프록시도 잘못되었습니다. 다음과 같이 지정해야합니다 proxies={'http': 'http://222.255.169.74:8080'}.. 이것이 시간 초과없이 완료되지 않는 이유 일 수 있습니다.
Ian Stapleton Cordasco 2013-07-23

답변:


129

사용되는 기본 시간 제한은 무엇입니까?

기본 시간 제한은 None연결이 닫힐 때까지 대기 (중단)됨을 의미합니다.

시간 초과 값을 전달하면 어떻게됩니까?

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

3
내 생각 엔 당신이 맞다. None무한을 의미합니다 (또는 "연결이 닫힐 때까지 대기"). 내가 직접 시간 초과를 전달하면 반환됩니다!
Nawaz 2013

14
이 HTTP와 마찬가지로 @User 시간 제한은 https로 잘으로 작동
jaapz

이것은 인터넷 검색이나 다른 방법으로 문서에서 찾기가 정말 어려워 보입니다. 문서에서 이것이 어디에 나타나는지 아는 사람이 있습니까?
wordsforthewise


감사합니다. print(requests.request.__doc__)IPython 에서하는 것은 제가 찾고 있던 것 중 더 많은 것입니다. 나는 request.get()거기에 다른 선택적 인수가 무엇인지 궁금했습니다 .
wordsforthewise

40

에서 요청 문서 :

timeout 매개 변수를 사용하여 지정된 시간 (초) 후에 응답 대기를 중지하도록 Requests에 지시 할 수 있습니다.

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

노트 :

시간 제한은 전체 응답 다운로드에 대한 시간 제한이 아닙니다. 오히려 서버가 timeout 초 동안 응답을 발행하지 않은 경우 예외가 발생합니다 (더 정확하게는 timeout 초 동안 기본 소켓에 수신 된 바이트가없는 경우).

requests.get () timeout이 1 초라도 반환하는 데 시간이 너무 오래 걸리는 일이 많이 발생합니다 . 이 문제를 극복하는 몇 가지 방법이 있습니다.

1. TimeoutSauce내부 클래스 사용

출처 : 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

이 코드는 우리가 Session.get () 호출에 전달하는 타임 아웃 값인 연결 타임 아웃과 동일하게 읽기 타임 아웃을 설정하게합니다. (실제로이 코드를 테스트하지 않았으므로 빠른 디버깅이 필요할 수 있습니다. 방금 GitHub 창에 직접 작성했습니다.)

2. kevinburke의 요청 포크 사용 : https://github.com/kevinburke/requests/tree/connect-timeout

설명서에서 : https://github.com/kevinburke/requests/blob/connect-timeout/docs/user/advanced.rst

다음과 같이 제한 시간에 단일 값을 지정하는 경우 :

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

제한 시간 값은 연결 및 읽기 제한 시간 모두에 적용됩니다. 값을 개별적으로 설정하려면 튜플을 지정하십시오.

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

참고 : 이후 변경 사항이 기본 요청 프로젝트에 병합되었습니다 .

3. 유사한 질문에서 이미 언급했듯이 evenlet또는 사용 signal: python requests.get 전체 응답에 대한 시간 초과


7
기본값이 무엇인지 대답하지 않았습니다.
User

인용구 : timeout 매개 변수를 사용하여 지정된 시간 (초)이 지나면 응답 대기를 중지하도록 Requests에 지시 할 수 있습니다. 거의 모든 프로덕션 코드는 거의 모든 요청에서이 매개 변수를 사용해야합니다. 이렇게하지 않으면 프로그램이 무기한 중단 될 수 있습니다. 참고 시간 제한은 전체 응답 다운로드에 대한 시간 제한이 아닙니다. 오히려 서버가 timeout 초 동안 응답을 발행하지 않은 경우 (더 정확하게는 timeout 초 동안 기본 소켓에 수신 된 바이트가없는 경우) 예외가 발생합니다. 시간 초과를 명시 적으로 지정하지 않으면 요청이 시간 초과되지 않습니다.
DDay

코드 오타가 있습니다 requests.adapters이 TimeoutSauce 가져올에서 가져 오기 요청은 <여기에 새로운 라인을>
신안 Çetinkaya

4

나는 기본 시간 초과를 코드 묶음에 쉽게 추가하고 싶었습니다 (시간 초과가 문제를 해결한다고 가정)

이것이 요청 저장소에 제출 된 티켓에서 선택한 솔루션입니다.

크레딧 : https://github.com/kennethreitz/requests/issues/2011#issuecomment-477784399

해결책은 여기 마지막 두 줄이지 만 더 나은 컨텍스트를 위해 더 많은 코드를 보여줍니다. 재시도 동작을 위해 세션을 사용하고 싶습니다.

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

그러면 다음과 같이 할 수 있습니다.

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

4

모든 답변을 검토하고 문제가 여전히 존재한다는 결론에 도달했습니다. 일부 사이트에서는 요청이 무한히 멈출 수 있으며 다중 처리를 사용하는 것은 과도한 것 같습니다. 내 접근 방식 (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)

최신 정보

conn_timeout 및 read_timeout 사용에 대한 사용 중단 경고를 받으면 THIS 참조 하단 에서 ClientTimeout 데이터 구조를 사용하는 방법을 확인하십시오 . 위의 원본 코드에 연결된 참조별로이 데이터 구조를 적용하는 간단한 방법은 다음과 같습니다.

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 이상. 질문에 감사 드리며 Python 버전으로 답변을 업데이트했습니다. 합법적 인 파이썬 코드입니다. aiohttp 문서를 참조하십시오 aiohttp.readthedocs.io/en/stable/index.html
Alex Polekha 2017

이것은 다른 방법으로 해결할 수 없을 때 내 문제를 해결했습니다. Py 3.7. 헌신으로 인해 ... timeout = aiohttp.ClientTimeout (total = 60) aiohttp.ClientSession (timeout = timeout) 비동기를 클라이언트로 사용해야했습니다.
Thom Ives

2

문서화 된 "send"함수를 패치하면 많은 종속 라이브러리와 SDK에서도 모든 요청에 ​​대해이 문제가 해결됩니다. libs를 패치 할 때 TimeoutSauce가 아닌 지원 / 문서화 된 기능을 패치해야합니다. 그렇지 않으면 패치의 효과를 잃을 수 있습니다.

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

시간 초과가없는 경우의 영향은 매우 심각하며 기본 시간 초과를 사용하면 TCP 자체에도 기본 시간 초과가 있기 때문에 거의 아무것도 중단 할 수 없습니다.


0

제 경우에는 "requests.get never returns"의 이유 는 호스트requests.get()연결 하려는 시도가 먼저 ipv6 ip로 해결 되었기 때문 입니다. 해당 ipv6 ip를 연결하는 데 문제가 발생하여 중단 되면 명시 적으로 설정 timeout=<N seconds>하고 시간 초과에 도달 한 경우에만 ipv4 ip를 재 시도 합니다.

내 솔루션은 원숭이 패치 파이썬을 socket위해 IPv6를 무시 하거나, (또는 IPv4의 IPv4를 작동하지 않는 경우) 이 답변 또는 이 대답은 나를위한 작품이다.

ipv6이 완료 될 때까지 기다리지 않고 ipv4를 연결 curl하기 때문에 명령이 작동하는 이유가 궁금 할 수 있습니다 curl. strace -ff -e network -s 10000 -- curl -vLk '<your url>'명령을 사용 하여 소켓 시스템 호출을 추적 할 수 있습니다 . 파이썬의 경우 strace -ff -e network -s 10000 -- python3 <your python script>명령을 사용할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.