Урок 7 из 8 · 20 мин · обновлено

Данные из интернета: запросы к API и JSON

Как получать данные из интернета на Python: что такое API и JSON, запросы через библиотеку requests, курсы валют ЦБ, пересчёт цен поставщика в рубли, обработка ошибок сети и сохранение данных в файл.

API и JSON

«Канцмаркет» закупает часть товаров у поставщика в Китае, цены в прайсе — в юанях. Каждую неделю кто-то открывает сайт ЦБ, смотрит курс и пересчитывает цены в калькуляторе. Поручим это скрипту.

Сайты сделаны для людей: с картинками, меню, рекламой. Программе удобнее другое — API: адрес, по которому сервис отдаёт чистые данные. Чаще всего в формате JSON. Он очень похож на словари и списки Python:

JSON
{
  "Date": "2026-10-28T11:30:00+03:00",
  "Valute": {
    "USD": {
      "CharCode": "USD",
      "Nominal": 1,
      "Name": "Доллар США",
      "Value": 80.5307,
      "Previous": 80.4909
    }
  }
}

Это кусочек ответа с курсами валют. Курсы устанавливает Центральный банк, сам ЦБ публикует их на cbr.ru в формате XML. Мы возьмём те же данные в JSON с сайта cbr-xml-daily.ru — так работать проще.

Запрос через requests

Для запросов в интернет нужна библиотека requests, мы поставили её в уроке 1.

Python
import requests

URL = "https://www.cbr-xml-daily.ru/daily_json.js"

response = requests.get(URL, timeout=10)
response.raise_for_status()          # если сервер ответил ошибкой — остановиться
data = response.json()

print("Курсы на", data["Date"][:10])
for code in ["USD", "EUR", "CNY"]:
    valute = data["Valute"][code]
    rate = valute["Value"] / valute["Nominal"]
    print(f"{code} {valute['Name']}: {rate:.2f} ₽")
Результат
Курсы на 2026-10-28
USD Доллар США: 80.53 ₽
EUR Евро: 90.10 ₽
CNY Юань: 12.07 ₽

У вас курсы будут на сегодняшний день. Что делает каждая строка:

  • requests.get(URL, timeout=10) отправляет запрос и ждёт ответа не дольше 10 секунд;
  • raise_for_status() останавливает скрипт, если сервер ответил ошибкой — например, «страница не найдена»;
  • response.json() превращает JSON в словари и списки Python;
  • data["Valute"]["CNY"] — идём по вложенным ключам, как в обычном словаре.

Nominal — за сколько единиц валюты указан курс. У доллара и юаня это 1, а у некоторых валют — 10 или 100. Поэтому курс за одну единицу — всегда Value / Nominal.

Пересчитываем прайс

Теперь пересчитаем цены поставщика:

Python
import requests

prices_cny = {"Пенал школьный": 18.5, "Набор маркеров": 32.0, "Рюкзак": 145.0}

data = requests.get("https://www.cbr-xml-daily.ru/daily_json.js", timeout=10).json()
cny = data["Valute"]["CNY"]
rate = cny["Value"] / cny["Nominal"]

for product, price in prices_cny.items():
    print(f"{product}: {price} ¥ → {price * rate:,.0f} ₽".replace(",", " "))
Результат
Пенал школьный: 18.5 ¥ → 223 ₽
Набор маркеров: 32.0 ¥ → 386 ₽
Рюкзак: 145.0 ¥ → 1 750 ₽

В формате {price * rate:,.0f} запятая разделяет разряды, а .0f округляет до целых. Python ставит между разрядами запятую — .replace(",", " ") меняет её на привычный пробел. В настоящем скрипте прайс, конечно, читается из таблицы, как в уроке 3.

Когда что-то пошло не так

Интернет ненадёжен: сервер может не ответить, сайт — переехать, Wi-Fi — пропасть. Скрипт, который работает по расписанию, должен понятно сообщать о таких проблемах, а не падать с длинной ошибкой. Все ошибки requests ловятся одним исключением:

Python
try:
    response = requests.get(URL, timeout=10)
    response.raise_for_status()
    data = response.json()
except requests.RequestException as error:
    print("Не удалось получить курсы:", error)
    raise SystemExit(1)

Если, например, адрес неправильный, скрипт напишет:

Результат
Не удалось получить курсы: 404 Client Error: Not Found for url: https://www.cbr-xml-daily.ru/no-such

raise SystemExit(1) завершает скрипт с кодом ошибки — по нему планировщик из урока 8 поймёт, что запуск не удался.

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

Попробуйте сами. Сохраните курсы всех валют за сегодня в файл курсы.json: дату и словарь «код валюты — курс за одну единицу». Добавьте обработку ошибок сети.

Решение
Python
import json
import requests

URL = "https://www.cbr-xml-daily.ru/daily_json.js"

try:
    response = requests.get(URL, timeout=10)
    response.raise_for_status()
    data = response.json()
except requests.RequestException as error:
    print("Не удалось получить курсы:", error)
    raise SystemExit(1)

rates = {code: v["Value"] / v["Nominal"] for code, v in data["Valute"].items()}
with open("курсы.json", "w", encoding="utf-8") as f:
    json.dump({"date": data["Date"][:10], "rates": rates}, f, ensure_ascii=False, indent=2)

print("Сохранено валют:", len(rates))
Результат
Сохранено валют: 54

{code: ... for code, v in ...} — генератор словаря: короткая запись цикла, который собирает новый словарь. indent=2 делает файл читаемым, а ensure_ascii=False сохраняет русские буквы как есть. Прочитать файл обратно можно через json.load(f).

Мини-тест · 4 вопроса

Проверьте себя

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

  1. Что такое API сервиса?

  2. data = response.json(). Как достать курс юаня из ответа вида {"Valute": {"CNY": {"Value": 12.07}}}?

  3. Зачем указывать timeout=10 в requests.get()?

  4. Почему у курса нужно учитывать поле Nominal?