- Формируем запрос
- Передача параметров в URL
- Содержимое ответа
- Содержимое ответа сервера в бинарном виде
- Содержимое ответа сервера в формате JSON
- Необработанное Raw содержимое ответа
- Пользовательские заголовки запроса
- Более сложные запросы POST
- Передача POST multipart-Encoded файлов (бинарных данных)
- Коды статуса ответа
- Заголовки ответа
- Cookies
- Перенаправление и история
- Таймауты
- Ошибки и исключения
И так, вы хотите побыстрее приступить? Эта страница даст вам наглядное представление о том, как начать работать с библиотекой Requests.
Перед началом работы необходимо убедитесь в следующем:
- библиотека Requests установлена
- установленная версия библиотеки актуальна
Начнем с нескольких простых примеров, после рассмотрения которых вам станет понятен принцип использования Requests для решения ваших задач.
Формируем запрос
Сформировать запрос с помощью библиотеки Requests очень просто.
Начнем с импорта модуля Requests:
>>> import requests
Теперь давайте попробуем получить содержимое веб-страницы. В примере кода ниже получим данные с страницы общедоступной ленты новостей GitHub:
>>> r = requests.get('https://api.github.com/events')
В этом примере кода мы создали объект r
класса Response
. И затем с его помощью получили всю необходимую нам информацию (содержимое страницы), но при условии того, что сформированный нами запрос корректен.
Простой API Requests позволяет интуитивно понятно формировать все возможные формы 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?key1=val1&key2=val2
.
Requests позволяет скомпоновать нужные вам данные с помощью словаря, в котором ключи и значения являются соответствующими парами строк, а затем передать его в метод, формирующий запрос, используя аргумент params
. Так например, если вы хотите передать следующие параметры запроса key1=value1
и key2=value2
при обращении к httpbin.org/get
, вы можете использовать следующий код:
>>> payload = {'key1': 'value1', 'key2': 'value2'} >>> r = requests.get('https://httpbin.org/get', params=payload)
Проверить то, что полученный 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
Содержимое ответа
Используя библиотеку Requests мы можем получать и просматривать содержимое ответа сервера. Рассмотрим снова пример получения данных ленты новостей GitHub:
>>> import requests >>> r = requests.get('https://api.github.com/events') >>> r.text u'[{"repository":{"open_issues":0,"url":"https://github.com/...
Requests автоматически декодирует контент, полученный от сервера. При этом большинство Unicode кодировок библиотека корректно декодирует автоматически. Когда вы посылаете запрос, Requests по-умолчанию самостоятельно делает предположение о типе кодировки содержимого ответа на основе заголовков HTTP, принятых с ответом сервера. Затем тип, определенной Requests, текстовой кодировки используется для доступа и обработки непосредственного содержимого ответа, например, с использованием метода r.text
. Для того, чтобы определить какую кодировку Requests использует при декодировании содержимого ответа, а также для её изменения, используется свойство текущего экземпляра объекта запроса r.encoding
:
>>> r.encoding 'utf-8' >>> r.encoding = 'ISO-8859-1'
Если вы указанным выше способом измените тип кодировки, то далее Requests будет использовать его, как новое значение свойства текущего экземпляра r.encoding
, а, следовательно, и далее при каждом вызове метода r.text
и др. Возможно, вы воспользуетесь этой опцией в том случае, если захотите использовать другую более специфическую (пользовательскую) логику для определения кодировки содержимого ответа сервера.
Так например, в языках разметки HTML и XML имеется возможность указать тип кодировки контента непосредственно в теле документа с использованием соответствующего атрибута. В этом случае вы можете использовать значение свойства r.content
для того, чтобы определить тип кодировки полученной страницы, а затем для корректной работы установить соответствующее значение свойства r.encoding
. Этот прием позволит вам использовать метод r.text
для получения содержимого ответа уже с корректным типом кодировки.
Если вам необходимо, то Requests позволяет использовать пользовательские виды кодировок текстовых данных содержимого ответа. Например, если вы создали свою собственную кодировку символов и соответствующим образом зарегистрировали ее в текущей среде выполнения с помощью модуля codecs
. То далее вы можете просто использовать наименование вашего кодировщика в качестве значения свойства r.encoding
, и содержимое ответа сервера будет автоматически обработано с заданной вами логикой декодирования.
Содержимое ответа сервера в бинарном виде
Используя Requests можно получить доступ к содержимому ответа сервера как к набору байтов для нетекстовых (бинарных) запросов данных:
>>> r.content b'[{"repository":{"open_issues":0,"url":"https://github.com/...
Полученные таким образом данные ответа, сжатые с использованием алгоритмов gzip
или deflate
, будут автоматически корректно декодированы.
Следующий пример кода позволяет восстановить изображение из бинарных данных, полученных из содержимого запроса:
>>> from PIL import Image >>> from io import BytesIO >>> i = Image.open(BytesIO(r.content))
Содержимое ответа сервера в формате JSON
В Requests имеется встроенный JSON-декодер, на случай, если вы имеете дело с данными в формате JSON:
>>> import requests >>> r = requests.get('https://api.github.com/events') >>> r.json() [{u'repository': {u'open_issues': 0, u'url': 'https://github.com/...
В случае неудачи при декодировании содержимого JSON метод r.json()
возбуждает исключение соответствующего типа. Таким образом, если ответ на ваш запрос содержит код состояния 204 (нет содержимого), или же содержит не корректный JSON, вызов метода r.json()
приведет к возбуждению исключения типа: ValueError: No JSON object could be decoded
(невозможно декодировать объект JSON).
Следует отметить, что успешность вызова метода r.json()
не означает успешного получения ответа сервера. Некоторые серверы могут возвращать объект JSON, в котором содержатся параметры сервера (запроса), которые привели к возникновению ошибки с кодом 500 (внутренняя ошибка сервера). То есть полученный в виде JSON ответ будет корректно декодирован и возвращен соответствующим методом нашего объекта запроса без возбуждения исключений, но по сути запрос не был корректно обработан сервером. В этом случае для того, чтобы убедиться в успешности запроса, используется метод r.raise_for_status()
, который проверяет содержимое свойства r.status_code
.
Необработанное Raw содержимое ответа
В тех редких случаях, когда вы захотите получить необработанные raw данные, например, от определенного сокета сервера. То вы можете получить доступ к его содержимому, используя метод r.raw
(получаете файлоподобный объект). Кроме того необходимо убедится, что вы установили соответствующее значение аргумента stream=True
при формировании запроса к серверу.
Таким образом, если вы хотите получить ответ сервера в виде необработанных raw данных, вы можете использовать код следующего вида:
>>> r = requests.get('https://api.github.com/events', stream=True) >>> r.raw>>> 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)
Метод r.iter_content()
позволит обработать данные полученные с использованием метода r.raw
в соответствии с вашими предпочтениями более удобным способом. В противном случае вы можете обработать данные из r.raw
любым другим удобным вам способом. Обратите внимание, что при потоковой загрузке необработанных данных (raw) использование метода Response.iter_content()
является предпочтительным, и является наиболее рекомендуемым способом обработки содержимого ответа. Присваивание соответствующих значений аргументу 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=
. - Заголовки авторизации также будут удалены, если вы будете перенаправлены по другому адресу вне хоста куда был отправлен ваш запрос.
- Заголовки прокси-авторизации будут переопределены учетными данными прокси, указанными в URL.
- Заголовки Content-Length будут переопределены, если мы можем определить размер содержимого запроса.
В файле .netrc хранится информация об автоматическом входе в систему. Информация netrc — это необязательная информация, которая задает данные об идентификации. Пользователь, у которого нет разрешения на доступ к серверу баз данных или который не работает на доверенном компьютере сервера баз данных, может использовать этот файл, чтобы ввести имя и пароль доверенного пользователя. Эту информацию также может ввести пользователь, у которого на дистанционном компьютере есть другая учетная запись и пароль.
Кроме того, Requests не меняет своего поведения в зависимости от того, какие пользовательские заголовки указаны (не учитывает при конфигурировании). Заголовки просто передаются в окончательно сформированный запрос.
Примечание: Все значения полей заголовка должны быть следующих типов: string
, bytestring
или unicode
(строковый тип данных в Python 2.x). И хотя это не запрещено, рекомендуется избегать передачи значений полей заголовка в виде unicode
.
Более сложные запросы POST
Для того, чтобы отправить закодированные form-encoded данные так же как данные, отправляемые на сервер, из обычной HTML формы, просто передайте словарь dict
, содержащий эти данные в виде последовательности пар ключ-значение, в аргумент data
. Затем когда запрос будет формироваться, ваш словарь с данными будет автоматически form-encoded закодирован надлежащим образом:
>>> payload = {'key1': 'value1', 'key2': 'value2'} >>> r = requests.post("https://httpbin.org/post", data=payload) >>> print(r.text) { ... "form": { "key2": "value2", "key1": "value1" }, ... }
Аргумент data
может также содержать несколько вложенных значений для каждого ключа. Это можно реализовать, передав в data
либо список кортежей, либо словарь со списками в качестве значений его ключей. Этот прием можно использовать, если ваша форма имеет в своем составе несколько элементов, которые используют для передачи данных один и тот же ключ. Например, HTML элемент select, который предоставляет пользователю возможность выбора нескольких вариантов из списка.
>>> 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
В некоторых случаях если вы захотите отправить данные в незакодированном виде, то их необходимо передать аргументу data
как есть, то есть как строку 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), и его содержимое будет преобразован в JSON автоматически:
>>> 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 multipart-Encoded файлов (бинарных данных)
Библиотека Requests упрощает загрузку файлов по частям multipart-encoded:
>>> url = 'https://httpbin.org/post' >>> files = {'file': open('report.xls', 'rb')} >>> r = requests.post(url, files=files) >>> r.text { ... "files": { "file": "" }, ... }
Вы можете явно задать в запросе имя, передаваемого файла filename, 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": "" }, ... }
Если вам необходимо, то строки могут быть приняты сервером как файлы:
>>> 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
. Для ознакомления с возможностями этого пакета, а также особенностями его использования, вы можете обратиться к документации.
Как отправить нескольких файлов в одном запросе вы можете ознакомиться в продвинутом руководстве документации.
Предупреждение: Настоятельно рекомендуется открывать файлы в двоичном binary режиме. Это связано с тем, что когда 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
То есть, исключение соответствующего типа возбуждаться не будет.
Заголовки ответа
Мы можем просмотреть заголовки ответа сервера, получив их содержимое в виде словаря:
>>> 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 :
Получатель МОЖЕТ объединить несколько полей заголовка с одним и тем же именем поля в одну пару «field-name: field-value» без изменения семантики сообщения, добавляя каждое последующее значение поля в объединенное значение в порядке получения, разделяя их запятыми.
Cookies
Если ответ сервера содержит несколько файлов cookie, то вы можете легко получить к ним доступ:
>>> url = 'http://example.com/some/cookie/setting/url' >>> r = requests.get(url) >>> r.cookies['example_cookie_name'] 'example_cookie_value'
Для того, чтобы отправить свои собственные данные cookie на сервер, вы можете использовать при формировании вашего запроса параметр cookies
:
>>> url = 'https://httpbin.org/cookies' >>> cookies = dict(cookies_are='working') >>> r = requests.get(url, cookies=cookies) >>> r.text '{"cookies": {"cookies_are": "working"}}'
Содержимое данных cookie может быть получено с помощью объекта RequestsCookieJar
, с которым затем взаимодействуют как со словарем dict
, но он предоставляет более обширный интерфейс функций, подходящий для использования с несколькими доменами или путями. Принятые данные cookie также можно передавать в уже сформированные запросы:
>>> jar = requests.cookies.RequestsCookieJar() >>> jar.set('tasty_cookie', 'yum', domain='httpbin.org', path='/cookies') >>> jar.set('gross_cookie', 'blech', domain='httpbin.org', path='/elsewhere') >>> url = 'https://httpbin.org/cookies' >>> r = requests.get(url, cookies=jar) >>> r.text '{"cookies": {"tasty_cookie": "yum"}}'
Перенаправление и история
По умолчанию Requests позволяет осуществить перенаправление в другое местоположение для всех типов запросов, кроме HEAD.
Для отслеживания последовательности перенаправлений (редиректа) используется свойство history
объекта Response
. Response.history
содержит список всех объектов Response
, которые были созданы в порядке их получения. Список отсортирован от самого первого до последнего ответа ответа сервера в течение всего сеанса работы.
Например, GitHub перенаправляет все HTTP запросы на HTTPS:
>>> r = requests.get('http://github.com/') >>> r.url 'https://github.com/' >>> r.status_code 200 >>> r.history []
Если вы используете методы 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 []
Таймауты
При формировании запросов вы можете указать Requests прекратить ожидание ответа сервера через заданное количество секунд, с помощью аргумента timeout
. В последствии лучшей практикой является использование в вашем коде этого приема, установки значения аргумента timeout
, практически во всех формируемых запросах. Так как его отсутствие может привести к зависанию программы на неопределенный срок (до получения ответа сервера):
>>> requests.get('https://github.com/', timeout=0.001) Traceback (most recent call last): File "", line 1, in requests.exceptions.Timeout: HTTPConnectionPool(host='github.com', port=80): Request timed out. (timeout=0.001)
На заметку. Параметр timeout
в общем не служит для ограничения по времени загрузки ответа. Исключение возбуждается, если сервер не отвечает в течение секунд ожидания (точнее, если в базовом сокете не было получено ни одного байта в течение заданного числа секунд ожидания). Если значение параметра timeout
не задано, то отправление ваших запросов не прекратится.
Ошибки и исключения
В случае проблем с сетью (например, сбой DNS, отказ в соединении и т. д.) Requests будет возбуждать исключение типа ConnectionError
. Вызов метода Response.raise_for_status()
приведет к возникновению исключения типа HTTPError
, если HTTP запрос вернул неудачный код состояния.
Если время ожидания истекло, возбуждается исключение типа Timeout
.
Если запрос превышает настроенное количество максимальных перенаправлений, возбуждается исключение TooManyRedirects
.
Все исключения, которые явно возбуждаются Requests, наследуются от класса исключений request.exceptions.RequestException.
Готовы к большему? Ознакомьтесь с продвинутым руководством.