Быстрый старт¶
Хотите начать? Данная страница даёт хорошее представление о том, как начать работу с Requests.
Во-первых, убедитесь в том, что:
Requests установлен
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
.
Готовы к большему? Ознакомьтесь с продвинутым разделом.