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

내 스크립트에서 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사용 하는 기본 제한 시간은 무엇입니까 ?



답변

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

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

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

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


답변

에서 요청 문서 :

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 전체 응답에 대한 시간 초과


답변

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

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

크레딧 : 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,...


답변

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


답변

문서화 된 “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 자체에도 기본 시간 초과가 있기 때문에 거의 아무것도 중단 할 수 없습니다.


답변

제 경우에는 “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>명령을 사용할 수 있습니다.


답변