파이썬 요청 시간 초과. 전체 응답 받기


169

웹 사이트 목록에서 통계를 수집하고 있으며 단순성을 위해 요청을 사용하고 있습니다. 내 코드는 다음과 같습니다.

data=[]
websites=['http://google.com', 'http://bbc.co.uk']
for w in websites:
    r= requests.get(w, verify=False)
    data.append( (r.url, len(r.content), r.elapsed.total_seconds(), str([(l.status_code, l.url) for l in r.history]), str(r.headers.items()), str(r.cookies.items())) )

requests.get루프가 멈추지 않도록 10 초 후에 시간 초과 하고 싶습니다 .

이 질문은 이전 에도 관심이 있었지만 정답은 없습니다. 나는 좋은 답변을 얻기 위해 이것에 현상금을 넣을 것입니다.

요청을 사용하지 않는 것이 좋은 아이디어라고 생각하지만 요청이 제공하는 좋은 것들을 어떻게 얻어야합니까? (튜플에있는 것들)


1
어떤 답변을 찾고 있습니까? (또는 다른 말로하면, 현재의 대답이 왜 충분하지 않습니까?)
yuvi

우리는 현상금의 유예 기간에 있습니다. 답을 고를 시간?
totokaka

나는 여전히 eventlet 솔루션과 신호를 결정하고 있습니다. 오늘 밤까지 질문을 수여 할 것입니다.
Kiarash


답변:


137

eventlet 사용은 어떻습니까? 10 초 후에 요청 시간을 초과하려는 경우 데이터를 수신하더라도이 스 니펫은 다음과 같이 작동합니다.

import requests
import eventlet
eventlet.monkey_patch()

with eventlet.Timeout(10):
    requests.get("http://ipv4.download.thinkbroadband.com/1GB.zip", verify=False)

115
분명히 이것은 불필요하게 복잡합니다.
holdenweb

7
감사합니다. 귀하의 솔루션의 기술적 우월성을 이해하고 (답변의 시작 부분에서 간결하게 진술했습니다.) 타사 모듈의 문제는 모듈을 가져 오는 것이 아니라 가져올 수 있도록하는 것이므로 가능한 경우 표준 라이브러리를 사용하는 것이 좋습니다.
holdenweb

9
인가 eventlet.monkey_patch()가 필요?
사용자

3
예, socket모듈은 원숭이 패치가 필요합니다. 따라서 최소한 다음이 필요합니다.eventlet.monkey_patch(socket=True)
Alvaro

53
2018 년 현재이 답변은 구식입니다. 사용requests.get('https://github.com', timeout=5)
CONvid19

313

제한 시간 매개 변수를 설정하십시오 .

r = requests.get(w, verify=False, timeout=10) # 10 seconds

stream=True해당 요청을 설정하지 않으면 requests.get()연결에 10 초 이상이 걸리거나 서버가 10 초 이상 데이터를 보내지 않으면 호출 시간이 초과됩니다.


31
그것은 전체 응답이 아닙니다. requests.readthedocs.org/ko/latest/user/quickstart/#timeouts
Kiarash

1
그렇습니다. 어떤 상황에서는 그렇습니다. 그러한 상황 중 하나는 당신의 것입니다. =) 확신이 없으면 코드를 보도록 권유합니다.
Lukasa

어떤 상황입니까?
Kiarash

1
방금 이것을 확인 했는데 멈추지 않았습니다. r = requests.get ( ' ipv4.download.thinkbroadband.com/1GB.zip ', timeout = 20)
Kiarash

5
아 죄송합니다, 당신이 '전체 답변'을 말할 때 당신이 무슨 뜻인지 이해하지 못했습니다. 네, 그렇습니다. 총 대기 시간의 상한이 아닙니다.
Lukasa

85

업데이트 : https://requests.readthedocs.io/en/master/user/advanced/#timeouts

새 버전에서 requests:

다음과 같이 시간 초과에 단일 값을 지정하면

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

타임 아웃 값은 connectread타임 아웃 모두에 적용됩니다 . 값을 별도로 설정하려면 튜플을 지정하십시오.

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

원격 서버가 매우 느린 경우 시간 제한 값으로 없음을 전달한 다음 커피 한 잔을 검색하여 요청에 응답을 영원히 기다리도록 요청할 수 있습니다.

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

오래된 (아마 구식) 답변 (오래 전에 게시 됨) :

이 문제를 극복하는 다른 방법이 있습니다.

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):
        connect = kwargs.get('connect', 5)
        read = kwargs.get('read', connect)
        super(MyTimeout, self).__init__(connect=connect, read=read)

requests.adapters.TimeoutSauce = MyTimeout

이 코드로 인해 읽기 시간 초과가 연결 시간 초과와 동일하게 설정됩니다. 이는 세션 .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))

kevinburke가이 요청 을 기본 요청 프로젝트에 병합하도록 요청했지만 아직 승인되지 않았습니다.


옵션 1이 작동하지 않습니다. 그 스레드를 계속 읽으면 다른 사람들이 "이것은 당신의 유스 케이스에서 작동하지 않을 것입니다. "읽을 시간 초과보다 많은 시간 동안 데이터 전송을 중단합니다."
Kiarash

Windows를 사용하고 signal.alarm은 Linux 전용이기 때문에 Signal을 사용하는 스레드에 또 다른 멋진 솔루션이 있습니다.
Kiarash

@Kiarash 아직 테스트하지 않았습니다. 그러나 Lukasa가 언제 말했는지 이해 this won't work for you use-case합니다. 그는 다른 사람이 원하는 mp3 스트림으로는 작동하지 않는다는 것을 의미했습니다.
Hieu

1
@Hieu-이것은 또 다른 풀 요청에 합병되었습니다 -github.com/kennethreitz/requests/pull/…
yprez September

timeout = 아무도 통화를 차단하지 않습니다.
crazydan

49

timeout = int(seconds)

이므로 다음 requests >= 2.4.0과 같은 timeout인수를 사용할 수 있습니다 .

requests.get('https://duckduckgo.com/', timeout=10)

노트 :

timeout전체 응답 다운로드의 시간 제한이 아닙니다. 오히려, exception서버가 제한 시간 (초) 동안 응답을 발행하지 않은 경우 (보다 정확하게는 제한 시간 (초) 동안 기본 소켓에 바이트가 수신되지 않은 경우)가 발생합니다. 시간 초과를 명시 적으로 지정하지 않으면 요청 시간이 초과되지 않습니다.


새로운 타임 아웃 매개 변수가있는 요청 버전은 무엇입니까?
Rusty

1
버전 2.4.0 이후 인 것 같습니다 : 연결 시간 종료 지원! 제한 시간은 이제 개별 연결 및 읽기 제한 시간을 설정하는 데 사용되는 튜플 (연결, 읽기)을 허용합니다 . pypi.org/project/requests/2.4.0
CONvid19

23

타임 아웃을 만들려면 신호 를 사용할 수 있습니다 .

이 경우를 해결하는 가장 좋은 방법은 아마도

  1. 알람 신호 처리기로 예외 설정
  2. 10 초 지연으로 경보 신호 호출
  3. try-except-finally블록 안에서 함수를 호출하십시오 .
  4. 기능이 시간 초과되면 except 블록에 도달합니다.
  5. finally 블록에서는 알람을 중단하므로 나중에 신호를 보내지 않습니다.

예제 코드는 다음과 같습니다.

import signal
from time import sleep

class TimeoutException(Exception):
    """ Simple Exception to be called on timeouts. """
    pass

def _timeout(signum, frame):
    """ Raise an TimeoutException.

    This is intended for use as a signal handler.
    The signum and frame arguments passed to this are ignored.

    """
    # Raise TimeoutException with system default timeout message
    raise TimeoutException()

# Set the handler for the SIGALRM signal:
signal.signal(signal.SIGALRM, _timeout)
# Send the SIGALRM signal in 10 seconds:
signal.alarm(10)

try:    
    # Do our code:
    print('This will take 11 seconds...')
    sleep(11)
    print('done!')
except TimeoutException:
    print('It timed out!')
finally:
    # Abort the sending of the SIGALRM signal:
    signal.alarm(0)

여기에는 몇 가지주의 사항이 있습니다.

  1. 스레드 안전이 아니며 신호가 항상 메인 스레드로 전달되므로 다른 스레드에 넣을 수 없습니다.
  2. 신호 스케줄링 및 실제 코드 실행 후 약간의 지연이 있습니다. 이것은 예제가 10 초 동안 만 잤더라도 시간이 초과됨을 의미합니다.

그러나 모든 것이 표준 파이썬 라이브러리에 있습니다! 절전 기능 가져 오기를 제외하고 한 번만 가져옵니다. 타임 아웃을 여러 곳에서 사용하려는 경우 TimeoutException, _timeout 및 singaling을 함수에 쉽게 넣고 호출 할 수 있습니다. 또는 데코레이터를 만들어 기능에 적용 할 수 있습니다 (아래 링크 참조).

이것을 "컨텍스트 관리자" 로 설정 하여 with명령문 과 함께 사용할 수도 있습니다 .

import signal
class Timeout():
    """ Timeout for use with the `with` statement. """

    class TimeoutException(Exception):
        """ Simple Exception to be called on timeouts. """
        pass

    def _timeout(signum, frame):
        """ Raise an TimeoutException.

        This is intended for use as a signal handler.
        The signum and frame arguments passed to this are ignored.

        """
        raise Timeout.TimeoutException()

    def __init__(self, timeout=10):
        self.timeout = timeout
        signal.signal(signal.SIGALRM, Timeout._timeout)

    def __enter__(self):
        signal.alarm(self.timeout)

    def __exit__(self, exc_type, exc_value, traceback):
        signal.alarm(0)
        return exc_type is Timeout.TimeoutException

# Demonstration:
from time import sleep

print('This is going to take maximum 10 seconds...')
with Timeout(10):
    sleep(15)
    print('No timeout?')
print('Done')

이 컨텍스트 관리자 접근 방식의 한 가지 단점은 코드가 실제로 시간 초과되었는지 여부를 알 수 없다는 것입니다.

출처 및 권장 자료 :


3
신호만을 따라서는, 메인 쓰레드에 전달 defnitely 하지, 다른 스레드에서 작동하지 않습니다 아마 .
Dima Tisnek

1
초과 - 장식 된 패키지는 신호 (또는 선택적 다중 처리)를 사용하는 타임 아웃 장식을 제공한다.
Christian Long

13

시간 초과 및 오류 처리와 함께이 요청을 시도하십시오.

import requests
try: 
    url = "http://google.com"
    r = requests.get(url, timeout=10)
except requests.exceptions.Timeout as e: 
    print e

5

설정 stream=True하고 사용하십시오 r.iter_content(1024). 예, eventlet.Timeout어떻게 든 작동하지 않습니다.

try:
    start = time()
    timeout = 5
    with get(config['source']['online'], stream=True, timeout=timeout) as r:
        r.raise_for_status()
        content = bytes()
        content_gen = r.iter_content(1024)
        while True:
            if time()-start > timeout:
                raise TimeoutError('Time out! ({} seconds)'.format(timeout))
            try:
                content += next(content_gen)
            except StopIteration:
                break
        data = content.decode().split('\n')
        if len(data) in [0, 1]:
            raise ValueError('Bad requests data')
except (exceptions.RequestException, ValueError, IndexError, KeyboardInterrupt,
        TimeoutError) as e:
    print(e)
    with open(config['source']['local']) as f:
        data = [line.strip() for line in f.readlines()]

토론은 여기 https://redd.it/80kp1h


부끄러운 요청은 maxtime 매개 변수를 지원하지 않습니다.이 솔루션은 asyncio와 함께 작동하는 유일한 솔루션입니다.
wukong

4

이는 과도 할 수 있지만 Celery 분산 작업 대기열은 시간 초과를 효과적으로 지원합니다.

특히 프로세스에서 예외를 발생시키는 소프트 타임 제한 (정리 가능) 및 / 또는 시간 제한이 초과되었을 때 작업을 종료하는 어려운 시간 제한을 정의 할 수 있습니다.

표지 아래에서는 "이전"게시물에서 참조한 것과 동일한 신호 방식을 사용하지만보다 유용하고 관리하기 쉬운 방식으로 사용합니다. 또한 모니터링중인 웹 사이트 목록이 길면 많은 작업을 관리하는 모든 종류의 방법 인 기본 기능을 활용할 수 있습니다.


이것은 좋은 해결책이 될 수 있습니다. 총 시간 제한의 문제는 직접 관련이 없습니다 python-requests만에 httplib(파이썬 2.7의 요청에 의해 사용). 패키지는 timeouthttplib 에 직접 관련된 모든 것을 전달 합니다. httplib에서 프로세스가 오랫동안 머무를 수 있기 때문에 요청에서 아무것도 고칠 수 있다고 생각합니다.
hynekcer 2013

@ hynekcer, 당신이 옳다고 생각합니다. 이것이 Celery와 마찬가지로 프로세스를 완전히 종료하여 프로세스에서 시간 초과를 감지하고 적용하는 것이 좋은 방법 일 수 있습니다.
Chris Johnson

3

multiprocessing타사 패키지를 사용 하고 의존 할 수 없다고 생각합니다 .

import multiprocessing
import requests

def call_with_timeout(func, args, kwargs, timeout):
    manager = multiprocessing.Manager()
    return_dict = manager.dict()

    # define a wrapper of `return_dict` to store the result.
    def function(return_dict):
        return_dict['value'] = func(*args, **kwargs)

    p = multiprocessing.Process(target=function, args=(return_dict,))
    p.start()

    # Force a max. `timeout` or wait for the process to finish
    p.join(timeout)

    # If thread is still active, it didn't finish: raise TimeoutError
    if p.is_alive():
        p.terminate()
        p.join()
        raise TimeoutError
    else:
        return return_dict['value']

call_with_timeout(requests.get, args=(url,), kwargs={'timeout': 10}, timeout=60)

에 전달 된 타임 아웃이 kwargs얻을 수있는 제한 시간 인 모든 서버에서 응답을, 인수가 timeout얻을 수있는 제한 시간입니다 전체 응답을.


이것은 모든 오류를 잡아서 return_dict [ 'error']에 넣는 개인 함수의 일반적인 try / except를 사용하여 향상시킬 수 있습니다. 그런 다음 종료하기 전에 return_dict에서 'error'가 있는지 확인한 다음 올리십시오. 테스트하기도 훨씬 쉽습니다.
dialt0ne

2

timeout = (연결 시간 초과, 데이터 읽기 시간 초과) 또는 단일 인수 제공 (timeout = 1)

import requests

try:
    req = requests.request('GET', 'https://www.google.com',timeout=(1,1))
    print(req)
except requests.ReadTimeout:
    print("READ TIME OUT")

1

이 코드는 socketError 11004 및 10060에서 작동합니다 ...

# -*- encoding:UTF-8 -*-
__author__ = 'ACE'
import requests
from PyQt4.QtCore import *
from PyQt4.QtGui import *


class TimeOutModel(QThread):
    Existed = pyqtSignal(bool)
    TimeOut = pyqtSignal()

    def __init__(self, fun, timeout=500, parent=None):
        """
        @param fun: function or lambda
        @param timeout: ms
        """
        super(TimeOutModel, self).__init__(parent)
        self.fun = fun

        self.timeer = QTimer(self)
        self.timeer.setInterval(timeout)
        self.timeer.timeout.connect(self.time_timeout)
        self.Existed.connect(self.timeer.stop)
        self.timeer.start()

        self.setTerminationEnabled(True)

    def time_timeout(self):
        self.timeer.stop()
        self.TimeOut.emit()
        self.quit()
        self.terminate()

    def run(self):
        self.fun()


bb = lambda: requests.get("http://ipv4.download.thinkbroadband.com/1GB.zip")

a = QApplication([])

z = TimeOutModel(bb, 500)
print 'timeout'

a.exec_()

창의성에 대한지지
JSmyth

1

요청에 관한 질문에도 불구하고 pycurl CURLOPT_TIMEOUT관련 이 매우 쉽다는 것을 알았습니다. 또는 CURLOPT_TIMEOUT_MS 있습니다.

스레딩 또는 신호가 필요하지 않습니다.

import pycurl
import StringIO

url = 'http://www.example.com/example.zip'
timeout_ms = 1000
raw = StringIO.StringIO()
c = pycurl.Curl()
c.setopt(pycurl.TIMEOUT_MS, timeout_ms)  # total timeout in milliseconds
c.setopt(pycurl.WRITEFUNCTION, raw.write)
c.setopt(pycurl.NOSIGNAL, 1)
c.setopt(pycurl.URL, url)
c.setopt(pycurl.HTTPGET, 1)
try:
    c.perform()
except pycurl.error:
    traceback.print_exc() # error generated on timeout
    pass # or just pass if you don't want to print the error

1

옵션 stream=True을 사용하는 경우 다음 을 수행 할 수 있습니다.

r = requests.get(
    'http://url_to_large_file',
    timeout=1,  # relevant only for underlying socket
    stream=True)

with open('/tmp/out_file.txt'), 'wb') as f:
    start_time = time.time()
    for chunk in r.iter_content(chunk_size=1024):
        if chunk:  # filter out keep-alive new chunks
            f.write(chunk)
        if time.time() - start_time > 8:
            raise Exception('Request took longer than 8s')

이 솔루션에는 신호 또는 다중 처리가 필요하지 않습니다.


1

또 하나의 솔루션 ( http://docs.python-requests.org/en/master/user/advanced/#streaming-uploads 에서 얻었습니다 )

업로드하기 전에 컨텐츠 크기를 확인할 수 있습니다.

TOO_LONG = 10*1024*1024  # 10 Mb
big_url = "http://ipv4.download.thinkbroadband.com/1GB.zip"
r = requests.get(big_url, stream=True)
print (r.headers['content-length'])
# 1073741824  

if int(r.headers['content-length']) < TOO_LONG:
    # upload content:
    content = r.content

그러나 발신자가 'content-length'응답 필드에 잘못된 값을 설정할 수 있습니다.


감사. 깨끗하고 간단한 솔루션. 나를 위해 작동합니다.
petezurich

0

그렇다면 10 초 후에 요청의 내부 상태를 엉망으로 만드는 감시 스레드를 만듭니다 .

  • 기본 소켓을 닫고 이상적으로
  • 요청이 작업을 재 시도하면 예외를 트리거합니다.

시스템 라이브러리에 따라 DNS 확인 시한을 설정하지 못할 수도 있습니다.


0

글쎄, 나는이 페이지에서 많은 솔루션을 시도했지만 여전히 불안정성, 임의 중단, 연결 성능 ​​저하에 직면했습니다.

저는 현재 Curl을 사용하고 있으며, "불완전한 구현"이 있더라도 "최대 시간"기능과 글로벌 성능에 대해 정말 행복합니다.

content=commands.getoutput('curl -m6 -Ss "http://mywebsite.xyz"')

여기에서는 연결 시간과 전송 시간을 모두 포함하여 6 초 최대 시간 매개 변수를 정의했습니다.

파이썬 구문을 고수하려는 경우 컬에 멋진 파이썬 바인딩이 있다고 확신합니다. :)


0

모든 파이썬 함수를 시간 초과하는 데 사용할 수있는 timeout-decorator 라는 패키지가 있습니다.

@timeout_decorator.timeout(5)
def mytest():
    print("Start")
    for i in range(1,10):
        time.sleep(1)
        print("{} seconds have passed".format(i))

여기에는 일부 답변에서 제안하는 신호 방식이 사용됩니다. 또는 신호 대신 멀티 프로세싱을 사용하도록 지시 할 수 있습니다 (예 : 멀티 스레드 환경에있는 경우).


0

요청 2.2.1을 사용하고 있으며 eventlet이 작동하지 않았습니다. 대신 gevent가 gunicorn에 대한 서비스에 사용되기 때문에 대신 gevent 시간 초과를 사용할 수있었습니다.

import gevent
import gevent.monkey
gevent.monkey.patch_all(subprocess=True)
try:
    with gevent.Timeout(5):
        ret = requests.get(url)
        print ret.status_code, ret.content
except gevent.timeout.Timeout as e:
    print "timeout: {}".format(e.message)

gevent.timeout.Timeout은 일반적인 예외 처리에 의해 포착되지 않습니다. 따라서 gevent.timeout.Timeout 다른 예외를 명시 적으로 포착 하거나 전달하여 다음과 같이 사용하십시오 with gevent.Timeout(5, requests.exceptions.Timeout):.이 예외가 발생하면 메시지가 전달되지 않습니다.


-1

나는 명백히 추악하지만 실제 문제를 해결하는보다 직접적인 솔루션을 생각해 냈습니다. 다음과 같이 조금갑니다 :

resp = requests.get(some_url, stream=True)
resp.raw._fp.fp._sock.settimeout(read_timeout)
# This will load the entire response even though stream is set
content = resp.content

여기 에서 전체 설명을 읽을 수 있습니다


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