Тайм-аут для запросов 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 секунд, чтобы цикл не застрял.
этот вопрос был интересен до тоже, но ни один из ответов чистые. Я поставлю некоторую награду на это, чтобы получить хороший ответ.
Я слышал, что, возможно, не используя запросы-это хорошая идея, но тогда как я должен сделать хорошие вещи просит предложить. (те, что в кортеже)
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 просил он должен быть объединен в основной запрашивает проект, но он еще не принят.
для создания таймаута вы можете использовать сигналы.
лучший способ решить это дело, вероятно,
- установить исключение, в качестве обработчика сигнала тревоги
- вызовите сигнал тревоги с десятисекундной задержкой
- вызовите функцию внутри a
try-except-finallyблок.- блок except достигается, если истекло время ожидания функции.
- в последнем блоке вы прерываете сигнал тревоги, поэтому он не поется позже.
вот пример кода:
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)есть некоторые предостережения к этому:
- это не threadsafe, сигналы всегда доставляются в основной поток, поэтому вы не можете поместить это в любой другой поток.
- существует небольшая задержка после планирования сигнала и выполнения фактического кода. Это означает, что пример будет тайм-аут, даже если он спал только в течение десяти секунд.
но это все в стандартной библиотеке 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')один из возможных вниз сторона этого подхода к контекстному менеджеру заключается в том, что вы не можете знать, действительно ли код тайм-аут или нет.
источники и рекомендуемая литература:
- документация по сигналам
- этот ответ на тайм-ауты @David Narayan. Он организовал приведенный выше код как оформителя.
Это может быть излишним, но распределенная очередь задач сельдерея имеет хорошую поддержку для таймаутов.
в частности, вы можете определить мягкий лимит времени, который просто вызывает исключение в вашем процессе (так что вы можете очистить) и/или жесткий лимит времени, который завершает задачу, когда лимит времени был превышен.
под обложками это использует тот же подход к сигналам, что и в вашем сообщении "до", но более удобным и управляемым способом. А если список веб сайты, которые вы отслеживаете долго, вы можете извлечь выгоду из его основной функции-все виды способов управления выполнением большого количества задач.
Я считаю, что вы можете использовать
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