Быстрый старт

Хотите начать? Данная страница даёт хорошее представление о том, как начать работу с Requests.

Во-первых, убедитесь в том, что:

Начнём с нескольких простых примеров.

Выполнить запрос

Сделать запрос с помощью Requests очень просто.

Начните с импорта модуля Requests:

>>> import requests

Теперь давайте попробуем получить веб-страницу. Для этого примера возьмём общедоступную таймлайн GitHub:

>>> r = requests.get('https://api.github.com/events')

Теперь у нас есть объект Response с именем r. Мы можем получить всю необходимую информацию из этого объекта.

Простой API Requests означает, что все формы HTTP-запросов столь же очевидны. Например, так вы делаете запрос HTTP POST:

>>> r = requests.post('https://httpbin.org/post', data = {'key':'value'})

Красиво, правда? А как насчёт других типов HTTP-запросов: PUT, DELETE, HEAD и OPTIONS? Всё это так же просто:

>>> r = requests.put('https://httpbin.org/put', data = {'key':'value'})
>>> r = requests.delete('https://httpbin.org/delete')
>>> r = requests.head('https://httpbin.org/get')
>>> r = requests.options('https://httpbin.org/get')

Это все хорошо, но это только начало того, на что способен Requests.

Передача параметров в URL-адресах

Вы часто хотите отправить какие-то данные в строке запроса URL-адреса. Если бы вы создавали URL-адрес вручную, эти данные были бы представлены в виде пар ключ/значение в URL-адресе после вопросительного знака, например httpbin.org/get?key=val. Requests позволяет вам предоставить эти аргументы в виде словаря строк, используя ключевой аргумент params. Например, если вы хотите передать key1=value1 и key2=value2 в httpbin.org/get, вы должны использовать следующий код:

>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> r = requests.get('https://httpbin.org/get', params=payload)

Вы можете увидеть, что URL-адрес был правильно закодирован, распечатав URL- адрес:

>>> print(r.url)
https://httpbin.org/get?key2=value2&key1=value1

Обратите внимание, что любой ключ словаря со значением None не будет добавлен в строку запроса URL-адреса.

Вы также можете передать список элементов в качестве значения:

>>> payload = {'key1': 'value1', 'key2': ['value2', 'value3']}

>>> r = requests.get('https://httpbin.org/get', params=payload)
>>> print(r.url)
https://httpbin.org/get?key1=value1&key2=value2&key2=value3

Содержание ответа

Мы можем прочитать содержимое ответа сервера. Снова рассмотрим таймлайн GitHub:

>>> import requests

>>> r = requests.get('https://api.github.com/events')
>>> r.text
'[{"repository":{"open_issues":0,"url":"https://github.com/...

Requests автоматически декодирует контент с сервера. Большинство Юникод кодировок легко декодируются.

Когда вы делаете запрос, Requests делает обоснованные предположения о кодировке ответа на основе заголовков HTTP. Кодировка текста, предполагаемая Requests, используется при доступе к r.text. Вы можете узнать, какую кодировку использует Requests, и изменить её, используя свойство r.encoding:

>>> r.encoding
'utf-8'
>>> r.encoding = 'ISO-8859-1'

Если вы измените кодировку, Requests будет использовать новое значение r.encoding при каждом вызове r.text. Возможно, вы захотите сделать это в любой ситуации, когда вы можете применить специальную логику для определения того, какой будет кодировка контента. Например, HTML и XML могут указывать свою кодировку в своем body. В подобных ситуациях следует использовать r.content, чтобы найти кодировку, а затем установить r.encoding. Это позволит вам использовать r.text с правильной кодировкой.

Requests также будет использовать пользовательские кодировки, если они вам понадобятся. Если вы создали свою собственную кодировку и зарегистрировали её в модуле codecs, вы можете просто использовать имя кодека в качестве значения r.encoding, и Requests выполнит декодирование за вас.

Контент двоичного ответа

Вы также можете получить доступ к телу ответа в байтах для нетекстового запроса:

>>> r.content
b'[{"repository":{"open_issues":0,"url":"https://github.com/...

Кодировки передачи gzip и deflate автоматически декодируются для вас.

Кодирование передачи br автоматически декодируется для вас, если установлена библиотека Brotli, например brotli или brotlicffi.

Например, чтобы создать изображение из двоичных данных, возвращаемых запросом, вы можете использовать следующий код:

>>> from PIL import Image
>>> from io import BytesIO

>>> i = Image.open(BytesIO(r.content))

Содержимое JSON ответа

Также есть встроенный декодер JSON, если вы имеете дело с JSON данными:

>>> import requests

>>> r = requests.get('https://api.github.com/events')
>>> r.json()
[{'repository': {'open_issues': 0, 'url': 'https://github.com/...

В случае сбоя декодирования JSON r.json() вызывает исключение. Например, если ответ получает 204 (нет содержимого) или если ответ содержит недопустимый JSON, попытка r.json() вызывает simplejson.JSONDecodeError, если установлен simplejson, или ValueError: No JSON object could be decoded для Python 2 или json.JSONDecodeError для Python 3.

Следует отметить, что успешный вызов r.json() не означает успешность ответа. Некоторые серверы могут возвращать объект JSON в неудачном ответе (например, сведения об ошибке с HTTP 500). Такой JSON будет декодирован и возвращён. Чтобы проверить успешность запроса, использовать r.raise_for_status() или проверить, что r.status_code соответствует вашим ожиданиям.

Необработанный контент ответа

В том редком случае, когда вы хотите получить необработанный сокетный ответ от сервера, вы можете получить доступ к r.raw. Если вы хотите это сделать, убедитесь, что вы установили stream=True в своём первоначальном запросе. Как только вы это сделаете, вы сможете сделать:

>>> r = requests.get('https://api.github.com/events', stream=True)

>>> r.raw
<urllib3.response.HTTPResponse object at 0x101194810>

>>> r.raw.read(10)
'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03'

В общем, однако, вы должны использовать подобный шаблон для сохранения потоковой передачи в файл:

with open(filename, 'wb') as fd:
    for chunk in r.iter_content(chunk_size=128):
        fd.write(chunk)

Использование Response.iter_content позволит решить большую часть того, с чем вам в противном случае пришлось бы справиться при прямом использовании Response.raw. При потоковой передаче загрузки описанный выше способ является предпочтительным и рекомендуемым для получения содержимого. Обратите внимание, что chunk_size можно свободно изменить до числа, которое может лучше соответствовать вашим вариантам использования.

Примечание

Важное замечание об использовании Response.iter_content по сравнению с Response.raw. Response.iter_content автоматически декодирует кодировки передачи gzip и deflate. Response.raw — это необработанный поток байтов — он не преобразует содержимое ответа. Если вам действительно нужен доступ к байтам в том виде, в каком они были возвращены, используйте Response.raw.

Пользовательские заголовки

Если вы хотите добавить в запрос HTTP заголовки, просто передайте dict параметру headers.

Например, в предыдущем примере мы не указали свой user-agent:

>>> url = 'https://api.github.com/some/endpoint'
>>> headers = {'user-agent': 'my-app/0.0.1'}

>>> r = requests.get(url, headers=headers)

Примечание. Пользовательские заголовки имеют меньший приоритет, чем более конкретные источники информации. Например:

  • Заголовки авторизации, установленные с помощью headers =, будут переопределены, если учётные данные указаны в .netrc, который, в свою очередь, будет переопределён параметром auth=. Requests будет искать файл netrc в ~/.netrc,` ~/_netrc` или по пути, указанному в переменной среды NETRC.

  • Заголовки авторизации будут удалены, если вы будете перенаправлены за пределы хоста.

  • Заголовки Proxy-Authorization будут переопределены учётными данными прокси, указанными в URL-адресе.

  • Заголовки Content-Length будут переопределены, когда мы сможем определить длину содержимого.

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

Примечание. Все значения заголовка должны быть string, байтовой строкой или юникодом. Хотя это разрешено, рекомендуется избегать передачи значений заголовка в юникоде.

Более сложный POST запросы

Как правило, вы хотите отправить некоторые закодированные в форме данные — очень похоже на HTML-форму. Для этого просто передайте словарь в аргумент data. Ваш словарь данных будет автоматически закодирован, когда будет сделан запрос:

>>> payload = {'key1': 'value1', 'key2': 'value2'}

>>> r = requests.post("https://httpbin.org/post", data=payload)
>>> print(r.text)
{
  ...
  "form": {
    "key2": "value2",
    "key1": "value1"
  },
  ...
}

Аргумент data также может иметь несколько значений для каждого ключа. Это можно сделать, сделав data либо списком кортежей, либо словарем со списками в качестве значений. Это особенно полезно, когда в форме есть несколько элементов, использующих один и тот же ключ:

>>> payload_tuples = [('key1', 'value1'), ('key1', 'value2')]
>>> r1 = requests.post('https://httpbin.org/post', data=payload_tuples)
>>> payload_dict = {'key1': ['value1', 'value2']}
>>> r2 = requests.post('https://httpbin.org/post', data=payload_dict)
>>> print(r1.text)
{
  ...
  "form": {
    "key1": [
      "value1",
      "value2"
    ]
  },
  ...
}
>>> r1.text == r2.text
True

Бывают случаи, когда вам может потребоваться отправить данные, не закодированные в форме. Если вы передадите string вместо dict, эти данные будут опубликованы напрямую.

Например, GitHub API v3 принимает данные POST/PATCH в кодировке JSON:

>>> import json

>>> url = 'https://api.github.com/some/endpoint'
>>> payload = {'some': 'data'}

>>> r = requests.post(url, data=json.dumps(payload))

Вместо того, чтобы кодировать dict самостоятельно, вы также можете передать его напрямую, используя параметр json (добавлен в версии 2.4.2), и он будет закодирован автоматически:

>>> url = 'https://api.github.com/some/endpoint'
>>> payload = {'some': 'data'}

>>> r = requests.post(url, json=payload)

Обратите внимание, что параметр json игнорируется, если передан data или files.

Использование параметра json в запросе изменит Content-Type в заголовке на application/json.

POST файл с многократным кодированием

Requests упрощает загрузку файлов с кодировкой Multipart:

>>> url = 'https://httpbin.org/post'
>>> files = {'file': open('report.xls', 'rb')}

>>> r = requests.post(url, files=files)
>>> r.text
{
  ...
  "files": {
    "file": "<censored...binary...data>"
  },
  ...
}

Вы можете явно указать имя файла, content_type и заголовки:

>>> url = 'https://httpbin.org/post'
>>> files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})}

>>> r = requests.post(url, files=files)
>>> r.text
{
  ...
  "files": {
    "file": "<censored...binary...data>"
  },
  ...
}

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

>>> url = 'https://httpbin.org/post'
>>> files = {'file': ('report.csv', 'some,data,to,send\nanother,row,to,send\n')}

>>> r = requests.post(url, files=files)
>>> r.text
{
  ...
  "files": {
    "file": "some,data,to,send\\nanother,row,to,send\\n"
  },
  ...
}

Если вы отправляете очень большой файл как запрос multipart/form-data, вы можете захотеть передать запрос в потоковом режиме. По умолчанию requests не поддерживает это, но есть отдельный пакет, который поддерживает — requests-toolbelt. Вы должны прочитать `документацию по набору инструментов toolbelt для получения более подробной информации о том, как его использовать.

Информацию об отправке нескольких файлов в одном запросе см. в продвинутом разделе.

Предупреждение

Настоятельно рекомендуется открывать файлы в двоичном режиме. Это связано с тем, что Requests может попытаться заполнить заголовок Content-Length, и если это произойдет, это значение будет установлено на количество байтов в файле. Ошибки могут возникнуть, если вы откроете файл в текстовом режиме.

Коды состояния ответа

Мы можем проверить код статуса ответа:

>>> r = requests.get('https://httpbin.org/get')
>>> r.status_code
200

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

>>> r.status_code == requests.codes.ok
True

Если мы сделали неверный запрос (ошибка клиента 4XX или ответ с ошибкой сервера 5XX), мы можем поднять его с помощью Response.raise_for_status():

>>> bad_r = requests.get('https://httpbin.org/status/404')
>>> bad_r.status_code
404

>>> bad_r.raise_for_status()
Traceback (most recent call last):
  File "requests/models.py", line 832, in raise_for_status
    raise http_error
requests.exceptions.HTTPError: 404 Client Error

Но поскольку наш status_code для r был 200, когда мы вызываем raise_for_status(), мы получаем:

>>> r.raise_for_status()
None

Всё хорошо.

Заголовки ответов

Мы можем просмотреть заголовки ответов сервера, используя словарь Python:

>>> r.headers
{
    'content-encoding': 'gzip',
    'transfer-encoding': 'chunked',
    'connection': 'close',
    'server': 'nginx/1.0.4',
    'x-runtime': '148ms',
    'etag': '"e1ca502697e5c9317743dc078f67693f"',
    'content-type': 'application/json'
}

Однако словарь особенный: он предназначен только для HTTP заголовков. Согласно RFC 7230, имена заголовков HTTP не чувствительны к регистру.

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

>>> r.headers['Content-Type']
'application/json'

>>> r.headers.get('content-type')
'application/json'

Это также особенность в том, что сервер мог отправлять один и тот же заголовок несколько раз с разными значениями, но requests объединяет их, чтобы они могли быть представлены в словаре в рамках одного сопоставления, согласно RFC 7230:

Получатель МОЖЕТ объединить несколько полей заголовка с одинаковым именем поля в одну пару «имя-поля: значение-поле» без изменения семантики сообщения, добавляя каждое последующее значение поля к объединенному значению поля по порядку, разделенное знаком запятая.

Перенаправление и история

По умолчанию Requests выполняет перенаправление для всех глаголов, кроме HEAD.

Мы можем использовать свойство history объекта Response для отслеживания перенаправления.

Список Response.history содержит объекты Response, которые были созданы для выполнения запроса. Список отсортирован от самого старого до самого последнего ответа.

Например, GitHub перенаправляет весь HTTP requests на HTTPS:

>>> r = requests.get('http://github.com/')

>>> r.url
'https://github.com/'

>>> r.status_code
200

>>> r.history
[<Response [301]>]

Если вы используете GET, OPTIONS, POST, PUT, PATCH или DELETE, вы можете отключить обработку перенаправления с помощью параметра allow_redirects:

>>> r = requests.get('http://github.com/', allow_redirects=False)

>>> r.status_code
301

>>> r.history
[]

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

>>> r = requests.head('http://github.com/', allow_redirects=True)

>>> r.url
'https://github.com/'

>>> r.history
[<Response [301]>]

Таймауты

Вы можете указать Requests прекратить ожидание ответа через заданное количество секунд с помощью параметра timeout. Почти весь производственный код должен использовать этот параметр почти во всех requests. В противном случае ваша программа может зависнуть на неопределенное время:

>>> requests.get('https://github.com/', timeout=0.001)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
requests.exceptions.Timeout: HTTPConnectionPool(host='github.com', port=80): Request timed out. (timeout=0.001)

Примечание

timeout не является ограничением по времени для загрузки всего ответа; скорее, возникает исключение, если сервер не отправил ответ в течение timeout секунды (точнее, если не было получено байтов на базовый сокет в течение timeout секунд). Если тайм-аут не указан явно, таймаут requests не истечёт.

Ошибки и исключения

В случае сетевой проблемы (например, сбой DNS, отказ в подключении и т. д.) Requests вызовет исключение ConnectionError.

Response.raise_for_status() вызовет HTTPError, если HTTP-запрос вернул неудачный код состояния.

Если время ожидания запроса истекло, возникает исключение Timeout.

Если запрос превышает настроенное максимальное количество перенаправлений, возникает исключение TooManyRedirects.

Все исключения, которые явно вызывает Requests, наследуются от requests.exceptions.RequestException.

Готовы к большему? Ознакомьтесь с продвинутым разделом.