Библиотека Requests в Python, HTTP-запросы

Статьи

Библиотека Requests в Python, HTTP-запросы

Автор: Рамис, 05 сентября 2021

Библиотека Requests

Модуль Requests предоставляет возможность управления HTTP-запросами при помощи языка Python. Инструментарий библиотеки широкий и рассчитан на все случаи взаимодействия с web-приложениями. Код, написанный с применением Requests, не является громоздким, легко читается, а функции и методы наглядно настраиваются под специфические нужды.

Несмотря на то, что в Python встроен модуль urllib3, обладающий сходным функционалом, практически все применяют Requests, что свидетельствует о его удобстве и простоте.

Основные возможности библиотеки Requests

Модуль разработан с учетом потребностей современных web-разработчиков и актуальных технологий. Многие операции автоматизированы, а ручные настройки сведены к минимуму.

Для понимания инструментария библиотеки перечислим ее основные возможности:
- поддержка постоянного HTTP-соединения и его повторное использование;
- применение международных и национальных доменов;
- использование Cookie: передача и получение значений в формате ключ: значение;
- автоматическое декодирование контента;
- SSL верификация;
- аутентификация пользователей на большинстве ресурсов с сохранением;
- поддержка proxy при необходимости;
- загрузка и выгрузка файлов;
- стриминговые загрузки и фрагментированные запросы;
- задержки соединений;
- передача требуемых заголовков на web-ресурсы и др.

В целом, практически любая задача, которая возникает у разработчика, нашла свое отражение в коде библиотеки. Важно понимать, что Requests не предназначен для парсинга ответа сервера (для этого применяют другие модули, например, Beautiful Soup).

Первичная настройка и начало работы

Первым делом установим библиотеку в требуемую папку (через консоль).

Консоль/Терминал

pip install requests # Для Windows
pip3 install requests # Для Linux, MacOS

Эта команда установит Requests и дополнительные зависимые модули.

Полезно знать

Согласно рекомендациям официальной документации Python можно применять и другой способ установки библиотек, который позволяет контролировать версию языка, запускать библиотеку напрямую из консоли.

python -m pip install requests # Установит библиотеку для последней версии Python
python3.7 -m pip install requests # Requests для Python 3.7

Чтобы проверить, что все прошло успешно, сделаем простой GET запрос на сайт https://github.com.

Пример

>>> import requests
>>> response = requests.get('https://github.com')
>>> response
<Response [200]>

Никаких ошибок не получено, следовательно, запрос прошел успешно. Ответ от сервера сохраняем в объект response. В нем содержится вся необходимая информация для анализа, которую нам предоставил сервис. Рассмотрим основные атрибуты данного объекта (в примерах ниже часть ответов сервера будет вырезана, чтобы не загромождать пространство).

Пример

>>> dir(response)
[…, 'apparent_encoding', 'close', 'connection', 'content', 'cookies', 'elapsed', 'encoding', 'headers', 'history', 'is_permanent_redirect', 'is_redirect', 'iter_content', 'iter_lines', 'json', 'links', 'next', 'ok', 'raise_for_status', 'raw', 'reason', 'request', 'status_code', 'text', 'url']

К любому из этих атрибутов можно обратиться, чтобы получить более детальную информацию.

Атрибут 'headers'

Пример

>>> response.headers
{'Server': 'GitHub.com', 'Date': 'Fri, 05 Mar 2021 09:47:19 GMT', 'Content-Type': 'text/html; charset=utf-8', …}

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

Атрибуты: 'status_code', 'ok'

Пример

>>> response.status_code
200
>>> response.ok
True

Код статуса 200 говорит о том, что запрашиваемый сайт обнаружен, и мы от него получили успешный ответ.

Важно знать

Коды HTTP-ответов показывают успешность выполнения запросов к серверу. Их группируют по 5 классам:
- информационного характера (100-199) – разного рода подсказки, призывы к продолжению промежуточных действий
- успешные (200-299) – запрос обработан, ответ получен, содержимого нет, но присланы заголовки и т.п.
- коды перенаправления (300-399) – пользователь перенаправляется по другой ссылке
- ошибки на стороне клиента (400-499) – пользователь не авторизован, нет такой страницы, запрещен доступ и др.
- ошибки на сервере (500-599) – сервис не доступен по каким-либо причинам, метод запроса им не поддерживается.

Благодаря возможности проверять статус ответа, мы можем по-разному реагировать исходя из этого. Свойство ok более общее. Удобно, когда нужно убедиться, что все хорошо (под этим понимаются ответы с кодами от 200 до 399).

Атрибуты: 'url', 'request'

Пример

>>> response.request
<PreparedRequest [GET]>
>>> response.url
'https://github.com/'

Приведенные свойства предоставляют информацию о методе запроса (GET, POST и др.) и запрашиваемой ссылке со всеми дополнительными параметрами.

Атрибуты: 'text', 'content', 'json'

Пример

>>> response.text
'\n\n\n\n\n<!DOCTYPE html>\n<html lang="en"  class="html-fluid">\n  <head>\n    <meta charset="utf-8">\n  <link rel="dns-prefetch" href="https://github.githubassets.com">\n  <link rel="dns-prefetch" href="https://user-images.githubusercontent.com/">…
>>> response.content
b'\n\n\n\n\n<!DOCTYPE html>\n<html lang="en"  class="html-fluid">\n  <head>\n   <meta charset="utf-8">\n  <link rel="dns-prefetch" href="https://github.githubassets.com">\n  <link rel="dns-prefetch" href="https://avatars.githubusercontent.com">…
>>> response.json()
Error…

Свойство text показывает тело ответа сервера в текстовом формате (актуально для html-страниц), content – результат в виде байтов (удобно при скачивании графической, аудио- или видеоинформации), метод json() приводит содержимое ответа к обычному словарю (если данные к нему приводимы, в противном случае будет ошибка, как в примере; актуально для API-запросов).

Разные типы HTTP-запросов

Для получения содержимого web-страницы используется метод GET, работу которого мы показали выше. Библиотека Requests позволяет обращаться к сервисам с помощью других запросов, если они разрешены на сервере. Чтобы полноценно продемонстрировать его работу, разработчики библиотеки создали специальный сайт https://httpbin.org/, на котором можно их опробовать.

HTTP методы

Метод OPTIONS

С помощью данного метода мы увидим принимаемые ресурсом или конкретным его разделом HTTP-запросы (просмотр опций включен не у всех ресурсов).

Пример

>>> response = requests.options('https://httpbin.org/')
>>> response.headers['Access-Control-Allow-Methods']
GET, POST, PUT, DELETE, PATCH, OPTIONS

Как видим, на сайте имеется возможность протестировать практически все виды HTTP-запросов.

Метод GET

У GET-запросов могут присутствовать параметры, которыми легко управлять. Например: https://site.ru/?text=python&lang=ru (ищем сайты про Python на русском языке на некоем условном поисковике). Для этого есть именованный аргумент params. Предположим, требуется 11 страница в категории автомобили.

Пример

>>> get_params = {'page': 11, 'product': 'car'}
>>> response = requests.get('https://httpbin.org/', params=get_params)
>>> response.url
'https://httpbin.org/?page=11&product=car'

Ссылка успешно сформирована и передана на сервис.

Метод POST

Для отправки данных (например, форм) применяют метод POST библиотеки Requests. В аргументе data указываются все требуемые поля. Ответом будет json-объект с переданными данными, а также ряд иных сведений (заголовки, ip-адрес, ссылка).

Пример

>>> post_params = {'user': 'admin', 'password': 'admin_pass1'}
>>> response = requests.post('https://httpbin.org/post', data=post_params)
>>> response.json()['form']
{'password': 'admin_pass1', 'user': 'admin'}

Метод HEAD

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

Пример

>>> response = requests.head('https://httpbin.org/')
>>> response.headers
{'Date': 'Sat, 06 Mar 2021 11:40:49 GMT', 'Content-Type': 'text/html; charset=utf-8', 'Content-Length': '9593', 'Connection': 'keep-alive', 'Server': 'gunicorn/19.9.0', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true'}

Метод PUT

Представленный метод является идемпотентным. Это значит, что повторная отправка идентичных данных никак не повлияет на работу ресурса. Если использовать метод POST, то возможны ошибки.

Пример

>>> put_params = {'user': 'admin', 'password': 'admin_pass1'}
>>> response = requests.put('https://httpbin.org/put', data=put_params)
>>> response.status_code
200
>>> response = requests.put('https://httpbin.org/', data=put_params)
>>> response.status_code
405

В первом случае запрос отработал удачно (о чем говорит код 200), тогда как во втором – нет (405 код – неразрешенный метод по указанному адресу).

Метод PATCH

Предполагает частичное обновление данных на сервере.

Пример

>>> patch_params = {'user': 'new_admin', 'password': 'new_pass'}
>>> response = requests.patch('https://httpbin.org/patch', data=patch_params)
>>> response.status_code
200

Чаще всего применяется для изменения содержимого конфигурационных файлов (например, вы поменяли токен для доступа к API).

Метод DELETE

Когда требуется удаление некоего ресурса.

Пример

>>> del_params = {"name": "Николай", "job": "Повар"}
>>> response = requests.delete('https://httpbin.org/delete', data=del_params)
>>> response.json()['form']
{'job': 'Повар', 'name': 'Николай'}

Практика: скачивание файлов

Предположим, перед нами стала задача скачать картинку с определенного сайта и сохранить ее к себе. Проще, конечно, это сделать руками, но когда у вас будет список из хотя бы 100 фотографий, проблема автоматизации будет очевидной. Продемонстрируем 2 способа применения библиотеки Requests для загрузки фото.

Способ 1 – Единовременная загрузка изображения

Пример

import requests

response = requests.get('https://www.hdwallpapers.in/download/colorful_glowing_shape_lines_4k_8k_hd_abstract-HD.jpg')
with open('img.jpg', 'wb') as picture:
    picture.write(response.content)

Скачивание изображения с помощью библиотеки requests

Фото специально выбрано большого размера для теста. Минус первого способа таков: если на сервере произойдет ошибка во время скачивания, то все что вы делали, потеряется. Более того, сам файл займет солидную часть в памяти (а если это фильм на 50 Гб?). Достаточно неудобно.

Способ 2 – Запись файла частями

Пример

import requests

response = requests.get('https://www.hdwallpapers.in/download/colorful_glowing_shape_lines_4k_8k_hd_abstract-HD.jpg',
                        stream=True)
with open('img.jpg', 'wb') as picture:
    for chunk in response.iter_content(chunk_size=10000):
        if chunk:
            picture.write(chunk)

Во-первых, мы добавили параметр stream=True к методу get() . Это гарантирует постепенную загрузку фотографии, что не позволит переполнять память. Метод iter_content() скачивает картинку частями (в нашем случае – по 10 Кб). Таким образом, мы «дописываем» изображение новыми кусками на каждой итерации, а если связь с сервером будет потеряна, то на ПК останется хотя бы часть фото.

Практика: авторизация на сайте

Так как на большинстве сайтов авторизация является защищенной и сложной, использовать модуль Requests не всегда получится. В некоторых случаях потребуется ключ для API того или иного ресурса.

Рассмотрим https://api.github.com, который разрешает аутентификацию через токен. Его можно получить на сайте в настройках вашего персонального профиля:

Страница получения токена GitHub

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

Пример логина и токена GitHub

Логин: smartiqa-user
Токен: 3b7cbc8bbe0c7866ccd003c1ba21add68f6ddb31

Пример

>>> import requests
>>> response = requests.get('https://api.github.com/user', auth=('smartiqa-user', '3b7cbc8bbe0c7866ccd003c1ba21add68f6ddb31'))
>>> response.json()
{'login': 'smartiqa-user', 'id': 713123083, …'type': 'User', 'site_admin': False, 'name': 'Smartiqa Programming and QA', 'company': None, 'blog': '', 'location': 'Russia'}

Часть информации вырезана, для краткости. В ответе мы получим такие данные: логин, имя, данные об аккаунте, аватарке, подписчиках, правах, времени создания и т.п.

Если обратиться по другой ссылке (например, посмотреть все репозитории пользователя), API предоставит следующие сведения:

Пример

>>> response = requests.get('https://api.github.com/user/repos', auth=('smartiqa-user', '3b7cbc8bbe0c7866ccd003c1ba21add68f6ddb31'))
>>> response.json()
[{'id': 281217463, 'node_id': 'MDEwOlJlcG9zaXRvcnkyODEyMTc0NjM=', 'name': 'blog', 'full_name': 'smartiqaorg/blog', 'private': True, 'owner': {'login': 'smartiqaorg', 'id': 44243438, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjQ0MjQzNDM4', 'avatar_url': 'https://avatars.githubusercontent.com/u/44243438?v=4',
...
'archived': False, 'disabled': False, 'open_issues_count': 0, 'license': None, 'forks': 0, 'open_issues': 0, 'watchers': 0, 'default_branch': 'main', 'permissions': {'admin': True, 'push': True, 'pull': True}}]

Ответ занимает несколько страниц (здесь приводится только часть, так как он большой по объему), если у вас имеется хотя бы 2-3 репозитория: названия, ссылки, комментарии и исправления, обновление, структура и т.п.

Практика: Сессии и прокси

Сайты, особенно высоконагруженные, не очень любят видеть на своих страницах роботов (если они не одобрены, для чего часто внедряют API). Те, кто сталкивался с парсингом ресурсов, могли не раз убедиться, что со временем сервисы закрывают доступ по вашему IP или на основании пересылаемых модулем Requests данных. В каждом случае решение проблемы индивидуально. Однако имеются базовые приемы, снижающие вероятность блокировки работы вашего скрипта.

Сделаем стандартный запрос на главную страницу сайта https://www.python.org.

Пример

>>> import requests
>>> response = requests.get('https://www.python.org/')
>>> response.request.headers
{'User-Agent': 'python-requests/2.25.1', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}

Строка response.request.headers отображает отправляемые нами заголовки на сайт. В качестве юзер-агента указана библиотека requests, что не очень хорошо, так как любой нормальный сервис вас сразу же забанит при частых запросах. Его стоит поменять на реальный, как у «живого» человека.

Пример

>>> import requests
>>> user_agent = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                            'Chrome/89.0.4389.72 Safari/537.36'}
>>> response = requests.get('https://www.python.org/', headers=user_agent)
>>> response.request.headers
{'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}

Как видим, сервер теперь думает, что мы вошли через браузер Chrome, например. Это не гарантирует отсутствие блокировки, но уменьшает ее шансы.

На практике можно дополнительно пользоваться сессией и прокси-серверами:

  1. Сессии помогают сайту «запоминать» нас (со всеми параметрами), чтобы при каждом новом обращении к нему (отдельным страницам) не отправлять все заголовки заново, с нуля.
  2. Прокси подменяют наш IP-адрес на другой, который пока что не находится в «черном списке». Следует понимать, что их использование не всегда имеет негативный оттенок. В частности, они могут ускорять маршрутизацию к ресурсам (которые находятся на другом конце планеты), обходить блокировку отдельных провайдеров или стран (когда требуется посетить ресурс, недоступный через ваш IP).

Схема работы прокси-сервера

В коде ниже ответы сервиса изменены в целях безопасности (на своем ПК вы увидите реальные значения).

Пример

>>> import requests
>>> user_agent = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                            'Chrome/89.0.4389.72 Safari/537.36'}
>>> proxy = {'http': 'http://185.253.98.21:3128',
        'https': 'http://185.253.98.21:3128'}
>>> response = requests.get('https://ramziv.com/ip')
>>> response.text
29.174.182.126
>>> session = requests.Session()
>>> session.proxies.update(proxy)
>>> session.headers.update(user_agent)
>>> response = session.get('https://ramziv.com/ip')
>>> response.text
185.253.98.21

На странице https://ramziv.com/ip можно выяснить свой IP-адрес. В первом случае мы его не меняли (отобразился реальный), а потом с помощью сессий, прокси и заголовков мы подменили свои данные.

Практика: Мультискачивание и редиректы

Ниже представлен код, позволяющий загружать любое количество картинок с учетом редиректов сайта. Воспользуемся сайтом https://picsum.photos/, который генерирует случайные изображения. Напишем функцию, чтобы скачивать требуемое количество файлов размером 200x200. Применим сессии и подмену юзер-агента.

Пример

import requests

user_agent = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                            'Chrome/89.0.4389.72 Safari/537.36'}
session = requests.Session()
session.headers.update(user_agent)


def pics_downloader (num_of_pics):
    for num in range(num_of_pics):
        response = session.get('https://picsum.photos/200', allow_redirects=True, stream=True)
        with open(f'{num}.jpg', 'wb') as picture:
            for chunk in response.iter_content(chunk_size=1024):
                if chunk:
                    picture.write(chunk)
        print(f'Скачано {num + 1} фото из {num_of_pics}')


pics_downloader(20)

Параметр allow_redirects по умолчанию включен (приведен для наглядности). Передав значение в функцию, мы скачаем требуемое количество картинок.

Таким образом, библиотека Requests не зря является одной из самых скачиваемых. Ее функционал огромен, а использование достаточно простое. Можно легко автоматизировать рутинные задачи для работы с web-сервисами, которые сэкономят массу времени.

Скажем спасибо за статью:
Автор: Михаил Макарик, Источник