Тайм-аут для всех запросов 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())) )
Теперь я хочу, чтобы requests.get
истекал тайм-аут через 10 секунд, поэтому цикл не застревает.
Этот вопрос был интересен до, но ни один из ответов не является чистым. Я получу немного щедрости, чтобы получить хороший ответ.
Я слышал, что, возможно, не использование запросов - это хорошая идея, но тогда как я могу получить предложения о приятных вещах. (те, которые указаны в кортеже)
Ответы
Ответ 1
Как насчет использования eventlet? Если вы хотите отсрочить запрос через 10 секунд, даже если данные будут получены, этот фрагмент будет работать для вас:
import requests
import eventlet
eventlet.monkey_patch()
with eventlet.Timeout(10):
requests.get("http://ipv4.download.thinkbroadband.com/1GB.zip", verify=False)
Ответ 2
Установите параметр timeout:
r = requests.get(w, verify=False, timeout=10)
Пока вы не устанавливаете stream=True
в этом запросе, это приведет к отключению вызова requests.get()
, если соединение заняло более десяти секунд или если сервер не отправляет данные больше, чем десять секунд.
Ответ 3
UPDATE: 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
Этот код должен заставлять нас устанавливать таймаут чтения равным время ожидания подключения, которое является значением тайм-аута, которое вы передаете на своем 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))
kevinburke запросил его объединить в проект основных запросов, но он еще не принят.
Ответ 4
timeout = int(seconds)
Так как requests >= 2.4.0
, вы можете использовать аргумент времени timeout
requests
, а именно:
requests.get(url, timeout=10)
Замечания:
timeout
не является ограничением по времени загрузки всего ответа; скорее, возникает exception
если сервер не выдал ответ в течение секунд ожидания (точнее, если в базовом сокете не было получено ни одного байта в течение секунд ожидания). Если тайм-аут не указан явно, запросы не истекают.
Ответ 5
Для создания тайм-аута вы можете использовать сигналы.
Лучший способ решить это дело, вероятно,
- Установить исключение в качестве обработчика для сигнала тревоги
- Вызовите сигнал тревоги с задержкой в десять секунд
- Вызовите функцию внутри блока
try-except-finally
. - Блок исключений достигается, если время ожидания истекло.
- В блоке 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)
Есть некоторые оговорки к этому:
- Это не потокобезопасно, сигналы всегда доставляются в основной поток, поэтому вы не можете поместить это в любой другой поток.
- После планирования сигнала и выполнения фактического кода наблюдается небольшая задержка. Это означает, что время ожидания истекло бы, даже если он спал только десять секунд.
Но все это в стандартной библиотеке Python! За исключением импорта функции сна это только один импорт. Если вы собираетесь использовать тайм-ауты во многих местах, вы можете легко поместить TimeoutException, _timeout и singaling в функцию и просто вызвать это. Или вы можете сделать декоратор и поставить его на функции, см. Ответ ниже.
Вы также можете установить это как "менеджер контекста", чтобы использовать его with
оператором 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')
Один из возможных недостатков этого подхода к контекстному менеджеру состоит в том, что вы не можете знать, истек ли срок действия кода или нет.
Источники и рекомендуемое чтение:
Ответ 6
Это может быть излишним, но распределенная очередь задач Selery имеет хорошую поддержку тайм-аутов.
В частности, вы можете определить мягкий лимит времени, который просто вызывает исключение в вашем процессе (так что вы можете очистить) и/или жесткое ограничение времени, которое завершает задачу, когда предел превышен.
Под обложками используется тот же подход к сигналам, что и ссылка на "до", но более удобная и управляемая. И если список веб-сайтов, которые вы контролируете, длинный, вы можете воспользоваться его основной функцией - всеми способами управления выполнением большого количества задач.
Ответ 7
простите, но мне интересно, почему никто не предложил следующее более простое решение? : -o
## request
requests.get('www.mypage.com', timeout=20)
Ответ 8
Я считаю, что вы можете использовать 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
- это таймаут, чтобы получить полный ответ.
Ответ 9
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")
Ответ 10
этот код работает для 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_()
Ответ 11
Несмотря на вопрос о запросах, я считаю, что это очень легко сделать с 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
Ответ 12
Еще одно решение (получено из 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
Но будьте осторожны, отправитель может установить неправильное значение в поле ответа "длина контента".
Ответ 13
Если дело доходит до этого, создайте сторожевой поток, который перепутает внутреннее состояние запросов через 10 секунд, например:
- закрывает базовый сокет и в идеале
- вызывает исключение, если запросы повторяют операцию
Обратите внимание, что в зависимости от системных библиотек вы не сможете установить крайний срок для разрешения DNS.
Ответ 14
Ну, я попробовал много решений на этой странице и по-прежнему сталкивался с неустойчивостями, случайными зависаниями, плохой производительностью соединений.
Теперь я использую Curl, и я очень доволен функциональностью "max time" и глобальными характеристиками даже при такой плохой реализации:
content=commands.getoutput('curl -m6 -Ss "http://mywebsite.xyz"')
Здесь я определил максимальный временной параметр 6 секунд, закрепив как время соединения, так и время передачи.
Я уверен, что Curl имеет хорошее связывание с python, если вы предпочитаете придерживаться пифонического синтаксиса:)
Ответ 15
Установите 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
Ответ 16
Если вы используете опцию 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')
Решение не требует сигналов или многопроцессорности.
Ответ 17
Существует пакет под названием timeout-decorator, который можно использовать для тайм-аута любой функции python.
@timeout_decorator.timeout(5)
def mytest():
print("Start")
for i in range(1,10):
time.sleep(1)
print("{} seconds have passed".format(i))
Он использует подход сигналов, что некоторые ответы здесь предлагают. В качестве альтернативы вы можете сказать ему использовать многопроцессорную обработку вместо сигналов (например, если вы находитесь в многопоточном окружении).
Ответ 18
Попробуйте этот запрос с тайм-аутом и обработкой ошибок:
import requests
try:
url = "http://google.com"
r = requests.get(url, timeout=10)
except requests.exceptions.Timeout as e:
print e
Ответ 19
Я придумал более прямое решение, которое, по общему признанию, уродливо, но фиксирует реальную проблему. Это примерно так:
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
Вы можете прочитать полное объяснение здесь