Eager to get started? This page gives a good introduction in how to get started
with Requests.
First, make sure that:
-
Requests is installed
-
Requests is up-to-date
Let’s get started with some simple examples.
Make a Request¶
Making a request with Requests is very simple.
Begin by importing the Requests module:
Now, let’s try to get a webpage. For this example, let’s get GitHub’s public
timeline:
>>> r = requests.get('https://api.github.com/events')
Now, we have a Response
object called r
. We can
get all the information we need from this object.
Requests’ simple API means that all forms of HTTP request are as obvious. For
example, this is how you make an HTTP POST request:
>>> r = requests.post('https://httpbin.org/post', data={'key': 'value'})
Nice, right? What about the other HTTP request types: PUT, DELETE, HEAD and
OPTIONS? These are all just as simple:
>>> 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')
That’s all well and good, but it’s also only the start of what Requests can
do.
Passing Parameters In URLs¶
You often want to send some sort of data in the URL’s query string. If
you were constructing the URL by hand, this data would be given as key/value
pairs in the URL after a question mark, e.g. httpbin.org/get?key=val
.
Requests allows you to provide these arguments as a dictionary of strings,
using the params
keyword argument. As an example, if you wanted to pass
key1=value1
and key2=value2
to httpbin.org/get
, you would use the
following code:
>>> payload = {'key1': 'value1', 'key2': 'value2'} >>> r = requests.get('https://httpbin.org/get', params=payload)
You can see that the URL has been correctly encoded by printing the URL:
>>> print(r.url) https://httpbin.org/get?key2=value2&key1=value1
Note that any dictionary key whose value is None
will not be added to the
URL’s query string.
You can also pass a list of items as a value:
>>> 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
Response Content¶
We can read the content of the server’s response. Consider the GitHub timeline
again:
>>> import requests >>> r = requests.get('https://api.github.com/events') >>> r.text '[{"repository":{"open_issues":0,"url":"https://github.com/...
Requests will automatically decode content from the server. Most unicode
charsets are seamlessly decoded.
When you make a request, Requests makes educated guesses about the encoding of
the response based on the HTTP headers. The text encoding guessed by Requests
is used when you access r.text
. You can find out what encoding Requests is
using, and change it, using the r.encoding
property:
>>> r.encoding 'utf-8' >>> r.encoding = 'ISO-8859-1'
If you change the encoding, Requests will use the new value of r.encoding
whenever you call r.text
. You might want to do this in any situation where
you can apply special logic to work out what the encoding of the content will
be. For example, HTML and XML have the ability to specify their encoding in
their body. In situations like this, you should use r.content
to find the
encoding, and then set r.encoding
. This will let you use r.text
with
the correct encoding.
Requests will also use custom encodings in the event that you need them. If
you have created your own encoding and registered it with the codecs
module, you can simply use the codec name as the value of r.encoding
and
Requests will handle the decoding for you.
Binary Response Content¶
You can also access the response body as bytes, for non-text requests:
>>> r.content b'[{"repository":{"open_issues":0,"url":"https://github.com/...
The gzip
and deflate
transfer-encodings are automatically decoded for you.
The br
transfer-encoding is automatically decoded for you if a Brotli library
like brotli or brotlicffi is installed.
For example, to create an image from binary data returned by a request, you can
use the following code:
>>> from PIL import Image >>> from io import BytesIO >>> i = Image.open(BytesIO(r.content))
JSON Response Content¶
There’s also a builtin JSON decoder, in case you’re dealing with JSON data:
>>> import requests >>> r = requests.get('https://api.github.com/events') >>> r.json() [{'repository': {'open_issues': 0, 'url': 'https://github.com/...
In case the JSON decoding fails, r.json()
raises an exception. For example, if
the response gets a 204 (No Content), or if the response contains invalid JSON,
attempting r.json()
raises requests.exceptions.JSONDecodeError
. This wrapper exception
provides interoperability for multiple exceptions that may be thrown by different
python versions and json serialization libraries.
It should be noted that the success of the call to r.json()
does not
indicate the success of the response. Some servers may return a JSON object in a
failed response (e.g. error details with HTTP 500). Such JSON will be decoded
and returned. To check that a request is successful, use
r.raise_for_status()
or check r.status_code
is what you expect.
Raw Response Content¶
In the rare case that you’d like to get the raw socket response from the
server, you can access r.raw
. If you want to do this, make sure you set
stream=True
in your initial request. Once you do, you can do this:
>>> r = requests.get('https://api.github.com/events', stream=True) >>> r.raw <urllib3.response.HTTPResponse object at 0x101194810> >>> r.raw.read(10) b'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03'
In general, however, you should use a pattern like this to save what is being
streamed to a file:
with open(filename, 'wb') as fd: for chunk in r.iter_content(chunk_size=128): fd.write(chunk)
Using Response.iter_content
will handle a lot of what you would otherwise
have to handle when using Response.raw
directly. When streaming a
download, the above is the preferred and recommended way to retrieve the
content. Note that chunk_size
can be freely adjusted to a number that
may better fit your use cases.
Note
An important note about using Response.iter_content
versus Response.raw
.
Response.iter_content
will automatically decode the gzip
and deflate
transfer-encodings. Response.raw
is a raw stream of bytes – it does not
transform the response content. If you really need access to the bytes as they
were returned, use Response.raw
.
More complicated POST requests¶
Typically, you want to send some form-encoded data — much like an HTML form.
To do this, simply pass a dictionary to the data
argument. Your
dictionary of data will automatically be form-encoded when the request is made:
>>> payload = {'key1': 'value1', 'key2': 'value2'} >>> r = requests.post('https://httpbin.org/post', data=payload) >>> print(r.text) { ... "form": { "key2": "value2", "key1": "value1" }, ... }
The data
argument can also have multiple values for each key. This can be
done by making data
either a list of tuples or a dictionary with lists
as values. This is particularly useful when the form has multiple elements that
use the same key:
>>> 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
There are times that you may want to send data that is not form-encoded. If
you pass in a string
instead of a dict
, that data will be posted directly.
For example, the GitHub API v3 accepts JSON-Encoded POST/PATCH data:
>>> import json >>> url = 'https://api.github.com/some/endpoint' >>> payload = {'some': 'data'} >>> r = requests.post(url, data=json.dumps(payload))
Please note that the above code will NOT add the Content-Type
header
(so in particular it will NOT set it to application/json
).
If you need that header set and you don’t want to encode the dict
yourself,
you can also pass it directly using the json
parameter (added in version 2.4.2)
and it will be encoded automatically:
>>> url = 'https://api.github.com/some/endpoint' >>> payload = {'some': 'data'}
>>> r = requests.post(url, json=payload)
Note, the json
parameter is ignored if either data
or files
is passed.
POST a Multipart-Encoded File¶
Requests makes it simple to upload Multipart-encoded files:
>>> url = 'https://httpbin.org/post' >>> files = {'file': open('report.xls', 'rb')} >>> r = requests.post(url, files=files) >>> r.text { ... "files": { "file": "<censored...binary...data>" }, ... }
You can set the filename, content_type and headers explicitly:
>>> 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>" }, ... }
If you want, you can send strings to be received as files:
>>> 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" }, ... }
In the event you are posting a very large file as a multipart/form-data
request, you may want to stream the request. By default, requests
does not
support this, but there is a separate package which does —
requests-toolbelt
. You should read the toolbelt’s documentation for more details about how to use it.
For sending multiple files in one request refer to the advanced
section.
Warning
It is strongly recommended that you open files in binary
mode. This is because Requests may attempt to provide
the Content-Length
header for you, and if it does this value
will be set to the number of bytes in the file. Errors may occur
if you open the file in text mode.
Response Status Codes¶
We can check the response status code:
>>> r = requests.get('https://httpbin.org/get') >>> r.status_code 200
Requests also comes with a built-in status code lookup object for easy
reference:
>>> r.status_code == requests.codes.ok True
If we made a bad request (a 4XX client error or 5XX server error response), we
can raise it with
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
But, since our status_code
for r
was 200
, when we call
raise_for_status()
we get:
>>> r.raise_for_status() None
All is well.
Cookies¶
If a response contains some Cookies, you can quickly access them:
>>> url = 'http://example.com/some/cookie/setting/url' >>> r = requests.get(url) >>> r.cookies['example_cookie_name'] 'example_cookie_value'
To send your own cookies to the server, you can use the cookies
parameter:
>>> url = 'https://httpbin.org/cookies' >>> cookies = dict(cookies_are='working') >>> r = requests.get(url, cookies=cookies) >>> r.text '{"cookies": {"cookies_are": "working"}}'
Cookies are returned in a RequestsCookieJar
,
which acts like a dict
but also offers a more complete interface,
suitable for use over multiple domains or paths. Cookie jars can
also be passed in to requests:
>>> 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"}}'
Redirection and History¶
By default Requests will perform location redirection for all verbs except
HEAD.
We can use the history
property of the Response object to track redirection.
The Response.history
list contains the
Response
objects that were created in order to
complete the request. The list is sorted from the oldest to the most recent
response.
For example, GitHub redirects all HTTP requests to HTTPS:
>>> r = requests.get('http://github.com/') >>> r.url 'https://github.com/' >>> r.status_code 200 >>> r.history [<Response [301]>]
If you’re using GET, OPTIONS, POST, PUT, PATCH or DELETE, you can disable
redirection handling with the allow_redirects
parameter:
>>> r = requests.get('http://github.com/', allow_redirects=False) >>> r.status_code 301 >>> r.history []
If you’re using HEAD, you can enable redirection as well:
>>> r = requests.head('http://github.com/', allow_redirects=True) >>> r.url 'https://github.com/' >>> r.history [<Response [301]>]
Timeouts¶
You can tell Requests to stop waiting for a response after a given number of
seconds with the timeout
parameter. Nearly all production code should use
this parameter in nearly all requests. Failure to do so can cause your program
to hang indefinitely:
>>> 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)
Note
timeout
is not a time limit on the entire response download;
rather, an exception is raised if the server has not issued a
response for timeout
seconds (more precisely, if no bytes have been
received on the underlying socket for timeout
seconds). If no timeout is specified explicitly, requests do
not time out.
Errors and Exceptions¶
In the event of a network problem (e.g. DNS failure, refused connection, etc),
Requests will raise a ConnectionError
exception.
Response.raise_for_status()
will
raise an HTTPError
if the HTTP request
returned an unsuccessful status code.
If a request times out, a Timeout
exception is
raised.
If a request exceeds the configured number of maximum redirections, a
TooManyRedirects
exception is raised.
All exceptions that Requests explicitly raises inherit from
requests.exceptions.RequestException
.
Ready for more? Check out the advanced section.
If you’re on the job market, consider taking this programming quiz. A substantial donation will be made to this project, if you find a job through this platform.
HTTP
Давайте подумаем о том, каким образом данные передаются от одного устройства другому в компьютерных сетях. Данные изначально представлены в виде последовательности байтов. А между устройствами эти байты уже передаются в виде физического сигнала различной природы: электричество, радиоволна, свет и др. Всего выделяют семь уровней, которые проходят данные, начиная с представления в виде байтов и заканчивая физическим сигналом.
Эти уровни передачи определены в стандарте OSI (The Open Systems Interconnection model, модель взаимодействия открытых систем). На каждом уровне описаны протоколы, по которым происходит обмен данными. Протокол — это набор правил, который определяет процесс обмена данными между различными устройствами или программами.
Программы работают на самом высоком уровне — прикладном. Программисты для передачи данных между программами выбирают один из существующих протоколов прикладного уровня и следуют его правилам. Такой подход позволяет не задумываться о том, как передавать данные на более низких уровнях. Это упрощает и ускоряет разработку сетевых программ.
Для обмена данными между программами в компьютерных сетях используются различные протоколы прикладного уровня. Один из самых популярных протоколов — HTTP (HyperText Transfer Protocol, протокол передачи гипертекста). Он создавался для передачи гипертекстовых документов формата HTML (веб-страницы), однако в настоящее время используется для обмена произвольными данными: графическими и видеофайлами, документами и т. д.
Протокол HTTP работает по принципу «запрос — ответ». В протоколе описаны виды запросов, правила формирования запросов и возможные варианты ответов на запросы.
Ответы в протоколе HTTP имеют коды состояния, которые представляют собой числовые значения. По коду состояния всегда можно определить, верно ли был обработан запрос или произошла ошибка.
Например, вам, скорее всего, знаком код состояния 404
, который означает, что запрошенный объект (чаще всего веб-страница) не был найден.
В протоколе HTTP описаны различные виды запросов: на получение данных (GET), на передачу данных (POST), на добавление и изменение данных (PUT), на удаление данных (DELETE) и др.
Итак, многие сетевые программы взаимодействуют по протоколу HTTP. Часто разработчикам веб-сервисов нужно отправлять в качестве ответа на запросы не веб-страницы, а данные, например информацию о географических объектах в формате JSON или графический файл с определённой областью географической карты. Порой это помогает расширить функционал сервиса. К примеру, добавить авторизацию через соцсети.
Бывает и коммерческий вариант такого взаимодействия, когда сторонние разработчики платят за то, чтобы использовать возможность работы с сервисами для создания собственных продуктов.
Реализация взаимодействия с сервисом через такой набор правил происходит посредством API (Application Programming Interface). Правила API описывают возможные запросы к сервису и ответы сервиса на эти запросы. Так, API отдельного веб-сервиса можно назвать нестандартным протоколом этого сервиса, действующим над протоколом HTTP.
Для работы с API можно не писать программу, а просто выполнить запрос в браузере, так как он также работает по протоколу HTTP.
Static API
Воспользуемся в качестве примера одним из API сервиса Яндекс Карт — Static API. Static API возвращает изображение карты в ответ на HTTPS-запрос. Добавляя в URL разные параметры и задавая их значения, вы можете определить центр карты, её размер и область показа, отметить нужные объекты и отобразить пробки. Данные будут обновляться при каждом новом обращении, поэтому карта всегда будет актуальной.
Обратите внимание: у каждого API есть условия использования, которые нужно обязательно изучить перед началом работы и которым нужно следовать при разработке программ на основе API. Например, для Static API в бесплатной версии API Яндекс Карт изображение карты обязательно должно быть размещено на общедоступном сайте или в приложении. Есть и другие условия бесплатного использования.
Давайте сделаем первый запрос к Static API. Для этого надо открыть документацию и понять, каким должен быть запрос. Запрос к Static API имеет следующий формат: https://static-maps.yandex.ru/1.x/?{параметры URL}. Воспользуемся примерами использования и выполним в браузере следующий запрос:
https://static-maps.yandex.ru/1.x/?ll=37.677751,55.757718&spn=0.016457,0.00619&l=map
Параметр ll
отвечает за координаты центра карты (через запятую указываются долгота и широта в градусах). Параметр spn
определяет область показа (протяжённость карты в градусах по долготе и широте). Параметр l
определяет тип карты (в запросе используется тип map
— схема). Все возможные параметры запроса можно посмотреть в документации.
В ответе на запрос сервер пришлёт часть карты по запрошенным координатам.
Чтобы воспользоваться API в программе, нужно из неё отправить такой же запрос, а затем получить ответ сервера. Для удобного формирования HTTP-запросов и получения ответов можно использовать библиотеку requests
.
Библиотека requests
является нестандартной и устанавливается следующей командой:
pip install requests
В библиотеке requests
функции для формирования HTTP-запросов называются так же, как и сами запросы. Например, для выполнения GET-запроса используем функцию get()
:
from requests import get
response = get("https://static-maps.yandex.ru/1.x/?"
"ll=37.677751,55.757718&"
"spn=0.016457,0.00619&"
"l=map")
print(response)
Вывод программы:
<Response [200]>
Функция get()
вернула ответ HTTP-сервера с кодом 200. Значит, запрос был обработан успешно. Для получения данных из ответа сервера воспользуемся атрибутом content
. В этом атрибуте находятся данные в виде последовательности байтов. Из документации Static API известно, что ответом сервера на успешный запрос должен быть графический файл формата PNG
. Запишем данные из ответа сервера в новый файл с расширением .png
. Для этого откроем файл функцией open()
на запись в бинарном режиме (wb
), так как будем сохранять байты, а не текст. А затем воспользуемся методом write()
созданного файлового объекта:
with open("map.png", "wb") as file:
file.write(response.content)
В папке с программой будет создан файл map.png
с запрошенной областью карты.
В нашей программе запрос формируется в виде длинной строки, включающей в себя адрес сервера и параметры запроса. Функция get()
имеет аргумент params
, в который можно передать словарь с параметрами запроса:
params = {"ll": "37.677751,55.757718",
"spn": "0.016457,0.00619",
"l": "map"}
response = get("https://static-maps.yandex.ru/1.x/", params=params)
При выполнении запроса к серверу могут происходить нештатные ситуации. Например, отсутствие подключения к сети. В таком случае программа вызовет исключение ConnectionError
из модуля requests
. Обработка исключения поможет нашей программе не падать с ошибкой, а сообщить о необходимости проверки подключения к сети:
from requests import get, ConnectionError
params = {"ll": "37.677751,55.757718",
"spn": "0.016457,0.00619",
"l": "map"}
try:
response = get("https://static-maps.yandex.ru/1.x/", params=params)
except ConnectionError:
print("Проверьте подключение к сети.")
else:
with open("map.png", "wb") as file:
file.write(response.content)
Static API имеет очень широкий функционал, поэтому рассмотреть его весь в данном параграфе невозможно. Для более детального изучения API воспользуйтесь документацией.
API Яндекс Диск
Рассмотрим ещё один пример взаимодействия с API. Яндекс Диск — это облачный сервис для хранения файлов и обмена ими. Он предоставляет API для сохранения файлов и организации доступа к ним. Полный список запросов к API и формат ответов сервера находится в документации.
Для работы с API Яндекс Диска требуется авторизация пользователя по протоколу OAuth 2.0. Открытый протокол авторизации OAuth 2.0 обеспечивает предоставление третьей стороне ограниченного доступа к защищённым ресурсам пользователя без передачи ей логина и пароля. Вместо логина и пароля авторизация производится по OAuth-токену. Токен — это уникальная для каждого пользователя строка-ключ для доступа с помощью API к файлам в облаке.
Для работы с API необходимо создать и зарегистрировать приложение-сервис по инструкции. При создании сервиса выполните следующие действия:
- Заполнить поле «Название сервиса».
- Выбрать вариант платформы «Веб-сервисы».
- Выбрать значение «Подставить URL для разработки» в поле Callback URL.
- В качестве доступных сервису данных указать «Яндекс Диск REST API» и выдать все четыре возможных права доступа.
- Нажать кнопку «Создать приложение».
После выполнения указанных действий появится окно, в котором будут указаны значения для полей ClientID
, Client secret
и Redirect URL
. Эти значения мы будем использовать для получения токена по протоколу авторизации OAuth 2.0. Данный протокол реализован в библиотеке requests-oauthlib
.
Приложение-сервис должно перенаправить пользователя по ссылке для авторизации. Пользователь переходит по ссылке и копирует сгенерированный одноразовый код, а затем вводит его в приложение. Библиотека requests-oauthlib
на основе введённого кода делает запрос на получение токена и возвращает его. Более детальный порядок получения токена изложен в инструкции.
Напишем программу с авторизацией сервиса и получением токена. Значения переменных client_id
и client_secret
нужно скопировать из полей ClientID
, Client secret
со страницы информации о зарегистрированном сервисе.
from requests_oauthlib import OAuth2Session
from requests import get, post, put, delete
client_id = ""
client_secret = ""
auth_url = "https://oauth.yandex.ru/authorize"
token_url = "https://oauth.yandex.ru/token"
oauth = OAuth2Session(client_id=client_id)
authorization_url, state = oauth.authorization_url(auth_url, force_confirm="true")
print("Перейдите по ссылке, авторизуйтесь и скопируйте код:", authorization_url)
code = input("Вставьте одноразовый код: ")
token = oauth.fetch_token(token_url=token_url,
code=code,
client_secret=client_secret)
access_token = token["access_token"]
print(access_token)
Переменная token
является словарём, в котором необходимый для выполнения запросов токен находится по ключу access_token
.
Полученный на этапе авторизации токен необходимо передавать во всех запросах к API. Для этого нужно заполнять в заголовке запроса поле Authorization
значением OAuth <ваш токен>
.
Создадим словарь headers
с соответствующими ключом и значением. Заголовок с токеном будут передаваться в функции запросов модуля requests
через именованный аргумент с таким же именем headers
.
Все возможные запросы к сервису, их формат и ответы сервера можно посмотреть в документации.
Выполним GET-запрос по адресу https://cloud-api.yandex.net/v1/disk для получения информации о состоянии облачного хранилища:
headers = {"Authorization": f"OAuth {access_token}"}
r = get("https://cloud-api.yandex.net/v1/disk", headers=headers)
print(r.json())
Данные в ответе на запрос имеют формат JSON. Они преобразуются в словарь методом json()
. В ответе содержится информация о максимальном размере файла на диске (поле max_file_size
), о размере диска (поле total_space
), об использованном объёме (used_space
) и др. Полное описание полей ответа можно посмотреть в документации.
Создадим в корневой папке диска новую папку Тест API
. Скопируем файл map.png
, полученный в примерах для Static API, в эту папку. Для создания папки потребуется выполнить PUT-запрос по адресу https://cloud-api.yandex.net/v1/disk/resources с обязательным параметром path
, в котором должен быть записан путь к создаваемой папке.
params = {"path": "Тест API"}
r = put("https://cloud-api.yandex.net/v1/disk/resources", headers=headers, params=params)
print(r)
В случае успешного запроса программа выведет код ответа <Response [201]>, а в корневой папке облачного диска будет создана папка Тест API
:
Для копирования файла в папку необходимо выполнить два запроса:
- GET-запрос по адресу https://cloud-api.yandex.net/v1/disk/resources/upload для получения ссылки на загрузку файла в облако. Если запрос будет успешен, то URL для загрузки файла будет в поле
href
JSON-ответа сервера. - PUT-запрос по URL из предыдущего пункта. В аргумент
files
функцииput
передаётся словарь с ключомfile
и значением — файловым объектом, открытым в режиме бинарного чтенияrb
. Передавать токен в данном запросе не нужно.
Выполним указанные запросы в программе:
params = {"path": "Тест API/map.png"}
r = get("https://cloud-api.yandex.net/v1/disk/resources/upload",
headers=headers, params=params)
href = r.json()["href"]
files = {"file": open("map.png", "rb")}
r = put(href, files=files)
print(r)
В результате успешного запроса программа выведет <Response [201]>, а в папке Тест API
в облаке появится файл map.png
:
Попробуйте реализовать другие операции с хранилищем через запросы к API в соответствии с документацией.
Кроме рассмотренных API сервисов Яндекса существует большое количество других сервисов. Теперь вы знаете, как ваше приложение должно с ними взаимодействовать для обмена данными.
#статьи
-
0
Разбираемся в методах работы с HTTP-запросами в Python на практике.
Иллюстрация: Катя Павловская для Skillbox Media
Изучает Python, его библиотеки и занимается анализом данных. Любит путешествовать в горах.
Библиотека Requests для Python позволяет работать с HTTP-запросами любого уровня сложности, используя простой синтаксис. Это помогает не тратить время на написание кода, а быстро взаимодействовать с серверами.
Python Requests — это библиотека, которая создана для быстрой и простой работы с запросами. Стандартные HTTP-библиотеки Python, например та же Urllib3, часто требуют значительно больше кода для выполнения одного и того же действия, а это затрудняет работу. Давайте сравним код для простой задачи, написанный с помощью Urllib3 и Requests.
Urllib3:
import urllib3
http = urllib3.PoolManager()
gh_url = 'https://api.github.com'
headers = urllib3.util.make_headers(user_agent= 'my-agent/1.0.1', basic_auth='abc:xyz')
requ = http.request('GET', gh_url, headers=headers)
print (requ.headers)
print(requ.data)
# ------# 200# 'application/json'
Requests:
import requests r = requests.get('https://api.github.com', auth=('user', 'pass')) print r.status_codeprint r.headers['content-type'] # ------# 200# 'application/json'
Количество строк различается в два раза: на Urllib3 — восемь строк, а на Requests — четыре. И это только один небольшой запрос.
Писать код на Python лучше всего в специальной IDE, например в PyCharm или Visual Studio Code. Они подсвечивают синтаксис и предлагают автодополнение кода — это сильно упрощает работу программиста. Весь код из этой статьи мы писали в Visual Studio Code.
Для начала работы с библиотекой Requests её необходимо установить в IDE. Для этого откройте IDE и введите команду в терминале:
pip install requests
Библиотека готова к работе. Остаётся только импортировать её:
import requests
Из всех HTTP-запросов наиболее часто используется GET. Он позволяет получить данные из указанного источника — обычно с какого-то веб-сайта. Чтобы отправить GET-запрос, используется метод requests.get(), в который в качестве параметра добавляется URL-адрес назначения:
requests.get('https://skillbox.ru')
Этот код совершает одно действие — связывается с указанным адресом и получает от сервера информацию о нём. Когда вы вводите домен в адресную строку браузера и переходите на сайт, под капотом выполняются те же самые операции. Единственное различие в том, что Requests позволяет получить чистый HTML-код страницы без рендеринга, то есть мы не видим вёрстку и разные визуальные компоненты — только код и техническую информацию.
Для проверки ответа на запрос существуют специальные НТТР-коды состояния. Чтобы воспользоваться ими, необходимо присвоить запрос переменной и «распечатать» её значение:
res = requests.get('https://skillbox.ru') # Создаём переменную, в которую сохраним код состояния запрашиваемой страницы. print(res) # Выводим код состояния.
Если запустить этот код, то в терминале выведется <Response [200]>. Это хороший результат — значит, запрос прошёл успешно. Но бывают и другие HTTP-коды состояний.
Коды состояний имеют вид трёхзначных чисел от 100 до 500. Чаще всего встречаются следующие:
- 200 — «OK». Запрос прошёл успешно, и мы получили ответ.
- 400 — «Плохой запрос». Его получаем тогда, когда сервер не может понять запрос, отправленный клиентом. Как правило, это указывает на неправильный синтаксис запроса, неправильное оформление сообщения запроса и так далее.
- 401 — «Unauthorized». Для выполнения запроса необходимы актуальные учётные данные.
- 403 — «Forbidden». Сервер понял запрос, но не может его выполнить. Например, у используемой учётной записи нет достаточных прав для просмотра содержимого.
- 404 — «Не найдено». Сервер не нашёл содержимого, соответствующего запросу.
Кодов состояния намного больше. С полным списком можно ознакомиться здесь.
Для получения содержимого страницы используется метод content. Он позволяет получить информацию в виде байтов, то есть в итоге у нас будет вся информация, не только строковая. Запустим его и посмотрим на результат:
response = requests.get('https://api.github.com') response.content
Ответ:
Информацию из байтового вида в строковый можно декодировать с помощью метода text:
response = requests.get('https://api.github.com') response.text
Ответ:
В обоих случаях мы получаем классический JSON-текст, который можно использовать как словарь, получая доступ к нужным значениям по известному ключу.
Заголовки ответа — важная часть запроса. Хотя в них и нет содержимого исходного сообщения, зато там можно обнаружить множество важных деталей ответа: информация о сервере, дата, кодировка и так далее. Для работы с ними используется метод headers:
print(response.headers)
Ответ:
Зачем это надо? Например, таким образом мы можем узнать дату и время на сервере в момент получения запроса. В нашем случае ответ пришёл 9 марта в 05:45:28 GMT. Это помогает логировать действия для их последующей оценки, например, при поиске ошибок выполнения.
Метод | Описание |
---|---|
GET | GET-метод используется для обычного запроса к серверу и получения информации по URL. |
POST | Метод запроса POST запрашивает веб-сервис для приёма данных, например для хранения информации. |
PUT | Метод PUT просит, чтобы вложенный в него объект был сохранён под определённым URI. Если URI ссылается на уже существующий ресурс, он модифицируется, а если URI указывает на несуществующий ресурс, сервер может создать новый ресурс с этим URI. |
DELETE | Метод DELETE удаляет объект с сервера. |
HEAD | Метод HEAD запрашивает ответ, идентичный запросу GET, но без тела ответа. |
PATCH | Метод используется для модификации информации на сервере. |
Подробнее о методах можно прочитать в официальной документации.
Запрос GET можно настроить с помощью передачи параметров в методе params. Посмотрим, как это работает на простом примере — попробуем найти изображение на фотостоке Pixabay.
Для начала создадим переменную, которая будет содержать необходимые нам параметры:
query = {'q': 'Forest', 'order': 'popular', 'min_width': '1000', 'min_height': '800'}
Наш запрос для поиска изображений на стоке Pixabay представлен словарём, где:
- q — передаём ключевые слова для поиска;
- order — порядок фильтрации поиска, в нашем случае — по популярности;
- min_width и min_height — минимальная ширина и высота соответственно.
Напишем запрос и посмотрим на результат выполнения:
req = requests.get('<a href="https://pixabay.com/en/photos/">https://pixabay.com/en/photos/</a>', params=query) req.url
В ответе мы получим ссылку с нужными параметрами запроса:
‘<a href=»https://pixabay.com/en/photos/?order=popular_height=800&q=Forest&min_width=1000″>https://pixabay.com/en/photos/?order=popular_height=800&q=Forest&min_width=1000</a>’
Откроём её в браузере:
Всё получилось. У нас правильно настроена сортировка и размеры изображений.
Аутентификацию используют в тех случаях, когда сервис должен понять, кто вы. Например, это часто необходимо при работе с API. Аутентификация в библиотеке Requests очень простая — для этого достаточно использовать параметр с именем auth. Попробуем написать код для доступа к API GitHub. Для него вам потребуются данные учётной записи на сервисе — логин и пароль. Поставьте их в нужные места кода:
from getpass import getpass # Импортируем метод getpass из одноимённой библиотеки для ввода пароля доступа. requests.get('https://api.github.com/user', auth=('username', getpass())
При запуске кода вам будет необходимо ввести пароль от своего профиля. Если пароль правильный, вернётся ответ 200, если нет — 401.
SSL-сертификат указывает на то, что установленное через HTTP соединение безопасно и зашифровано. Важно, что библиотека Requests не только умеет работать с SSL-сертификатами «из коробки», но и позволяет настраивать взаимодействие с ними. Для примера отключим проверку SSL-сертификата, передав параметру функции запроса verify значение False:
requests.get('https://api.github.com', verify=False)
Ответ:
Мы видим, что ответ на запрос содержит предупреждение о неверифицированном сертификате. Всё дело в том, что мы отключили его получение вручную в коде выше с помощью функции verify.
Метод GET позволяет работать с запросами на высоком уровне абстракции, не разбираясь в деталях их выполнения, при этом надо настроить лишь базовые параметры.
Однако возможности библиотеки Requests на этом не заканчиваются: с помощью класса Session мы можем контролировать выполнение запросов и увеличивать скорость их выполнения.
Класс Session позволяет создавать сеансы — базовые запросы с сохранёнными параметрами (то есть без повторного указания параметров).
Напишем код для простой сессии, позволяющей получить доступ к GitHub:
import requests from getpass import getpass with requests.Session() as session: session.auth = ('login', getpass()) response = session.get('https://api.github.com/user') # Выведем ответ на экран. print(response.headers) print(response.json())
Запустим его и введём пароль. Как видим, всё сработало:
Запрос возвращает информацию с сервера при этом работает с помощью session. То есть теперь нам не придётся вводить повторные параметры авторизации при следующих запросах.
Библиотека Requests — простой инструмент для работы с HTTP-запросами разного уровня сложности. Рекомендуем подробно изучить возможности библиотеки, методы и примеры их использования в официальной документации.
Как зарабатывать больше с помощью нейросетей?
Бесплатный вебинар: 15 экспертов, 7 топ-нейросетей. Научитесь использовать ИИ в своей работе и увеличьте доход.
Узнать больше
Прежде чем начать, убедитесь, что установлена последняя версия Requests.
Для начала, давайте рассмотрим простые примеры.
Импортируйте модуль Requests:
Попробуем получить веб-страницу с помощью get-запроса. В этом примере давайте рассмотрим общий тайм-лайн GitHub:
r = requests.get('https://api.github.com/events')
Мы получили объект Response
с именем r
. С помощью этого объекта можно получить всю необходимую информацию.
Простой 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')
Передача параметров в 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)
print(r.url)
Как видно, 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
Содержимое ответа (response)
Мы можем прочитать содержимое ответа сервера. Рассмотрим снова тайм-лайн GitHub:
>>> import requests
>>> r = requests.get('https://api.github.com/events')
>>> r.text
'[{"repository":{"open_issues":0,"url":"https://github.com/...
Requests будет автоматически декодировать содержимое ответа сервера. Большинство кодировок unicode
декодируются без проблем.
Когда вы делаете запрос, Requests делает предположение о кодировке, основанное на заголовках HTTP. Эта же кодировка текста, используется при обращение к 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
.
Бинарное содержимое ответа
Вы можете также получить доступ к телу ответа в виде байтов для не текстовых ответов:
>>> 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
Если вы работаете с данными в формате 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 (No Content), или на случай если ответ содержит не валидный JSON, попытка обращения к r.json()
будет возвращать ValueError: No JSON object could be decoded
.
Следует отметить, что успешный вызов 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)
Использование r.iter_content
обработает многое из того, с чем бы вам пришлось иметь дело при использовании r.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=
. - Они же будут удалены при редиректе.
- Заголовки авторизации с прокси будут переопределены учетными данными прокси-сервера, которые указаны в вашем URL.
- Content-Length будут переопределены, когда вы определите длину содержимого.
Кроме того, запросы не меняют свое поведение вообще, основываясь на указанных пользовательских заголовках.
Значения заголовка должны быть
string, bytestring или unicode
. Хотя это разрешено, рекомендуется избегать передачи значений заголовковunicode
.
Более сложные POST запросы
Часто вы хотите послать некоторые form-encoded
данные таким же образом, как это делается в HTML форме. Для этого просто передайте соответствующий словарь в аргументе data
. Ваш словарь данных в таком случае будет автоматически закодирован как HTML форма, когда будет сделан запрос:
>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> r = requests.post("https://httpbin.org/post", data=payload)
>>> print(r.text)
{
...
"form": {
"key2": "value2",
"key1": "value1"
},
...
}
Аргумент data
также может иметь несколько значений для каждого ключа. Это можно сделать, указав data
в формате tuple
, либо в виде словаря со списками в качестве значений. Особенно полезно, когда форма имеет несколько элементов, которые используют один и тот же ключ:
>>> 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
Бывают случаи, когда нужно отправить данные не закодированные методом form-encoded
. Если вы передадите в запрос строку вместо словаря, эти данные отправятся в не измененном виде.
К примеру, GitHub API v3 принимает закодированные JSON POST/PATCH данные:
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 отправка Multipart-Encoded файла
Запросы упрощают загрузку файлов с многостраничным кодированием (Multipart-Encoded) :
>>> 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 для получения более детальной информации о том, как им пользоваться.
Для отправки нескольких файлов в одном запросе, обратитесь к расширенной документации.
Предупреждение!
Настоятельно рекомендуется открывать файлы в бинарном режиме. Это связано с тем, что запросы могут пытаться предоставить для вас заголовок Content-Length
, и если это значение будет установлено на количество байтов в файле будут возникать ошибки, при открытии файла в текстовом режиме.
Коды состояния ответа
Мы можем проверить код состояния ответа:
>>> r = requests.get('https://httpbin.org/get')
>>> r.status_code
200
У requests есть встроенный объект вывода кодов состояния:
>>> r.status_code == requests.codes.ok
True
Если мы сделали неудачный запрос (ошибка 4XX или 5XX), то можем вызвать исключение с помощью r.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'
Cookies
Если в запросе есть cookies, вы сможете быстро получить к ним доступ:
>>> url = 'https://example.com/some/cookie/setting/url'
>>> r = requests.get(url)
>>> r.cookies['example_cookie_name']
'example_cookie_value'
Чтобы отправить собственные cookies на сервер, используйте параметр cookies:
>>> url = 'https://httpbin.org/cookies'
>>> cookies = dict(cookies_are='working')
>>> r = requests.get(url, cookies=cookies)
>>> r.text
'{"cookies": {"cookies_are": "working"}}'
Cookies возвращаются в 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 будет выполнять редиректы для всех HTTP глаголов, кроме HEAD.
Мы можем использовать свойство history
объекта Response, чтобы отслеживать редиректы .
Список Response.history содержит объекты Response, которые были созданы для того, чтобы выполнить запрос. Список сортируется от более ранних, до более поздних ответов.
Например, GitHub перенаправляет все запросы HTTP на HTTPS:
>>> r = requests.get('https://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('https://github.com/', allow_redirects=False)
>>> r.status_code
301
>>> r.history
[]
Если вы используете HEAD, вы также можете включить редирект:
>>> r = requests.head('https://github.com/', allow_redirects=True)
>>> r.url
'https://github.com/'
>>> r.history
[<Response [301]>]
Тайм-ауты
Вы можете сделать так, чтобы Requests прекратил ожидание ответа после определенного количества секунд с помощью параметра 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 секунд (точнее, если ни одного байта не было получено от основного сокета за timeout секунд).
Ошибки и исключения
В случае неполадок в сети (например, отказа DNS, отказа соединения и т.д.), Requests вызовет исключение ConnectionError
.
Response.raise_for_status()
вызовет HTTPError
если в запросе HTTP возникнет статус код ошибки.
Если выйдет время запроса, вызывается исключение Timeout
. Если запрос превышает заданное значение максимального количества редиректов, то вызывают исключение TooManyRedirects
.
Все исключения, которые вызывает непосредственно Requests унаследованы от requests.exceptions.RequestException
.
Тест на знание основ Requests
Какой из HTTP-запросов является правильным?
requests.get(url, params={‘key’:’value’})
requests.post(url, params={‘key’:’value’})
requests.post(url, data={‘key’:’value’})
Какое из утверждений верно?
response.json возвращает содержимое ответа в виде объекта dict
response.content возвращает содержимое ответа в виде строкового объекта
response.json() возвращает содержимое ответа в виде объекта dict
response.text возвращает содержимое ответа в виде байтового объекта
Как получить куки из ответа на запрос?
Что вернет метод status_code объекта Response?
Ошибку, если запрос неудачный
Какой код сформирует url «https://test.com/page?key1=value1&key2=value21,value22»
r.get(‘https://test.com/page’, params={‘key1’: ‘value1’, ‘key2’: ‘value21,value22’})
r.get(‘https://test.com/page’, params={‘key1’: ‘value1’, ‘key2’: ‘value21’, ‘key2’: ‘value22’})
r.get(‘https://test.com/page’, params={‘key1’: ‘value1’, ‘key2’: [‘value21’, ‘value22’]})
-
Главная
-
Инструкции
-
Python
-
Введение в работу с библиотекой Requests в Python
Requests — это одна из библиотек, которая поможет вам подружить ваш локальный скрипт с веб-ресурсами и глобальной сетью. Requests предоставляет разработчику обширный пул функций для работы со всеми видами HTTP-запросов. Благодаря этой библиотеке вы сможете получить прогноз погоду, перевести текст, скачать фильм или фото без использования браузера внутри скрипта.
В этом материале вы найдете вводную информацию по этой библиотеке, которой будет достаточно для её использования в ваших скриптах.
В рамках всего материала речь будет идти про работу в IDE Pycharm.
Библиотека Requests в Python является сторонней, поэтому перед началом работы её необходимо установить. Создаем проект и открываем терминал. В Python установка библиотеки Requests осуществляется следующей командой:
pip3 install requests
Если вы используете виртуальную среду Pipenv, то установка библиотеки Requests в Python 3 производится другой командой:
pipenv install requests
После исполнения этих команд начнется загрузка модуля. С помощью команды pip freeze
можем узнать, какие модули были установлены:
PS C:\Users\Timeweb\PycharmProjects\RequestTimeweb> pip3 freezecertifi==2022.9.24
charset-normalizer==2.1.1
idna==3.4
requests==2.28.1
urllib3==1.26.12
Расскажем, какие функции выполняют эти модули:
- certifi — пакет сертификатов для обеспечения безопасного соединения;
- charset-normalizer — библиотека, которая автоматически распознает тексты неизвестной кодировки. Полезный модуль, поскольку не все сайты и сервисы работают на распространенной UTF-8;
- idna — библиотека для поддержки интернационализированных доменных имен в приложениях;
- requests — собственно, сам модуль request;
- urllib3 — модуль, включающий в себя функции и классы для работы с URL-адресами;
Первые запросы
В этой части статьи мы напишем код для получения информации с ресурсов и узнаем основные составляющие библиотеки Request. В дальнейшем мы более детально разберем все аспекты.
Работа с библиотекой Requests в Python начинается с импорта:
import request
Обратимся к сайту google.com:
import requests as rqresponse = rq.get('https://google.com')
print(response)
Вывод:
<Response [200]>
Здесь мы используем HTTP-запрос GET. Его работа аналогична переходу на сайт по URL в браузере. Далее мы подробнее разберем его.
В ответ мы получили объект Response. У него огромное количество различных свойств, которые мы также разберем дальше. При выводе этого объекта мы получаем код 200 — он означает, что запрос выполнен успешно. Если мы обратимся к несуществующему разделу на сайте google.com, то получим ответ об его отсутствии:
import requests as rqresponse = rq.get('https://google.com/timeweb')
print(response)
Вывод:
<Response [404]>
Теперь разберем, какие вообще существуют запросы и как с ними работать.
HTTP-запросы
Основным запросом к сервисам и сайтам является GET. Он позволяет просматривать содержимое ресурса без его изменения. Однако для полноценной работы с ресурсами в сети может понадобится ряд других запросов. Некоторые из них могут не поддерживаться со стороны того или иного сервера. Вот 7 запросов, которые поддерживает библиотека Request:
- GET
- POST
- OPTIONS
- HEAD
- PUT
- PATCH
- DELETE
Для тестирования библиотеки создатели спроектировали сайт https://httpbin.org, с помощью которого вы можете попрактиковаться.
GET
GET передает информацию сайту прямо в заголовке. Поэтому его стоит использовать в случаях, когда передаваемая информация не является чем-то ценным: например, поиск некоторой страницы в интернет-магазине. Его не стоит использовать для передачи паролей, банковский карт и подобных данных.
Для передачи данных серверу к его URL-адресу добавляется знак ?, затем идут сами данные. Выглядит это следующим образом:
https://serverurl.ru/get?param1=value1¶m2=value2
где:
- https://serverurl.ru/get — это URL;
- param1=value1¶m2=value2 — параметры. Если параметров несколько, то они отделяются амперсандом «&».
В Request GET имеет следующий синтаксис:
request.get( 'URL-адрес', {key: value}, различные аргументы)
где:
- URL-адрес — адрес ресурса;
- {key: value} — параметры. Метод самостоятельно включит их в запрос. Необязательный аргумент метода;
- Различные аргументы — необязательные аргументы. Например, время таймаута.
Опробуем его на практике:
import requests as rqGetParams = {'param1': 'value1', 'param2': 'value2'}
response = rq.get('https://google.com', GetParams)
print(response.url)
Вывод:
https://www.google.com/?param1=value1¶m2=value2
POST
POST используется для отправки данных на сайт в теле запроса. Тело запроса — это данные, передающиеся при совершении запроса к ресурсу. Эта информация не размещается в заголовке и подходят для передачи конфиденциальных данных.
В Request POST имеет следующий синтаксис:
request.post('URL-адрес', {key: value}, различные аргументы}
где
- URL-адрес — адрес ресурса;
- {key: value} — параметры. Метод самостоятельно включит их в тело запроса. Необязательный аргумент метода;
- Различные аргументы — необязательные аргументы. Например, время таймаута.
Если не указать параметры, то GET и POST запросы вернут одинаковый результат для одного URL. Посмотрим, как работает это на практике:
import requests as rqPostParams = {'param1': 'value1', 'param2': 'value2'}
response = rq.post('https://httpbin.org/post', PostParams, timeout=2)
print(response.json()['form'])
Вывод:
{'param1': 'value1', 'param2': 'value2'}
С помощью метода json() мы получили тело запроса из response. К слову, если бы мы совершили аналогичный запрос к google.com, то получили бы ошибку:
import requests as rqPostParams = {'param1': 'value1', 'param2': 'value2'}
response = rq.post('https://google.com', PostParams, timeout=2)
print(response)
Вывод:
<Response [405]>
Ошибка 405 (Method Not Allowed) означает, что ресурс не поддерживает такой запрос.
Для того, чтобы узнать какие запросы поддерживает ресурс необходимо использовать OPTIONS.
OPTIONS
С помощью OPTIONS мы можем узнать, какие запросы не будут заблокированы ресурсом.
Синтаксис OPTIONS:
request.option('URL-адрес', необязательные аргументы)
Отправим OPTIONS-запрос к google.com и узнаем, какие запросы он поддерживает:
import requests as rqresponse = rq.options('https://google.com', timeout=2)
print(response.headers['Allow'])
Вывод:
GET, HEAD
HEAD
В ответ на HEAD сервер вернет HTTP заголовки. Этот запрос выполняется, когда нужно получить не содержимое файла, а другие данные. Также HEAD может выполнять тестовые функции.
Синтаксис:
request.head('URL-адрес', различные аргументы)
где
- URL-адрес — адрес ресурса;
- Различные аргументы — необязательные аргументы. Например, время таймаута.
Применим его к google.com:
import requests as rqresponse = rq.head('https://google.com', timeout=2)
print(response.headers)
В выводе мы получили большое количество заголовков.
PUT
PUT создает новый объект или заменяет существующий. Он похож на метод POST, но отличается идемпотентностью. Это означает, что при повторных выполнениях PUT с аналогичными данными результат не изменится.
Для лучшего понимания представим базу данных с паролями и логинами. Допустим пользователь хочет поменять пароль. При использовании метода POST он добавит новую запись со своим идентификатором, в данном случае логином (при отсутствии других программных проверок). При использовании PUT он обновит текущий.
Синтаксис:
request.put('URL-адрес', {key: value}, различные аргументы)
где:
- URL-адрес — адрес ресурса;
- {key: value} — параметры. Метод самостоятельно включит их в тело запроса.
- Различные аргументы — необязательные аргументы. Например, время таймаута.
Применим его к httpbin:
import requests as rqPutParams = {'param1': 'value2', 'param2': 'value2'}
response = rq.put('https://httpbin.org/put', data=PutParams, timeout=2)
print(response.status_code)
Вывод:
200
PATCH
С помощью PATCH осуществляется частичное обновление данных на ресурсе (например, смена токена). Синтаксис следующий:
request.patch ('URL-адрес', {key: value}, различные аргументы)
где:
- URL-адрес — адрес ресурса;
- {key: value} — параметры. Метод самостоятельно включит их в тело запроса.
- Различные аргументы — необязательные аргументы. Например, время таймаута.
Применим его httpbin:
import requests as rqPatchParams = {'param1': 'value2', 'param2': 'value2'}
response = rq.patch('https://httpbin.org/patch', data=PatchParams, timeout=2)
print(response.status_code)
Вывод:
200
DELETE
Используется для удаления некого объекта на ресурсе. Синтаксис:
requests.delete('URL-адрес', {key: value})
где:
- URL-адрес — адрес ресурса;
- {key: value} — параметры (данные, которые необходимо удалить). Метод самостоятельно включит их в тело запроса.
Применение:
import requests as rqDelParams = {'param1': 'value2', 'param2': 'value2'}
response = rq.delete('https://httpbin.org/delete', data=DelParams, timeout=2)
print(response.status_code)
Вывод:
200
Объект Response
Как было показано в примерах, объект Response обладает большим количеством методов и свойств. Ниже вы найдете их с кратким описанием:
- apparent_encoding — кодировка, распознанная charset-normalizer;
- close() — закрывает соединение с сервером;
- content — вывод полученных данных в байтах;
- cookies — вывод куки;
- elapsed — вывод времени;
- encoding — выбор кодировки для декодирования;
- headers — вывод заголовков ресурса;
- history — вывод переадресации;
- is_permanent_redirect — определение постоянных редиректов;
- is_redirect — определение наличия редиректа;
- iter_content() — возврат данных по частям;
- iter_lines() — возврат данных построчно;
- json() — возврат данных в формате JSON;
- links — возврат ссылок в заголовках ответа;
- next — возврат PreparedRequest;
- ok — True при удачном соединении, False при неудачном;
- raise_for_status() — вызов исключения HTTPError;
- reason — вывод текстового представления объекта;
- request — возврат результат PreparedRequest;
- status_code — код ответа;
- text — вывод ответа в юникоде;
- url — вывод URL.
Заключение
В рамках этого материала мы рассмотрели основные элементы библиотеки Requests. Если вы хотите узнать о ней больше, то официальная документация библиотеки Requests Python даст вам необходимые ответы. А для тестирования своих скриптов можно использовать облачные серверы от timeweb.cloud.