Продвинутое руководство по библиотеке Python Requests

4772

В этом материале описаны продвинутые функции библиотеки 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/Монти_Пайтон')
Python data course

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

>>> 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-сертификатов, если значение verifyFalse.

>>> requests.get('https://kennethreitz.com', verify=False)
<Response [200]>

По умолчанию значение verifyTrue. Параметр подходит только для сертификатов хостов.

Можно также определить файл локального сертификата в виде пути или пары ключ-значение:

>>> 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-Typetext. В таком случае, согласно 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 запросов.

Многие 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)

Учитесь программировать по книгам, подписывайте на телеграм каналы:

Книги Python разработчика RU / EN

Книги / Data Science / RU-EN

Тест на знание python

Что выведет этот код?
Как в Python называется встроенная библиотека для использования регулярных выражений?
Что выведет этот код?
Какой код выведет строку — "C:\Common\testString.doc" ?
Какое значение вернет код colors[2] ?
Александр
Я создал этот блог в 2018 году, чтобы распространять полезные учебные материалы, документации и уроки на русском. На сайте опубликовано множество статей по основам python и библиотекам, уроков для начинающих и примеров написания программ. Пишу на популярные темы: веб-разработка, работа с базами данных, data sciense и другие...