Python Requests

Основы

Введение в тему

Модуль python requests – это общепринятый стандарт для работы с запросами по протоколу HTTP.

Этот модуль избавляет Вас от необходимости работать с низкоуровневыми деталями. Работа с запросами становится простой и элегантной.

В этом уроке будут рассмотрены самые полезные функций библиотеки requests и различные способы их использования.

Перед использованием модуля его необходимо установить:

Создание get и post запроса

Сперва необходимо добавить модуль Requests в Ваш код:

Создадим запрос и получим ответ, содержащий страницу и все необходимые данные о ней.

import requests

response = requests.get('https://www.google.ru/')

В переменную response попадает ответ на запрос. Благодаря этому объекту можно использовать любую информацию, относящуюся к этому ответу.

Сделать POST запрос так же очень просто:

import requests

 

response = requests.post('https://www.google.ru/', data = {'foo':3})

Другие виды HTTP запросов, к примеру: PUT, DELETE, и прочих, выполнить ничуть не сложнее:

import requests

 

response = requests.put('https://www.google.ru/', data = {'foo':3})

response = requests.delete('https://www.google.ru/')

response = requests.head('https://www.google.ru/')

response = requests.options('https://www.google.ru/')

Передача параметров в url

Иногда может быть необходимо отправить различные данные вместе с запросом URL. При ручной настройке URL, параметры выглядят как пары ключ=значение после знака «?». Например, https://www.google.ru/search?q=Python. Модуль Requests предоставляет возможность передать эти параметры как словарь, применяя аргумент params. Если вы хотите передать q = Python и foo=’bar’ ресурсу google.ru/search, вы должны использовать следующий код:

import requests

params_dict = {'q':'Python', 'foo':'bar'}
response = requests.get('https://www.google.ru/search', params=params_dict)
print(response.url)

#Вывод:

https://www.google.ru/search?q=Python&foo=bar

Здесь мы видим, что URL был сформирован именно так, как это было задумано.

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

Так же есть возможность передавать в запрос список параметров:

import requests

params_dict = {'q':'Python', 'foo':['bar', 'eggs']}
response = requests.get('https://www.google.ru/search', params=params_dict)
print(response.url)
#Вывод:

https://www.google.ru/search?q=Python&foo=bar&foo=eggs

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

Код из предыдущего листинга создаёт объект Response, содержащий ответ сервера на наш запрос. Обратившись к его атрибуту .url можно просмотреть адрес, куда был направлен запрос. Атрибут .text позволяет просмотреть содержимое ответа. Вот как это работает:

import requests

params_dict = {'q':'Python'}
response = requests.get('https://www.google.ru/search', params=params_dict)
print(response.text)
#Вывод:<!doctype html><html lang="ru"><head><meta charset="UTF-8"><meta content="/images/branding/googleg/1x/googleg_standard_color_128dp.png"…

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

import requests

params_dict = {'q':'Python'}
response = requests.get('https://www.google.ru/search', params=params_dict)
print(response.encoding)
#Вывод:

windows-1251

Можно так же самостоятельно установить кодировку используя атрибут .encoding.

import requests

params_dict = {'q':'Python'}
response = requests.get('https://www.google.ru/search', params=params_dict)
response.encoding = 'utf-8' # указываем необходимую кодировку вручную
print(response.encoding)
#Вывод:

utf-8

Бинарное содержимое ответа

Существует возможность просмотра ответа в виде байтов:

import requests

params_dict = {'q':'Python'}
response = requests.get('https://www.google.ru/search', params=params_dict)
print(response.content)
#Вывод:

b'<!doctype html><html lang="ru"><head><meta charset="UTF-8"><meta content="/images/branding/googleg/1x/googleg_standard_color_128dp.png" …

При передаче со сжатием ответ автоматически декодируется для Вас.

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

Так же в Requests есть встроенная обработка ответов в формате JSON:

import requests
import json

response = requests.get(‘http://api.open-notify.org/astros.json’)
print(json.dumps(response.json(), sort_keys=True, indent=4))
#Вывод:

{

«message»: «success»,

«number»: 10,

«people»: [

{

«craft»: «ISS»,

«name»: «Mark Vande Hei»

},

{

«craft»: «ISS»,

«name»: «Oleg Novitskiy»

},

[/dm_code_snippet]

Если ответ не является JSON, то .json выбросит исключение:

import requests
import json

response = requests.get('https://www.google.ru/search')
print(json.dumps(response.json(), sort_keys=True, indent=4))
#Вывод:

…

json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Необработанное содержимое ответа

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

import requests

response = requests.get('https://www.google.ru/', stream=True)
print(response.raw)
print('Q'*10)
print(response.raw.read(15))
#Вывод:

<urllib3.response.HTTPResponse object at 0x000001E368771FA0>

QQQQQQQQQQ

b'\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xc5[[s\xdb'

Так же можно использовать метод .iter_content. Этот метод итерирует данные потокового ответа и это позволяет избежать чтения содержимого сразу в память для больших ответов. Параметр chunk_size – это количество байтов, которые он должен прочитать в памяти.  Параметр chunk_size можно произвольно менять.

import requests

response = requests.get('https://www.google.ru/', stream=True)
print(response.iter_content)
print('Q'*10)
print([i for i in response.iter_content(chunk_size=256)])
#Вывод:

<bound method Response.iter_content of <Response [200]>>

QQQQQQQQQQ

[b'<!doctype html><html itemscope="" itemtype="http://sche', b'ma.org/WebPage" lang="ru"><head><meta content=…

response.iter_content будет автоматически декодировать сжатый ответ. Response.raw — чистый набор байтов, неизменённое содержимое ответа.

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

Если необходимо установить заголовки в HTTP запросе, передайте словарь с ними в параметр headers. Значения заголовка должны быть типа string, bytestring или unicode. Имена заголовков не чувствительны к регистру символов.
В следующем примере мы устанавливаем информацию об используемом браузере:

import requests

response = requests.get('https://www.google.ru/', headers={'user-agent': 'unknown_browser'})
print(response.request.headers)
# Вывод:

{'user-agent': 'unknown_browser', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}

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

Существует способ отправить данные так, будто это результат заполнения формы на сайте:

import requests

response = requests.post('https://httpbin.org/post', data={'foo': 'bar'})
print(response.text)
# Вывод:

{

"args": {},

"data": "",

"files": {},

"form": {

"foo": "bar"

},

"headers": {

…

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

import requests

response = requests.post('https://httpbin.org/post', data={'foo':['bar', 'eggs']})
print(response.json()['form'])
print('|'*10)
response = requests.post('https://httpbin.org/post', data=[('foo', 'bar'), ('foo', 'eggs')])
print(response.json()['form'])
# Вывод:

{'foo': ['bar', 'eggs']}

||||||||||

{'foo': ['bar', 'eggs']}

Если нужно отправить данные, не закодированные как данные формы, то передайте в запрос строку вместо словаря. Тогда данные отправятся в изначальном виде.

import requests

response = requests.post('https://httpbin.org/post', data={'foo': 'bar'})
print('URL:', response.request.url)
print('Body:', response.request.body)
print('-' * 10)
response = requests.post('https://httpbin.org/post', data='foo=bar')
print('URL:', response.request.url)
print('Body:', response.request.body)
# Вывод:

URL: https://httpbin.org/post

URL: https://httpbin.org/post

Body: foo=bar

----------

URL: https://httpbin.org/post

Body: foo=bar

Post отправка multipart encoded файла

Запросы упрощают загрузку файлов с многостраничным кодированием (Multipart-Encoded):

import requests

url = 'https://httpbin.org/post'

files = {'file': open('report.xls', 'rb')}

response = requests.post(url, files=files)

print(response.text)

# Вывод:

{

...

"files": {

"file": "<censored...binary...data>"

},

...

}

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

import requests

url = 'https://httpbin.org/post'

files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})}

response = requests.post(url, files=files)

print(response.text)

# Вывод:

{

...

"files": {

"file": "<censored...binary...data>"

},

...

}

Можете отправить строки, которые будут приняты в виде файлов:

import requests

url = 'https://httpbin.org/post'

files = {'file': ('report.csv', 'some,data,to,send\nanother,row,to,send\n')}

response = requests.post(url, files=files)

print(response.text)

# Вывод:

{

...

"files": {

"file": "some,data,to,send\\nanother,row,to,send\\n"

},

...

}

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

Возможно, наиболее важные данные (первые – уж точно), которые вы можете получить, используя библиотеку requests, является код состояния ответа.

Так, 200 статус означает, что запрос выполнен успешно, тогда как 404 статус означает, что ресурс не найден.

Важнее всего то, с какой цифры начинается код состояния:

  • 1XX — информация
  • 2XX — успешно
  • 3XX — перенаправление
  • 4XX — ошибка клиента (ошибка на нашей стороне)
  • 5XX — ошибка сервера (самые страшные коды для разработчика)

Используя атрибут .status_code можно получить статус, который вернул сервер:

import requests

response = requests.get('https://www.google.ru/')
print(response.status_code)

# Вывод:

200

.status_code вернул 200 — это означает, что запрос успешно выполнен и сервер вернул запрашиваемые данные.

При желании, такую информацию можно применить в Вашем Пайтон скрипте для принятия решений:

import requests

response = requests.get('https://www.google.ru/')
if response.status_code == 200:    print('Успех!')elif response.status_code == 404:    print('Страница куда-то пропала…')

# Вывод:

Успех!

Если код состояния response равен 200, то скрипт выведет «Успех!», но, если он равен 404, то скрипт вернёт «Страница куда-то пропала…».

Если применить модуль Response в условном выражении и проверить логическое значение его экземпляра (if response) то он продемонстрирует значение True, если код ответа находится в диапазоне между 200 и 400, и False во всех остальных случаях.

Упростим код из предыдущего примера:

import requests

response = requests.get('https://www.google.ru/fake/')
if response:
print('Успех!')
else:
print('Хьюстон, у нас проблемы!')
# Вывод:

Хьюстон, у нас проблемы!

Данный способ не проверяет, что код состояния равен именно 200.
Причиной этого является то, что response с кодом в диапазоне от 200 до 400, такие как 204 и 304, тоже являются успешными, ведь они возвращают обрабатываемый ответ. Следовательно, этот подход делит все запросы на успешные и неуспешные – не более того. Во многих случаях Вам потребуется более детальная обработка кодов состояния запроса.

Вы можете вызвать exception, если requests.get был неудачным. Такую конструкцию можно создать вызвав .raise_for_status() используя конструкцию try- except:

import requests

from requests.exceptions import HTTPError

for url in ['https://www.google.ru/', 'https://www.google.ru/invalid']:
try:
response = requests.get(url)

response.raise_for_status()
except HTTPError:
print(f'Возникла ошибка HTTP: {HTTPError}')
except Exception as err:
print(f'Возникла непредвиденная ошибка: {err}')
else:
print('Успех!')
# Вывод:

Успех!

Возникла ошибка HTTP: <class 'requests.exceptions.HTTPError'>

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

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

import requests

response = requests.get('https://www.google.ru/')
print(response.headers)
# Вывод:

{'Date': 'Sun, 27 Jun 2021 13:43:17 GMT', 'Expires': '-1', 'Cache-Control': 'private, max-age=0', 'Content-Type': 'text/html; charset=windows-1251', 'P3P': 'CP="This is not a P3P policy! See g.co/p3phelp for more info."', 'Content-Encoding': 'gzip', 'Server': 'gws', 'X-XSS-Protection': '0', 'X-Frame-Options': …

Cookies

Можно просмотреть файлы cookie, которые сервер отправляет вам обратно с помощью атрибута .cookies. Запросы также позволяют отправлять свои собственные cookie-файлы.

Чтобы добавить куки в запрос, Вы должны использовать dict, переданный в параметр cookie.

import requests

url = 'https://www.google.ru/'
headers = {'user-agent': 'your-own-user-agent/0.0.1'}
cookies = {'visit-month': 'February'}

response = requests.get(url, headers=headers, cookies=cookies)

print(response.request.headers)
# Вывод:

{'user-agent': 'your-own-user-agent/0.0.1', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Cookie': 'visit-month=February'}

Редиректы и история

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

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

Например, GitHub перенаправляет все запросы HTTP на HTTPS:

import requests

response = requests.get('https://www.google.ru/')
print(response.url)
print(response.status_code)
print(response.history)
# Вывод:

https://www.google.ru/

200

[]

Тайм ауты

Так же легко можно управлять тем, сколько программа будет ждать возврат response. Время ожидания задаётся параметром timeout. Это очень важный параметр, так как, если его не использовать, написанный Вами скрипт может «зависнуть» в вечном ожидании ответа от сервера. Используем предыдущий код:


import requests

response = requests.get(‘https://www.google.ru/’, timeout=0.001)
print(response.url)
print(response.status_code)
print(response.history)
# Вывод:

raise ConnectTimeout(e, request=request)

requests.exceptions.ConnectTimeout: HTTPSConnectionPool(host=’www.google.ru’, port=443): Max retries exceeded with url: / (Caused by ConnectTimeoutError(<urllib3.connection.HTTPSConnection object at 0x000001E331681C70>, ‘Connection to www.google.ru timed out. (connect timeout=0.001)’))

Модуль не ждёт полной загрузки ответа. Исключение возникает, если сервер не отвечает (хотя бы один байт) за указанное время.

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

Если возникнет непредвиденная ситуация – ошибка соединения, модуль Requests выбросит эксепшн ConnectionError.

response.raise_for_status() возвращает объект HTTPError, если в процессе произошла ошибка. Его применяют для отладки модуля и, поэтому, он является неотъемлемой частью запросов Python.

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

 

Оцените статью
О Python на русском языке
Добавить комментарий

Adblock
detector