Тайм-аут для запросов python.получить полный ответ



Я собираю статистику по списку веб-сайтов, и я использую запросы для него для простоты. Вот мой код:



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


теперь, я хочу!--1--> тайм-аут через 10 секунд, чтобы цикл не застрял.



этот вопрос был интересен до тоже, но ни один из ответов чистые. Я поставлю некоторую награду на это, чтобы получить хороший ответ.



Я слышал, что, возможно, не используя запросы-это хорошая идея, но тогда как я должен сделать хорошие вещи просит предложить. (те, что в кортеже)

2774   16  

16 ответов:

Как насчет использования eventlet? Если вы хотите тайм-аут запроса через 10 секунд, даже если данные получены, этот фрагмент будет работать для вас:

import requests
import eventlet
eventlet.monkey_patch()

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

установить параметр timeout:

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

пока вы не установите stream=True по этому запросу, это вызовет вызов requests.get() тайм-аут, если соединение занимает более десяти секунд, или если сервер не отправляет данные в течение более десяти секунд.

обновление:http://docs.python-requests.org/en/master/user/advanced/#timeouts

в новой редакции requests:

если вы укажете одно значение для таймаута, например:

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

значение таймаута будет применено к обоим connect и read тайм-ауты. Укажите кортеж, если вы хотите установить значения отдельно:

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

если удаленный сервер работает очень медленно, вы можете сказать запросы ждать навсегда для ответа, передав None в качестве значения тайм-аута, а затем получив чашку кофе.

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 () call. (Заметьте, что я не тестировал этот код, так возможно, потребуется быстрая отладка, я просто написал ее прямо в Окна на GitHub.)

2. Используйте вилку запросов от кевинбурка: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. установить исключение, в качестве обработчика сигнала тревоги
  2. вызовите сигнал тревоги с десятисекундной задержкой
  3. вызовите функцию внутри a try-except-finally блок.
  4. блок except достигается, если истекло время ожидания функции.
  5. в последнем блоке вы прерываете сигнал тревоги, поэтому он не поется позже.

вот пример кода:

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. это не threadsafe, сигналы всегда доставляются в основной поток, поэтому вы не можете поместить это в любой другой поток.
  2. существует небольшая задержка после планирования сигнала и выполнения фактического кода. Это означает, что пример будет тайм-аут, даже если он спал только в течение десяти секунд.

но это все в стандартной библиотеке python! За исключением функции сна импорта это только один импорт. Если вы собираетесь использовать тайм-ауты во многих местах, вы можете легко поместить 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 = 10
        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')

один из возможных вниз сторона этого подхода к контекстному менеджеру заключается в том, что вы не можете знать, действительно ли код тайм-аут или нет.

источники и рекомендуемая литература:

Это может быть излишним, но распределенная очередь задач сельдерея имеет хорошую поддержку для таймаутов.

в частности, вы можете определить мягкий лимит времени, который просто вызывает исключение в вашем процессе (так что вы можете очистить) и/или жесткий лимит времени, который завершает задачу, когда лимит времени был превышен.

под обложками это использует тот же подход к сигналам, что и в вашем сообщении "до", но более удобным и управляемым способом. А если список веб сайты, которые вы отслеживаете долго, вы можете извлечь выгоду из его основной функции-все виды способов управления выполнением большого количества задач.

Я считаю, что вы можете использовать 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 - тайм-аут, чтобы получить полное ответ.

этот код работает для 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_()

несмотря на то, что вопрос касается запросов, я нахожу это очень легко сделать с pycurlCURLOPT_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

извините, но мне интересно, почему никто не предложил более простое решение? :-о

запросы. get('www.mypage.com', timeout=20)

Ну, я пробовал много решений на этой странице и все еще сталкивался с нестабильностью, случайными зависаниями, плохой производительностью соединений.

теперь я использую Curl, и я действительно доволен его функциональностью "max time" и глобальными выступлениями, даже с такой плохой реализацией:

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

здесь я определил параметр максимального времени 6 секунд, охватывающий как время соединения, так и время передачи.

Я уверен, что Curl имеет хорошую привязку python, если вы предпочитаете придерживаться питонский синтаксис:)

Set 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

в случае, если вы используете опцию 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')

решение не требует сигналов или многопроцессорной обработки.

просто еще одно решение (получил его от 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".

есть пакет под названием тайм-аут-оформителя что вы можете использовать для тайм-аута любой функции python.

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

Он использует подход сигналов, который предлагают некоторые ответы здесь. Кроме того, вы можете сказать ему использовать многопроцессорную обработку вместо сигналов (например, если вы находитесь в многопоточной среде).

Если дело дойдет до этого, создайте сторож поток, который портит внутреннее состояние запросов через 10 секунд, например:

  • закрывает базовый сокет, а в идеале
  • вызывает исключение, если просит повторить операцию

обратите внимание, что в зависимости от системных библиотек вы не можете установить крайний срок разрешения DNS.

Я придумал более прямое решение, которое, по общему признанию, уродливо, но исправляет реальную проблему. Он идет немного так:

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

вы можете прочитать полное объяснение здесь

Comments

    Ничего не найдено.