Библиотека requests Python с нуля: первый GET-запрос
Первый GET-запрос на requests: что улетает по сети, что возвращается и как разобрать ответ на статус, заголовки и тело — механику проверяем прямо в браузере.
Редакция Питоники
Твой скрипт хочет знать курс валют, погоду в Сочи и есть ли обновление программы. Все эти данные лежат не у тебя на диске — они на чужих серверах. Значит, программе нужно уметь одно: попросить. В Python стандарт де-факто для просьб — библиотека requests: её скачивали со PyPI больше миллиарда раз, и почти каждый туториал по API начинается с import requests. В этом уроке мы отправим первый GET-запрос и заглянем внутрь ответа так, чтобы он перестал быть чёрным ящиком.
Одна честная оговорка сразу. В браузерном интерпретаторе этой страницы сети нет — поэтому сам requests здесь не импортируется. Мы не будем делать вид, что это не так: механику запроса и ответа разберём на настоящих стандартах Python, которые исполняются по-настоящему, а реальный код requests будет рядом — скопировал, и он работает на твоём компьютере. Такой формат мы уже применяли в уроке про API и REST.
Что происходит, когда одна программа просит данные у другой
В сети всегда разговаривают двое. Клиент — тот, кто просит: браузер, мобильное приложение, твой Python-скрипт. Сервер — тот, кто отвечает: машина где-то в дата-центре, на которой крутится программа с данными. Разговаривают они на протоколе HTTP — строгом текстовом языке, где у каждой стороны свой формат реплик: клиент отправляет запрос (request), сервер возвращает ответ (response).
Запрос — это объявление о поиске: метод (что хочу сделать), адрес (где искать) и заголовки (служебные пометки). Ответ — найденное: статус (как дела), заголовки (как читать вложение) и тело (само содержимое). requests нужен как раз для того, чтобы не собирать эти реплики руками.
GET /weather HTTP/1.1
Host: api.pythonika.ru
User-Agent: python-requests/2.32
Accept: */*
Первая строка — самая важная: GET — метод («хочу прочитать»), /weather — путь («вот что»), HTTP/1.1 — версия протокола. Ниже — заголовки: Host говорит, к какому сайту обращаемся, User-Agent представляется («это requests, версия такая-то»), Accept предупреждает, что клиент согласен на любой формат ответа. Тела у GET-запроса нет — читать нечего передавать.
HTTP/1.1 200 OK
Content-Type: application/json
Server: nginx
{"city": "Москва", "temp": 12}
Смотри, как симметрично. Первая строка ответа — статус-строка: протокол, код 200 и человеческая подпись OK («всё получилось»). Дальше заголовки — теперь уже сервер представляется: Content-Type сообщает, что в теле JSON, Server честно называет своё имя. Потом пустая строка — и тело: сами данные. HTTP-ответ — это обычный текст из трёх частей: статус-строка, заголовки и тело. requests просто аккуратно его распаковывает.
Как разобрать ответ по строкам — механика requests
Прежде чем доверять библиотеке, собери её работу руками. Сырой ответ — строка, а у строк Python есть partition и split. Следующий блок исполняется прямо на странице: он разбирает тот самый ответ по-настоящему.
# Сырой HTTP-ответ - обычный текст. Разберём его, как это делает requests.
raw = (
"HTTP/1.1 200 OK\n"
"Content-Type: application/json\n"
"Server: nginx\n"
"\n"
'{"city": "Москва", "temp": 12}'
)
head, _, body = raw.partition("\n\n")
lines = head.split("\n")
status = lines[0]
headers = {}
for line in lines[1:]:
name, _, value = line.partition(": ")
headers[name] = value
protocol, code, phrase = status.split(" ")
print("Протокол:", protocol)
print("Код:", code)
print("Фраза:", phrase)
print("Заголовков:", len(headers))
print("Content-Type:", headers["Content-Type"])
print("Тело:", body)
Протокол: HTTP/1.1
Код: 200
Фраза: OK
Заголовков: 2
Content-Type: application/json
Тело: {"city": "Москва", "temp": 12}Что здесь произошло. partition("\n\n") отрезает голову (статус и заголовки) от тела по пустой строке — так устроен сам протокол. Первая строка головы делится по пробелам на протокол, код и фразу. Остальные строки — заголовки: каждый режется по первому : и складывается в словарь, потому что обращаться к заголовку по имени (headers["Content-Type"]) удобнее, чем искать его по тексту. Ровно эту работу ты получаешь бесплатно, когда вызываешь requests — плюс кучу деталей вроде кодировок и повторных попыток.
Что делает requests.get?
requests.get(url) — это «собери GET-запрос к этому адресу, отправь по сети, дождись ответа и верни его мне». Возвращается не строка и не словарь, а объект Response — посылка, в которой лежит весь ответ целиком: код в .status_code, заголовки в .headers, тело текстом в .text. Посмотрим, что requests отправляет по сети, когда ты пишешь эту одну строку, — и соберём запрос сами:
# Что requests отправляет в сеть, когда ты пишешь requests.get(url)
url = "https://api.pythonika.ru/weather"
host = url.split("/")[2]
path = "/" + "/".join(url.split("/")[3:])
print("GET " + path + " HTTP/1.1")
print("Host: " + host)
print("User-Agent: python-requests/2.32")
print("Accept: */*")
GET /weather HTTP/1.1 Host: api.pythonika.ru User-Agent: python-requests/2.32 Accept: */*
URL сам содержит всё, что нужно для запроса: хост после схемы становится заголовком Host, путь — путём в первой строке. requests заодно дописывает несколько служебных заголовков от себя. Запрос готов; дальше его понесла бы сеть.
import requests
response = requests.get("https://api.pythonika.ru/weather", timeout=5)
print(response.status_code) # 200
print(response.text) # {"city": "Москва", "temp": 12}
Вот и весь первый GET-запрос: импорт, вызов, чтение полей. Обрати внимание на timeout=5 — это «жди ответа не дольше пяти секунд». Привычка ставить таймаут с первого запроса сэкономит тебе не один вечер зависшего скрипта; в восьмом уроке курса этому посвящён целый разговор.
Установка requests: pip install
Чтобы повторить всё по-настоящему, поставь библиотеку. Открой терминал (не интерпретатор Python!) и выполни:
pip install requests
python -c "import requests; print(requests.__version__)"
Successfully installed requests-2.32.5 2.32.5
Если работаешь в виртуальном окружении (а это правильная привычка), ставь внутрь него: python -m pip install requests — тогда пакет гарантированно попадёт к тому интерпретатору, которым ты запускаешь скрипт. Ошибка ModuleNotFoundError: No module named 'requests' почти всегда означает ровно это: pip установил библиотеку в один Python, а запускаешь ты другим.
Ответ в руках: status_code, headers, text
Теперь соберём объект Response сами — с тем же набором полей, что у настоящего, но без сети. Знакомься: FakeResponse, каркас, на котором мы будем тренировать работу с ответами во всех уроках раздела. Он маленький, честный и исполняется на странице.
import json
class FakeResponse:
"""Каркас ответа requests: те же поля, но без сети."""
def __init__(self, status_code, text, headers=None):
self.status_code = status_code
self.text = text
self.headers = headers or {}
def json(self):
return json.loads(self.text)
@property
def ok(self):
return self.status_code < 400
r = FakeResponse(200, '{"city": "Москва", "temp": 12}')
print(r.status_code)
print(r.ok)
print(r.json()["temp"])
print(len(r.text))
200 True 12 30
Смотри, что у ответа есть. status_code — код 200, успех. ok — удобное свойство «код меньше 400», о нём подробно пойдёт речь в уроке про коды ответа. json() превращает тело в словарь одним вызовом — так requests делает json.loads за тебя. И даже len(r.text) работает: тело — обычная строка длиной 30 символов, включая кавычки и скобки.
Чем .text отличается от .json()?
Самая частая путаница новичка — этих двух доступа к телу. Ответ держит тело один раз, строкой — это .text. Всё остальное — производные: .json() — это json.loads(r.text), то есть словарь, распакованный из той же строки.
import json
class FakeResponse:
def __init__(self, status_code, text, headers=None):
self.status_code = status_code
self.text = text
self.headers = headers or {}
def json(self):
return json.loads(self.text)
body = '{"rates": {"USD": 92.5, "EUR": 100.1}}'
r = FakeResponse(200, body, headers={"Content-Type": "application/json"})
print(r.text)
print(r.json()["rates"]["EUR"])
print(type(r.text).__name__)
print(type(r.json()).__name__)
{"rates": {"USD": 92.5, "EUR": 100.1}}
100.1
str
dictПравило простое: .text — когда хочешь посмотреть или сохранить тело как есть (HTML страницы, лог, сырой JSON), .json() — когда тело заведомо JSON и ты хочешь сразу работать с данными. Вызвать .json() на теле, которое не JSON, — ошибка json.decoder.JSONDecodeError, и это будет честно.
Заголовки ответа: словарь подсказок
Заголовки ответа — это записка сервера о том, как читать вложение: что за формат, какая кодировка, когда устареет. В requests они лежат в .headers как словарь, и по имени можно спросить любой:
class FakeResponse:
def __init__(self, status_code, text, headers=None):
self.status_code = status_code
self.text = text
self.headers = headers or {}
r = FakeResponse(
200,
"<h1>Привет</h1>",
headers={"Content-Type": "text/html; charset=utf-8", "Server": "nginx"},
)
for name, value in r.headers.items():
print(name + ":", value)
print("Это HTML?", "html" in r.headers["Content-Type"])
Content-Type: text/html; charset=utf-8 Server: nginx Это HTML? True
Заголовок Content-Type — главный: он говорит, что в теле. application/json — вызывай .json(), text/html — это разметка страницы, неси её в BeautifulSoup. Именно по этому заголовку ты решаешь, как обрабатывать ответ, а не по расширению в URL — у API-адресов расширений обычно нет вообще.
| Что в ответе | Где лежит в requests | Тип в Python |
|---|---|---|
| Код ответа | response.status_code | int |
| Человеческая причина | response.reason | str |
| Заголовки | response.headers | словареподобный объект |
| Тело текстом | response.text | str |
| Тело как JSON | response.json() | dict или list |
| Итоговый адрес запроса | response.url | str |
Почему учить requests без сети — не потеря времени?
Потому что сеть — самая скучная часть этой библиотеки. Вся мудрость requests — не в том, как открыть сокет, а в том, что делать с ответом: как прочитать статус, закодировать параметры, не проглотить ошибку, вытащить JSON. Эти навыки тренируются на строках и словарях — то есть на том, что исполняется где угодно, включая страницу самоучителя.
Схема раздела такая: в каждом уроке настоящий код requests показывается как есть — его ты запустишь дома, а рядом идут исполняемые блоки, которые вскрывают механику. Сегодня ты разобрал ответ по строкам и собрал запрос из URL. Дальше — параметры GET-запроса и query string: как requests превращает словарь в хвост адреса вида ?city=Moscow&days=3, и почему русские буквы в URL превращаются в проценты. Полный маршрут — самоучитель requests.
Requests — элегантная и простая HTTP-библиотека для Python, созданная для людей.
— Кеннет Райтц, автор requests
Сначала предскажи ответ в голове — это главный навык программиста.
line = "HTTP/1.1 404 Not Found"
parts = line.split(" ")
print(parts[1], len(parts))
import json
raw = '{"lang": "Python", "year": 1991}'
data = json.loads(raw)
print(data["lang"], data["year"] > 1990)
1. Какой HTTP-метод означает «прочитать данные, ничего не меняя»?
2. Что возвращает requests.get(url)?
3. В первой строке ответа «HTTP/1.1 404 Not Found» что означает число 404?
4. Как прочитать тело ответа как обычный текст?
5. Почему в браузерном интерпретаторе урока не работает import requests?
Собери ответ сервера вручную на каркасе FakeResponse (он уже в редакторе): создай ответ с кодом 200 и телом {"city": "Сочи", "temp": 24}, затем выведи четыре строки: status_code, ok, город из json() и длину тела в символах.
Что такое requests Python простыми словами?
requests — библиотека, которая отправляет HTTP-запросы и принимает ответы в пару строк: requests.get(url) — и у тебя в руках ответ сервера с кодом, заголовками и телом. Библиотека прячет сложность протокола: соединение, заголовки, кодировки, перенаправления. Слоган проекта так и звучит — «HTTP for Humans».
Как установить библиотеку requests?
В терминале: pip install requests. Если работаешь в виртуальном окружении — python -m pip install requests, чтобы пакет точно попал к нужному интерпретатору. Проверка установки: python -c "import requests; print(requests.__version__)".
Почему import requests не работает в браузерном интерпретаторе?
Код на странице исполняется в Pyodide — Python, собранном в WebAssembly внутри браузера. Браузерная песочница не имеет доступа к сети, поэтому HTTP-библиотеке там просто нечего делать. Уроки раздела устроены честно: механику запросов исполняем на настоящих стандартах Python, а код requests даём готовым — для запуска на своём компьютере.
Чем requests отличается от urllib из стандартной библиотеки?
urllib — набор низкоуровневых инструментов: соединение, заголовки и чтение ответа собираешь руками. requests делает то же самое в человеческом API: JSON одной строкой, автоматическое декодирование, сессии и cookies из коробки. При этом requests — надстройка над теми же механизмами, поэтому понимание urllib только помогает.
Понравился урок? Сошлитесь на него
«HTTP-ответ — это обычный текст из трёх частей: статус-строка, заголовки и тело. requests просто аккуратно его распаковывает.»
Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.
Что читать дальше
requests · Урок 2
Параметры GET-запроса: params и query string в requests
Query string глазами Python: словарь params превращается в ?city=Moscow&days=3, русские буквы — в проценты, а parse_qs разворачивает всё обратно. Каждый шаг исполняется на странице.
FastAPI · Урок 1
Что такое API и REST: введение в FastAPI для начинающих
Понять, что такое API, REST, HTTP-методы и JSON — и подготовиться к первому приложению на FastAPI.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
json · Урок 13
Что отдаёт API: ответ как текст
API отвечает текстом со статусом и заголовками: берём заранее сохранённый настоящий ответ — и учимся мерить его длину, искать подстроки и превращать байты в строку.
ответ api json текстhttp ответ структура
BeautifulSoup / Scrapy · Урок 2
Первый парсер: requests и BeautifulSoup — скачиваем и разбираем страницу
Собираем первого рабочего парсера по схеме «скачать — разобрать — достать»: requests скачивает страницу, BeautifulSoup превращает её в объект, из которого данные достаются в одну строку.
beautifulsoup первый парсерrequests python
requests · Урок 3
Коды ответа HTTP: status_code, ok и raise_for_status в requests
Семейства кодов ответа, свойство ok и метод raise_for_status: узнаём, что сервер на самом деле сказал, и ловим HTTPError — механику проверяем прямо на странице.
requests status_codeкод ответа http