В этом материале описаны продвинутые функции библиотеки Requests.
Объекты Session
Объект Session
позволяет сохранять определенные параметры между запросами. Он же сохраняет куки всех запросов, сделанных из экземпляра Session
.
Объект Session
включает все методы основного API Requests.
Попробуем передать куки между запросами:
s = requests.Session()
s.get('https://httpbin.org/cookies/set/sessioncookie/123456789')
r = s.get("https://httpbin.org/cookies")
print(r.text)
# '{"cookies": {"sessioncookie": "123456789"}}'
Session
могут также использоваться для предоставления данных по умолчанию для методов запроса. Для этого их нужно передать в параметры объекта:
s = requests.Session()
s.auth = ('user', 'pass')
s.headers.update({'x-test': 'true'})
# отправка с 'x-test' и 'x-test2'
s.get('http://httpbin.org/headers', headers={'x-test2': 'true'})
Любые словари, переданные методу запроса? будут объединены с заданными значениями уровня сессии. Параметры уровня методов перезаписывают параметры сессии.
Удалите значение из параметра словаря:
Иногда нужно будет не включать ключи уровня сессии в параметры словаря. Для этого необходимо установить значения ключаNone
в параметре уровня методов. Они будут пропускаться автоматически.
Все значения, содержащиеся в сессии, прямо доступны. Подробнее об этом в документации API Session.
Объекты запросов и ответов
Каждый раз при вызове запроса происходят две вещи. Во-первых, создается объект Request
, который будет направлен на сервер, чтобы сделать запрос или вернуть определенный ресурс. Во-вторых, когда библиотека получает ответ от сервера, генерируется объект Response
. Он содержит всю информацию, которую вернул сервер и оригинальный объект Request
. Вот простой запрос для получения крайне важной информации с серверов Википедии:
>>> r = requests.get('https://ru.wikipedia.org/wiki/Монти_Пайтон')
Если нужно получить доступ к заголовкам, которые вернул сервер, делается следующее:
>>> r.headers
{'Date': 'Fri, 06 Mar 2020 10:40:21 GMT', 'Content-Type': 'text/html; charset=UTF-8',
'Server': 'mw1258.eqiad.wmnet', 'X-Powered-By': 'PHP/7.2.26-1+0~20191218.33+debian9~1.gbpb5a340+wmf1',
'X-Content-Type-Options': 'nosniff', 'P3P': 'CP="See [https://ru.wikipedia.org/wiki/Special:CentralAutoLogin/P3P](https://ru.wikipedia.org/wiki/Special:CentralAutoLogin/P3P) for more info."',
'Content-language': 'ru', 'Vary': 'Accept-Encoding,Cookie,Authorization',
'Last-Modified': 'Fri, 21 Feb 2020 10:40:21 GMT', 'Backend-Timing': 'D=181758 t=1583491220993575',
'X-ATS-Timestamp': '1583491221', 'Content-Encoding': 'gzip',
'X-Varnish': '1017342992 492505475', 'Age': '82909', 'X-Cache': 'cp3050 miss, cp3056 hit/43',
'X-Cache-Status': 'hit-front', 'Server-Timing': 'cache;desc="hit-front"',
'Strict-Transport-Security': 'max-age=106384710; includeSubDomains; preload',
'X-Client-IP': '111.111.111.1', 'Cache-Control': 'private, s-maxage=0, max-age=0, must-revalidate',
'Accept-Ranges': 'bytes', 'Content-Length': '41759', 'Connection': 'keep-alive'}
А если нужны те, что были направлены серверу, тогда сперва нужно получить доступ к запросу, а потом — к его заголовкам:
>>> r.request.headers
{'User-Agent': 'python-requests/2.21.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
Подготовка запросов
При получении объекта Response
от вызова API или Session
, используется атрибут PreparedRequest
функции request
. В некоторых случаях над телом и заголовками (и чем угодно еще) можно будет провести дополнительную работу перед отправкой запроса. Простейший способ следующий:
s = Session()
req = Request('POST', url, data=data, headers=headers)
prepped = req.prepare()
# делаем что-то с prepped.body
prepped.body = 'No, I want exactly this as the body.'
# делаем что-то с prepped.headers
del prepped.headers['Content-Type']
resp = s.send(prepped,
stream=stream,
verify=verify,
proxies=proxies,
cert=cert,
timeout=timeout
)
print(resp.status_code)
Поскольку с объектом Request
не происходит ничего особенного, его можно сразу подготовить и изменить объект PreparedRequest
. Затем он отправляется с остальными параметрами, которые вы бы отправили в requests.*
или Session.*
.
Однако этот код лишен кое-каких преимуществ использования объекта Session
. Если точнее, состояние уровня Session
, например куки, не будет применено к запросу. Чтобы получить PreparedRequest
с примененным состоянием, замените вызов к Request.prepare()
вызовом к Session.prepare_request()
:
from requests import Request, Session
s = Session()
req = Request('GET', url, data=data, headers=headers)
prepped = s.prepare_request(req)
# делаем что-то с prepped.body
prepped.body = 'Seriously, send exactly these bytes.'
# делаем что-то с prepped.headers
prepped.headers['Keep-Dead'] = 'parrot'
resp = s.send(prepped,
stream=stream,
verify=verify,
proxies=proxies,
cert=cert,
timeout=timeout
)
print(resp.status_code)
При использовании потока “prepared request” помните, что он не учитывает окружение. Это может привести к проблемам в том случае, если переменные окружения используются для изменения поведения запросов. Например: самозаверенные SSL-сертификаты, определенные в REQUESTS_CA_BUNDLE
, учитываться не будут. Результат — SSL: CERTIFICATE_VERIFY_FAILED
. Обойти это поведение можно, явно объединив настройки окружения с сессией:
from requests import Request, Session
s = Session()
req = Request('GET', url)
prepped = s.prepare_request(req)
# Объединяем настройки среды в сессию
settings = s.merge_environment_settings(prepped.url, {}, None, None, None)
resp = s.send(prepped, **settings)
print(resp.status_code)
Проверка сертификата SSL
Библиотека Requests может верифицировать SSL-сертификаты для HTTPS-запросов так же, как и браузер. Для проверки сертификата хоста, нужно просто добавить аргумент verify
:
>>> requests.get('https://kennethreitz.com', verify=True)
requests.exceptions.SSLError: hostname 'kennethreitz.com'
doesn't match either of '*.herokuapp.com', 'herokuapp.com'
Если такового нет или он недействителен, вернется ошибка SSLError. Но у Github, например, есть:
>>> requests.get('https://github.com', verify=True)
<Response [200]>
verify
можно передать и файлу CA_BUNDLE
для частных сертификатов. Или же настроить переменную среды REQUESTS_CA_BUNDLE
.
Библиотека может игнорировать проверку SSL-сертификатов, если значение verify
— False
.
>>> requests.get('https://kennethreitz.com', verify=False)
<Response [200]>
По умолчанию значение verify
— True
. Параметр подходит только для сертификатов хостов.
Можно также определить файл локального сертификата в виде пути или пары ключ-значение:
>>> requests.get('https://kennethreitz.com', cert=('/path/server.crt', '/path/key'))
<Response [200]>
Если указан неправильный путь или недействительный сертификат, произойдет следующее:
>>> requests.get('https://kennethreitz.com', cert='/wrong_path/server.pem')
SSLError: [Errno 336265225] _ssl.c:347: error:140B0009:SSL routines:SSL_CTX_use_PrivateKey_file:PEM lib
Работа с содержанием ответа
По умолчанию при запросе тело ответа загружается сразу же. Переписать это поведение и отсрочить загрузку тела ответа до того момента, пока не будет получен доступ к атрибуту Response.content
, можно с помощью параметра stream
:
tarball_url = 'https://github.com/kennethreitz/requests/tarball/master'
r = requests.get(tarball_url, stream=True)
Сейчас загружаются только заголовки ответа, а соединение остается открытым. Это позволяет сделать получение контента по условию:
if int(r.headers['content-length']) < TOO_LONG:
content = r.content
# ...
Можно и дальше контролировать процесс работы с помощью методов Response.iter_content
и Response.iter_lines
или чтения их из лежащей в основе библиотеки urllib3 urllib3.HTTPResponse
в Response.raw
.
Постоянное соединение
Благодаря urllib3 постоянное соединение поддерживается на 100% автоматически прямо в сессии. Любые запросы в сессии будут автоматически использовать соответствующее соединение.
Стоит отметить, что соединения сбрасываются и возвращаются в пул для повторного использовать только после чтения данных тела. Важно задать значение stream
равным False
или читать свойство property
объекта Response
.
Потоковые загрузки
Requests поддерживает потоковые загрузки, которые позволяют отправлять крупные потоки или файлы без их чтения прямо в память. Для этого нужно предоставить файловый объект в data
:
with open('massive-body') as f:
request.post('http://some.url/streamed', data=f)
Запросы для данных, разбитых на части (chunk-encoded)
Requests также поддерживает механизм передачи с разбиением на части для входящих и исходящих запросов. Для отправления такого нужно предоставить генератор (или любой итератор без определенной длины) в data
:
def gen():
yield 'hi'
yield 'there'
request.post('http://some.url/chunked', data=gen())
POST для нескольких файлов типа multipart
Можно отправить несколько файлов одним запросом. Например, предположим, необходимо загрузить файлы изображений в HTML-форму images
для нескольких файлов :
<input type="file" name="images" multiple="true" required="true"/>
Чтобы сделать это, просто представьте файлы в виде списка кортежей такого формата (form_field_name, file_info)
:
>>> url = 'https://httpbin.org/post'
>>> multiple_files = [
... ('images', ('foo.png', open('foo.png', 'rb'), 'image/png')),
... ('images', ('bar.png', open('bar.png', 'rb'), 'image/png'))]
>>> r = requests.post(url, files=multiple_files)
>>> r.text
{
...
'files': {'images': 'data:image/png;base64,iVBORw ....'}
'Content-Type': 'multipart/form-data; boundary=3131623adb2043caaeb5538cc7aa0b3a',
...
}
Предупреждение:
Рекомендуется открывать файлы в бинарном режиме. Это важно, потому чтоRequests
может попробовать предоставить заголовокContent-Length
. В таком случае значением будет количеством байт в файле. А ошибки возникнут, если открыть файл в текстовом режиме.
Хуки (перехват управления)
В Requests есть система хуков, которую можно использовать для управления частями процесса запроса или обработки событий.
Доступные хуки:
response
: ответ, сгенерированный из объектаRequest
.
Можно назначать функцию перехвата для каждого запроса, передавая словарь {hook_name: callback_function}
в параметр запроса hooks
:
hooks=dict(response=print_url)
callback_function
получит кусок данных в качестве первого аргумента.
def print_url(r):
print(r.url)
Если при выполнении обратного вызова произойдет ошибка, отобразится предупреждение.
Если функция вернет значение, предполагается, что оно должно заменить данные, которые были переданы. Когда функция не возвращает ничего, нет никакого эффекта.
Выведем некоторые аргументы метода запроса:
>>> requests.get('http://httpbin.org', hooks=dict(response=print_url))
http://httpbin.org
<Response [200]>
Собственная аутентификация
Requests позволяет указать собственный механизм аутентификации.
Любой вызываемый объект, передаваемый в качестве аргумента auth
методу запроса, может изменить запрос до его отправки.
Реализации аутентификации — это подклассы requests.auth.AuthBase
, и их легко определить. Requests предоставляет две общие схемы реализации аутентификации в requests.auth:HTTPBasicAuth
и HTTPDigestAuth
.
Представим, что есть веб-сервис, который отвечает только в том случае, если значение заголовка X-Pizza
— значение пароля. Такое маловероятно, но мало ли.
from requests.auth import AuthBase
class PizzaAuth(AuthBase):
"""Прикрепляет аутентификацию HTTP Pizza к данному объекту запроса."""
def __init__(self, username):
# любые данные, связанные с аутентификацией
self.username = username
def __call__(self, r):
# изменить и вернуть запрос
r.headers['X-Pizza'] = self.username
return r
Теперь можно сделать запрос с помощью PizzaAuth
:
>>> requests.get('http://pizzabin.org/admin', auth=PizzaAuth('kenneth'))
<Response [200]>
Потоковые запросы
С помощью requests.Response.iter_lines()
можно запросто перебирать потоковые API, такие как Twitter Streaming API.
Используем его для отслеживания ключа словаря requests
:
import requests
import json
r = requests.post('https://stream.twitter.com/1/statuses/filter.json',
data={'track': 'requests'}, auth=('username', 'password'), stream=True)
for line in r.iter_lines(decode_unicode=True):
if line:
print(json.loads(line))
Прокси
Если есть необходимость использовать прокси, можно настроить индивидуальные запросы с помощью аргумента proxies
для любого метода запроса:
import requests
proxies = {
"http": "10.10.1.10:3128",
"https": "10.10.1.10:1080",
}
requests.get("http://example.org", proxies=proxies)
Также их можно настроить с помощью переменных среды HTTP_PROXY
и HTTPS_PROXY
.
$ export HTTP_PROXY="10.10.1.10:3128"
$ export HTTPS_PROXY="10.10.1.10:1080"
$ python
>>> import requests
>>> requests.get("http://example.org")
Для использования HTTP Basic Auth (аутентификации) со своим прокси, используется синтаксис http://user:password@host/
:
proxies = {
"http": "http://user:pass@10.10.1.10:3128/",
}
SOCKS
Новое в версии 2.10.0
В дополнение к базовым прокси HTTP Requests также поддерживает прокси с помощью протокола SOCKS. Это опциональная функция, требующая дополнительных библиотек. Их можно получить с помощью pip
:
$ pip install requests[socks]
После установки использовать прокси SOCKS так же просто, как и HTTP:
proxies = {
'http': 'socks5://user:pass@host:port',
'https': 'socks5://user:pass@host:port'
}
При использовании socks5
разрешение DNS работает на стороне клиента, а не на стороне прокси-сервера. Это работает в соответствии с curl, который использует схему, чтобы определять, на чьей стороне разрешать DNS. Если необходимо заниматься преобразованием на стороне прокси-сервера, тогда используется socks5h
.
Соответствие стандартам
Requests соответствует всем актуальным спецификациям и RFC (технические стандарты, применяемые в сети) там, где подобное соответствие не создает трудностей для пользователей. Такое внимание к спецификациям может привести к необычному поведению, которое покажется необычным для тех, кто не знаком с ними.
Кодировки
Когда вы получаете ответ, Requests предполагает, какую кодировку использовать для декодирования во время вызова метода Response.text
. Библиотека сначала проверит кодировку в заголовке HTTP, и если там ничего не указано, воспользуется charade
, чтобы попробовать угадать.
Она не будет вести себя подобным образом только в одном случае — если кодировка не указано явно, а значение Content-Type
— text
. В таком случае, согласно RFC 2616, кодировка по умолчанию — ISO-8859-1
. Библиотека следует этому правилу. Если вам требуется другая кодировка, вы можете вручную настроить свойство Response. encoding
или использовать сырой Response.content
.
Методы HTTP
Requests предоставляет доступ ко всем методам HTTP: GET, OPTIONS, HEAD, POST, PUT, PATCH, DELETE
. Далее будут детальные примеры того, как их использовать с GitHub API.
Начнем с самого популярного метода — GET
. GET
— это идемпотентный метод, который возвращает ресурс по заданному URL. Он используется для получения данных из определенного места. Пример — попытка получить информацию об определенном коммите из GitHub. Пусть будет коммит a050faf
. Это будет выглядеть вот так:
>>> import requests
>>> r = requests.get('https://api.github.com/repos/kennethreitz/requests/git/commits/a050faf084662f3a352dd1a941f2c7c9f886d4ad')
Нужно подтвердить, что GitHub ответил правильно. Если да — необходимо определить тип контента. Это делается следующим образом:
>>> if (r.status_code == requests.codes.ok):
... print(r.headers['content-type'])
...
application/json; charset=utf-8
Итак, GitHub возвращает JSON
. Можно использовать метод r.json
для парсинга его в объекты Python.
>>> commit_data = r.json()
>>> print(commit_data.keys())
['committer', 'author', 'url', 'tree', 'sha', 'parents', 'message']
>>> print(commit_data['committer'])
{'date': '2012-05-10T11:10:50-07:00', 'email': 'me@kennethreitz.com', 'name': 'Kenneth Reitz'}
>>> print(commit_data['message'])
makin' history
Пока что все просто. Но посмотрим, что еще есть в API GitHub. Можно просто почитать документацию, но еще интереснее, если просто поэкспериментировать с Requests. Используем метод OPTIONS
, чтобы увидеть какие еще методы HTTP поддерживаются для этого ресурса.
>>> verbs = requests.options(r.url)
>>> verbs.status_code
500
Оказывается, что у GitHub, как и у многих API, не реализован метод OPTIONS
. Так что придется все-таки использовать документацию. Но если бы метод OPTION
был реализован, он вернул бы примерно следующее.
>>> verbs = requests.options('http://a-good-website.com/api/cats')
>>> print(verbs.headers['allow'])
GET,HEAD,POST,OPTIONS
В документации указано, что единственные разрешенные методы для коммитов — POST
. Они создают новые коммиты. Но поскольку используется репозиторий Requests, лучше не делать туда бесполезные POST
. Вместо этого можно поиграть с функцией Issues.
Эта документация была добавлена в ответ на “Issue #482”. Возьмем ее в качестве примера.
>>> r = requests.get('https://api.github.com/repos/kennethreitz/requests/issues/482')
>>> r.status_code
200
>>> issue = json.loads(r.text)
>>> print(issue['title'])
Feature any http verb in docs
>>> print(issue['comments'])
3
Есть три комментария. Рассмотрим последний из них.
>>> r = requests.get(r.url + '/comments')
>>> r.status_code
200
>>> comments = r.json()
>>> print(comments[0].keys())
['body', 'url', 'created_at', 'updated_at', 'user', 'id']
>>> print(comments[2]['body'])
Probably in the "advanced" section
Можем сообщить автору, что он не прав. Но сперва узнаем, кто это.
>>> print(comments[2]['user']['login'])
kennethreitz
Теперь скажем этому kennethreitz
, что ему лучше отправляться в раздел для начинающих. Согласно документации API GitHub это делается с помощью метода POST.
>>> body = json.dumps({"body": "Sounds great! I'll get right on it!"})
>>> url = "https://api.github.com/repos/kennethreitz/requests/issues/482/comments"
>>> r = requests.post(url=url, data=body)
>>> r.status_code
404
Похоже, нужно авторизоваться. В Requests можно выполнить любой вид аутентификации, включая базовую.
>>> from requests.auth import HTTPBasicAuth
>>> auth = HTTPBasicAuth('fake@example.com', 'not_a_real_password')
>>> r = requests.post(url=url, data=body, auth=auth)
>>> r.status_code
201
>>> content = r.json()
>>> print(content['body'])
Sounds great! I'll get right on it.
Теперь попробуем отредактировать сообщение. Для этого нужен метод PATCH
.
>>> print(content["id"])
5804413
>>> body = json.dumps({"body": "Sounds great! I'll get right on it once I feed my cat."})
>>> url = "https://api.github.com/repos/kennethreitz/requests/issues/comments/5804413"
>>> r = requests.patch(url=url, data=body, auth=auth)
>>> r.status_code
200
С баловством покончено. Используем DELETE
для удаления сообщения.
>>> r = requests.delete(url=url, auth=auth)
>>> r.status_code
204
>>> r.headers['status']
'204 No Content'
Напоследок можно посмотреть, как много запросов было использовано. Для этого нужно сделать запрос HEAD
к заголовкам и не скачивать целую страницу.
>>> r = requests.head(url=url, auth=auth)
>>> print(r.headers)
...
'x-ratelimit-remaining': '4995'
'x-ratelimit-limit': '5000'
...
Осталось написать программу на Python, которая бы использовала остальные 4995 запросов.
Заголовки Link
Многие API используют заголовки Link
. Они делают API более описательными и простыми в использования. GitHub используют пагинацию в своем API, например:
>>> url = 'https://api.github.com/users/kennethreitz/repos?page=1&per_page=10'
>>> r = requests.head(url=url)
>>> r.headers['link']
<https://api.github.com/users/kennethreitz/repos?page=2&per_page=10>; rel="next", <https://api.github.com/users/kennethreitz/repos?page=6&per_page=10>; rel="last"
Requests автоматически парсит эти ссылки и позволяет с легкостью их использовать.
>>> r.links["next"]
{'url': 'https://api.github.com/users/kennethreitz/repos?page=2&per_page=10', 'rel': 'next'}
>>> r.links["last"]
{'url': 'https://api.github.com/users/kennethreitz/repos?page=7&per_page=10', 'rel': 'last'}
Пользовательские HTTP-методы
Иногда вы будете работать с сервером, который по какой-то причине требует использовать методы HTTP за исключение базовых. Например, метод MKCOL, который используют сервера WEBDAV. Однако с ними также можно работать в Requests. В данном случае используется встроенный метод .request
. Например:
>>> r = requests.request('MKCOL', url, data=data)
>>> r.status_code
200 # если вызов был правильный
Таким образом можно использовать любой метод, разрешенный сервером.
Transport Adapters
Начиная с версии v1.0.0, Requests использует внутренний модульный дизайн. Одна из причин — внедрение Transport Adapters. Они предоставляют средство для определения методов взаимодействия с HTTP. В частности, позволяют применять настройку для каждого сервиса по отдельности.
Requests поставляются с одним таким Transport Adapter — HTTPAdapter
. Он предоставляет возможность взаимодействия с HTTP и HTTPS посредством библиотеки urllib3
из Requests по умолчанию. При инициализации Session
один из них «крепится» к объекту Session
HTTP, а второй — к HTTPS.
Requests дают возможность пользователям создавать и использовать собственные Transport Adapters с конкретной функциональностью. После создания Transport Adapter может быть прикреплен к объему Session вместе с указанием сервисов, к которым он должен применяться.
>>> s = requests.Session()
>>> s.mount('https://github.com/', MyAdapter())
Вызов mount
регистрирует экземпляр Transport Adapter в префиксе. После этого HTTP-запросы, сделанные с помощью этого Session
и URL которых начинается с этого префикса, будут использовать указанный Transport Adapter.
Многие подробности использования Transport Adapter лежат за рамками этого материала, но вы сможете разобраться лучше на следующем примере.
Пример: конкретная версия SSL
Разработчики Requests заранее определили, какая версия SSL будет использоваться по умолчанию в urllib3
. Обычно это работает так, как нужно, но иногда требуется подключиться к конечной точке, которая использует версию, не совместимую с той, что указана по умолчанию.
В этом случае можно задействовать Transport Adapter, используя большую часть существующей реализации HTTPAdapter
и добавив параметр ssl_version
, который передается через urllib3
. Настроим Transport Adapter, чтобы библиотека использовала SSLv3
:
from urllib3.poolmanager import PoolManager
from requests.adapters import HTTPAdapter
class Ssl3HttpAdapter(HTTPAdapter):
""""Transport adapter" который позволяет использовать SSLv3."""
def init_poolmanager(self, connections, maxsize, block=False):
self.poolmanager = PoolManager(
num_pools=connections, maxsize=maxsize,
block=block, ssl_version=ssl.PROTOCOL_SSLv3)
Блокирующий или не-блокирующий
С Transport Adapter по умолчанию Requests не предоставляет никакого не-блокирующего IO (ввода-вывода). Свойство Response.content
будет блокировать до тех пор, пока весь ответ не загрузится. Если требуется большая детализация, потоковые возможности библиотеки позволяют получать маленькие порции ответа в определенное время. Но и эти вызовы будут блокироваться.
Если не хочется использовать блокировку IO, есть масса проектов, совмещающих Requests с одним из асинхронных фреймворков Python. Например, requests-threads, grequests, requests-futures и requests-async.
Порядок заголовков
В необычных обстоятельствах может понадобится предоставить заголовки в определенном порядке. Если передать OrderedDict
в headers
, это и будет обозначенный порядок. Но порядок заголовков по умолчанию в Requests будет иметь более высокий приоритет, поэтому если их перезаписать в headers
, они могут отображаться беспорядочно в сравнении с теми, что указаны в аргументе.
Чтобы решить эту проблему, необходимо настроить заголовки по умолчанию для объекта Session
, предоставив ему OrderedDict
. Этот порядок и станет приоритетным.
Таймауты
У большинства запросов к внешнем серверам есть прикрепленный таймаут в том случае, если сервер не отвечает вовремя. По умолчанию запросы не прерываются, только если время не указано явно. Без таймаута код может висеть по несколько минут.
connect
— это количество секунд, которые Requests будет выжидать для настройки соединения с вызовом удаленной машины (соответствующей connect()
) в сокете. Хорошей практикой считается настраивать это время чуть больше значения кратного 3, что является стандартным окном ретрансляции пакета TCP.
Когда клиент подключился к серверу и отправил HTTP-запрос, таймаут read
— это количество секунд, которые клиент будет ждать ответа от сервера. (Если точнее, это то количество секунд, которое клиент прождет между отправкой байтов с сервера. В 99,9% случаев оно меньше того времени с момента, когда сервер отправляет первый байт).
Если определить одно значение для таймаута, вот так:
r = requests.get('https://github.com', timeout=5)
Оно будет применено к таймауту connect
и read
. Если нужны отдельные значения, стоит определить их в кортеже:
r = requests.get('https://github.com', timeout=(3.05, 27))
Если сервер очень медленный, можно указать Requests, чтобы он ждал вечно, передав значение None
.
r = requests.get('https://github.com', timeout=None)