Фильтрация ответа API: цены, рейтинги, флаги
Товары из ответа API приехали — теперь вопросы: что дешевле N, что в наличии, какая средняя цена и сколько процентов подходит под условие.
Редакция Питоники
Ответ API разобран json.loads — в прошлом уроке список товаров уже приехал в переменную items. Но сам по себе этот список бесполезен: никто не читает тысячи позиций каталога подряд. Настоящая работа начинается, когда по данным задают вопросы: что дешевле трёх тысяч, что есть в наличии, какая средняя цена, сколько вообще товаров подходит под условие.
Хорошая новость: на все эти вопросы отвечает стандартный Python — условия, списки включения и пара встроенных функций. Ставить ничего не нужно, и каждый пример ниже исполняется прямо на странице, как и весь курс.
Ответ, с которым работаем
Сети в браузерной песочнице нет, поэтому текст ответа лежит в константе — ровно в том виде, в каком его отдал бы настоящий сервер. Разбор от этого не меняется: json.loads превращает текст в словарь, дальше работаем с обычным списком словарей.
import json
API_RESPONSE = """
{
"status": "ok",
"source": "shop-api",
"items": [
{"name": "Клавиатура", "price": 4990, "rating": 4.7, "in_stock": true},
{"name": "Мышь", "price": 1290, "rating": 4.3, "in_stock": true},
{"name": "Монитор", "price": 18400, "rating": 4.8, "in_stock": false},
{"name": "Наушники", "price": 7350, "rating": 3.9, "in_stock": true},
{"name": "Веб-камера", "price": 2450, "rating": 4.1, "in_stock": false}
]
}
"""
data = json.loads(API_RESPONSE)
items = data["items"]
print(len(items), "товара в ответе")
print(items[0]["name"], "-", items[0]["price"], "руб.")
5 товара в ответе Клавиатура - 4990 руб.
Пять товаров — пять словарей с одинаковым набором полей: name, price, rating и флаг in_stock. Однообразие структуры — подарок: одно и то же условие можно применить к каждому элементу скопом, без единого ручного if.
Фильтр по цене: условие вместо ручного перебора
Фильтр по ответу API — это обычное условие if, применённое сразу ко всему списку: прошёл товар по условию, попал в выборку. Задача «выбрать дешевле 5000» решается списком включения: обходим items, оставляем только прошедших проверку. Синтаксис тот же, что в уроке про фильтрацию, — меняется только источник данных.
import json
API_RESPONSE = """
{
"status": "ok",
"source": "shop-api",
"items": [
{"name": "Клавиатура", "price": 4990, "rating": 4.7, "in_stock": true},
{"name": "Мышь", "price": 1290, "rating": 4.3, "in_stock": true},
{"name": "Монитор", "price": 18400, "rating": 4.8, "in_stock": false},
{"name": "Наушники", "price": 7350, "rating": 3.9, "in_stock": true},
{"name": "Веб-камера", "price": 2450, "rating": 4.1, "in_stock": false}
]
}
"""
items = json.loads(API_RESPONSE)["items"]
cheap = [item for item in items if item["price"] < 5000]
for item in cheap:
print(item["name"], "-", item["price"])
print("Подходящих:", len(cheap))
Клавиатура - 4990 Мышь - 1290 Веб-камера - 2450 Подходящих: 3
В выборке остались только словари, для которых условие оказалось истинным. Исходный items при этом не изменился: фильтр строит новый список, а не правит старый — это важно, когда те же данные нужны для другого вопроса.
Заодно заметь, какие типы приехали в полях: цена — целое число, рейтинг — дробное. Это соответствие типов из урока 3: JSON-число без точки становится int, с точкой — float. Сравнения вроде < и >= требуют именно чисел — и когда сервер их отдал честно, условие работает без подготовки. А вот true и false из JSON — это True и False, и с ними фильтр работает чуть иначе, о чём ниже.
import json
API_RESPONSE = """
{
"status": "ok",
"source": "shop-api",
"items": [
{"name": "Клавиатура", "price": 4990, "rating": 4.7, "in_stock": true},
{"name": "Мышь", "price": 1290, "rating": 4.3, "in_stock": true},
{"name": "Монитор", "price": 18400, "rating": 4.8, "in_stock": false},
{"name": "Наушники", "price": 7350, "rating": 3.9, "in_stock": true},
{"name": "Веб-камера", "price": 2450, "rating": 4.1, "in_stock": false}
]
}
"""
items = json.loads(API_RESPONSE)["items"]
found = [item for item in items
if item["price"] < 8000 and item["rating"] >= 4.0]
for item in found:
print(item["name"], "-", item["price"], "- рейтинг", item["rating"])
Клавиатура - 4990 - рейтинг 4.7 Мышь - 1290 - рейтинг 4.3 Веб-камера - 2450 - рейтинг 4.1
Условий может быть сколько угодно: and требует выполнения обоих, or — хотя бы одного. Так собираются настоящие выборки «дешевле 8000 и рейтинг не ниже 4»: Наушники прошли по цене, но не прошли по рейтингу — и остались за бортом.
items_v2 = [
{"name": "Мышь", "price": "1290"}, # цена приехала строкой
{"name": "Коврик", "price": "890"},
]
cheap = [item for item in items_v2 if item["price"] < 1000]
# TypeError: '<' not supported between instances of 'str' and 'int'
items_v2 = [
{"name": "Мышь", "price": "1290"},
{"name": "Коврик", "price": "890"},
]
cheap = [item for item in items_v2 if float(item["price"]) < 1000]
print(cheap[0]["name"], "-", cheap[0]["price"], "руб.")
Коврик - 890 руб.
float() превратил строку в число — и условие заработало. Заметь: в выборке цена осталась строкой «890», фильтр ничего не переписывает, он только отбирает. Если дальше с ценой нужно считать — приводи её к числу отдельно, например списком цен [float(i["price"]) for i in cheap].
Флаг in_stock: фильтр по булеву значению
Булевы поля из JSON приезжают настоящими bool: true становится True, false — False. Поэтому условие «только в наличии» — само значение поля, без всяких сравнений.
import json
API_RESPONSE = """
{
"status": "ok",
"source": "shop-api",
"items": [
{"name": "Клавиатура", "price": 4990, "rating": 4.7, "in_stock": true},
{"name": "Мышь", "price": 1290, "rating": 4.3, "in_stock": true},
{"name": "Монитор", "price": 18400, "rating": 4.8, "in_stock": false},
{"name": "Наушники", "price": 7350, "rating": 3.9, "in_stock": true},
{"name": "Веб-камера", "price": 2450, "rating": 4.1, "in_stock": false}
]
}
"""
items = json.loads(API_RESPONSE)["items"]
available = [item for item in items if item["in_stock"]]
for item in available:
print(item["name"], "-", item["price"])
print(len(available), "из", len(items), "в наличии")
Клавиатура - 4990 Мышь - 1290 Наушники - 7350 3 из 5 в наличии
Написать item["in_stock"] == True можно — результат тот же, но сравнение с True лишнее: значение уже булево, и Python понимает его напрямую. «Нет в наличии» — это not item["in_stock"], а комбинация «в наличии и недорого» собирается через and, как в прошлом разделе. Составные условия поверх флагов — половина реальных выборок: [i for i in items if i["in_stock"] and i["rating"] >= 4.5] — уже готовая витрина «лучшее из доступного».
Среднее по полю: sum и len
Средняя цена — это сумма всех значений поля, делённая на их количество. Список включения собирает цены, sum() складывает, len() считает. Настоящие вопросы к данным почти всегда такие: не «покажи всё», а «сколько в среднем».
import json
API_RESPONSE = """
{
"status": "ok",
"source": "shop-api",
"items": [
{"name": "Клавиатура", "price": 4990, "rating": 4.7, "in_stock": true},
{"name": "Мышь", "price": 1290, "rating": 4.3, "in_stock": true},
{"name": "Монитор", "price": 18400, "rating": 4.8, "in_stock": false},
{"name": "Наушники", "price": 7350, "rating": 3.9, "in_stock": true},
{"name": "Веб-камера", "price": 2450, "rating": 4.1, "in_stock": false}
]
}
"""
items = json.loads(API_RESPONSE)["items"]
prices = [item["price"] for item in items]
avg = sum(prices) / len(prices)
print("Средняя цена:", round(avg, 2))
available = [item for item in items if item["in_stock"]]
avg_stock = sum(i["price"] for i in available) / len(available)
print("Средняя цена в наличии:", round(avg_stock, 2))
Средняя цена: 6896.0 Средняя цена в наличии: 4543.33
round(avg, 2) оставляет два знака после запятой — для отчёта это то, что нужно: без округления вторая строка напечаталась бы хвостом 4543.333333333333. Обрати внимание на второй подсчёт: генератор i["price"] for i in available экономит отдельный список, когда цены нужны только для суммы.
Доля подходящих
Единицы отчёта — не только рубли, но и проценты: какая доля товаров прошла фильтр. Считается теми же len(): подходящих делим на всех и умножаем на сто.
import json
API_RESPONSE = """
{
"status": "ok",
"source": "shop-api",
"items": [
{"name": "Клавиатура", "price": 4990, "rating": 4.7, "in_stock": true},
{"name": "Мышь", "price": 1290, "rating": 4.3, "in_stock": true},
{"name": "Монитор", "price": 18400, "rating": 4.8, "in_stock": false},
{"name": "Наушники", "price": 7350, "rating": 3.9, "in_stock": true},
{"name": "Веб-камера", "price": 2450, "rating": 4.1, "in_stock": false}
]
}
"""
items = json.loads(API_RESPONSE)["items"]
cheap = [item for item in items if item["price"] < 5000]
share = len(cheap) / len(items) * 100
print("Дешёвых:", len(cheap), "из", len(items))
print("Доля:", round(share), "процентов")
Дешёвых: 3 из 5 Доля: 60 процентов
Без умножения на сто доля осталась бы десятичной дробью 0.6 — это то же число, просто другие единицы. Для людей привычнее проценты, для программ — доли: смотри, что требуется в отчёте, и не смешивай оба вида в одном документе. Считать долю можно и от другой базы: доля дешёвых среди товаров в наличии, например, — те же len(), только оба списка сначала проходят через фильтр.
Лидер по полю: max и min
Крайние значения — самый дорогой и самый дешёвый товар — достаются без сортировки: max() и min() с параметром key сами найдут лидера по полю.
import json
API_RESPONSE = """
{
"status": "ok",
"source": "shop-api",
"items": [
{"name": "Клавиатура", "price": 4990, "rating": 4.7, "in_stock": true},
{"name": "Мышь", "price": 1290, "rating": 4.3, "in_stock": true},
{"name": "Монитор", "price": 18400, "rating": 4.8, "in_stock": false},
{"name": "Наушники", "price": 7350, "rating": 3.9, "in_stock": true},
{"name": "Веб-камера", "price": 2450, "rating": 4.1, "in_stock": false}
]
}
"""
items = json.loads(API_RESPONSE)["items"]
priciest = max(items, key=lambda item: item["price"])
cheapest = min(items, key=lambda item: item["price"])
print("Дороже всех:", priciest["name"], "-", priciest["price"])
print("Дешевле всех:", cheapest["name"], "-", cheapest["price"])
Дороже всех: Монитор - 18400 Дешевле всех: Мышь - 1290
max() с key вернул целый словарь — товар-лидер, а не число: key говорит функции, по какому полю сравнивать. Полный топ-N по местам строит функция sorted — её разбор ждёт в следующем уроке.
Пустая выборка: страховка от деления на ноль
import json
API_RESPONSE = """
{
"status": "ok",
"source": "shop-api",
"items": [
{"name": "Клавиатура", "price": 4990, "rating": 4.7, "in_stock": true},
{"name": "Мышь", "price": 1290, "rating": 4.3, "in_stock": true},
{"name": "Монитор", "price": 18400, "rating": 4.8, "in_stock": false},
{"name": "Наушники", "price": 7350, "rating": 3.9, "in_stock": true},
{"name": "Веб-камера", "price": 2450, "rating": 4.1, "in_stock": false}
]
}
"""
items = json.loads(API_RESPONSE)["items"]
expensive = [item for item in items if item["price"] > 100000]
print("Найдено:", len(expensive))
if expensive:
avg = sum(i["price"] for i in expensive) / len(expensive)
print("Средняя цена:", avg)
else:
print("Пусто: считать среднее не из чего")
Найдено: 0 Пусто: считать среднее не из чего
Проверка if expensive: запускает подсчёт только для непустого списка — тот же приём защиты, что try/except вокруг битого JSON в уроке 9, только здесь она бережёт арифметику, а не разбор.
Сводка по каталогу: все приёмы в пяти строках
Настоящий отчёт — это не один ответ, а связка: сколько всего, сколько подходит, какая средняя, кто лидер. Каждый ингредиент у тебя уже есть — осталось сложить их в один словарь. Такой словарь в следующем уроке поедет в файл почти без изменений.
import json
API_RESPONSE = """
{
"status": "ok",
"source": "shop-api",
"items": [
{"name": "Клавиатура", "price": 4990, "rating": 4.7, "in_stock": true},
{"name": "Мышь", "price": 1290, "rating": 4.3, "in_stock": true},
{"name": "Монитор", "price": 18400, "rating": 4.8, "in_stock": false},
{"name": "Наушники", "price": 7350, "rating": 3.9, "in_stock": true},
{"name": "Веб-камера", "price": 2450, "rating": 4.1, "in_stock": false}
]
}
"""
items = json.loads(API_RESPONSE)["items"]
cheap = [i for i in items if i["price"] < 5000]
summary = {
"count": len(items),
"cheap": len(cheap),
"share": round(len(cheap) / len(items) * 100),
"avg_price": round(sum(i["price"] for i in items) / len(items), 2),
"best_rating": max(items, key=lambda i: i["rating"])["name"],
}
for key, value in summary.items():
print(key, "=", value)
count = 5 cheap = 3 share = 60 avg_price = 6896.0 best_rating = Монитор
Пять вопросов — пять вычислений, каждое в одну строку, и словарь summary готов стать отчётом. Цикл for key, value in summary.items() печатает его построчно: пара ключ-значение за проход — приём обхода словаря из урока 10. Такой сводке не нужна ни сортировка, ни файл данных целиком в отчёте — только числа, и всё они у тебя уже умеют считаться.
| Вопрос к данным | Приём |
|---|---|
| что дешевле N | [item for item in items if item["price"] < N] |
| рейтинг не ниже X и в наличии | if item["rating"] >= X and item["in_stock"] |
| средняя цена | sum(цен) / len(цен) |
| доля подходящих | len(подходящих) / len(всех) * 100 |
| самый дорогой товар | max(items, key=lambda i: i["price"]) |
Чек-лист обработки ответа
- условие фильтра проверяет поле каждого словаря — записи должны быть одной структуры;
- строковые цены приводи float() до сравнения, иначе TypeError;
- булев флаг ставь в условие как есть: if item["in_stock"];
- среднее и долю считай только после проверки, что выборка не пуста.
from urllib.request import urlopen
import json
url = "https://shop.example/api/products?max_price=5000"
with urlopen(url) as response:
data = json.loads(response.read().decode("utf-8"))
cheap = [item for item in data["items"] if item["price"] < 5000]
print(len(cheap))
Такая конструкция — готовый каркас любого скрипта обработки: запрос, разбор, вопросы к данным. Меняется только URL и список условий — каркас переживает десятки задач: прайсы, выгрузки, отчёты по складу. Именно поэтому приёмы урока важнее конкретных цифр: цены и рейтинги условны, а условие в списке включения — нет.
Что дальше
Ты задал данным пять вопросов и получил на каждый ответ в пару строк: фильтр отобрал, sum и len посчитали, guard уберёг от пустой выборки. Дальше каталог хочется видеть по убыванию цены — сортировку и топ-N разбирает урок 16, а после неё результаты наконец можно записать на диск — этим займётся урок 17. Про сам запрос и доставку ответа подробно рассказывает курс requests.
Обработка ответа API — это вопросы к данным: условие выбирает, sum и len считают, а проверка на пустоту бережёт отчёт.
Сначала предскажи ответ в голове — это главный навык программиста.
items = [
{"name": "A", "price": 100, "in_stock": True},
{"name": "B", "price": 900, "in_stock": False},
{"name": "C", "price": 500, "in_stock": True},
]
names = [i["name"] for i in items if i["price"] < 600 and i["in_stock"]]
print(len(names), names[0])
prices = [1200, 800, 2600]
print(round(sum(prices) / len(prices)))
items = [{"price": 100}, {"price": 300}]
cheap = [i for i in items if i["price"] > 500]
if cheap:
print(len(cheap))
else:
print("пусто")
1. Как одним выражением выбрать из items все товары дешевле 3000?
2. Почему item["price"] < 1000 падает, если цена приехала строкой "1290"?
3. Что сделает фильтр [item for item in items if item["in_stock"]]
4. Как посчитать среднюю цену по списку цен prices?
5. Что вернёт max(items, key=lambda item: item["price"]) на списке товаров?
6. Зачем перед подсчётом среднего проверять выборку на непустоту?
Из сохранённого ответа магазина выбери товары, которые есть в наличии и стоят дороже 1000 рублей. Выведи названия найденных товаров, затем среднюю цену выборки с подписью «Средняя:», округлённую до целого.
Как отфильтровать список словарей из JSON по значению поля?
Списком включения с условием: [item for item in items if item["price"] < 5000]. Условие может проверять любое поле и комбинироваться через and и or. Исходный список при этом не меняется — фильтр возвращает новый. Полный набор приёмов фильтрации разобран в отдельном уроке.
Как посчитать среднее значение поля в JSON-данных?
Собери значения поля списком включения и раздели сумму на количество: sum(prices) / len(prices). Для красивого вывода округли round(avg, 2). Если выборка может оказаться пустой, сначала проверь её: деление на len пустого списка вызывает ZeroDivisionError.
Почему цена из API бывает строкой и что с ней делать?
JSON не требует типизировать числа: сервер вправе отдать "price": "1290". Строка с числом не сравнивается с int знаком «меньше» — будет TypeError. Приведи значение к числу перед сравнением: float(item["price"]) < 1000 или int(), если цены целые.
Чем разбор ответа API здесь отличается от работы с requests?
Ничем: requests доставляет ответ, а обработка начинается после него. response.json() в requests делает то же, что json.loads(response.text) — возвращает словарь. В этом курсе сеть заменена сохранённым текстом ответа, чтобы вся механика исполнялась прямо в браузере.
Что делать, если фильтр вернул пустой список?
Проверить выборку перед подсчётами: if found: — считать среднее и долю, else — вывести «ничего не найдено». Пустой список не ошибка, а честный ответ данных: товаров под условие просто нет. Падает не сам фильтр, а деление на len пустого списка в подсчётах после него.
Понравился урок? Сошлитесь на него
«Фильтр по ответу API — это обычное условие if, применённое сразу ко всему списку: прошёл товар по условию, попал в выборку.»
Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.
Что читать дальше
json · Урок 11
Фильтрация данных: найти нужное в JSON
Включение с условием для отбора, next с default для первого подходящего, any и all для вопросов ко всей коллекции — работаем с выгрузкой заказов.
json · Урок 14
json.loads на ответе API: от текста к данным
Текст, json.loads, словарь, поля: собираем полный конвейер разбора ответа API — с проверкой структуры, обработкой пустого и битого тела.
json · Урок 16
Сортировка и топ-N: sorted по ключу
Список из API приходит в том порядке, в каком отдал сервер: sorted с key наводит порядок, reverse разворачивает, срез берёт топ-3.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
sqlite3 · Урок 4
WHERE и фильтрация в SQL: условия, LIKE, BETWEEN и IN
Главный фильтр SQL: сравнения и логика с AND, OR, NOT, шаблоны LIKE с % и _, диапазоны BETWEEN, списки IN — и классическая ловушка = NULL.
фильтрация данных sqlsql фильтр по условию
Pandas · Урок 3
Выборка данных в Pandas: loc, iloc и фильтрация строк по условию
loc и iloc, булева маска, isin и between — достаём из таблицы заказов именно те строки и столбцы, которые нужны, и не попадаемся на SettingWithCopyWarning.
как отфильтровать строки в pandas по условиюфильтрация данных pandas
json · Урок 10
Обход данных: циклы по JSON-спискам
for по списку словарей, извлечение полей, суммирование выручки и list comprehension — превращаем выгрузку заказов в отчёт.
python цикл по jsonpython перебрать json список