Python · API · Начальный уровень

Пагинация

5 задач

Проходите по многостраничным ответам API, чтобы собрать все результаты.

Как работает пагинация

#
API редко возвращает тысячи записей в одном ответе — это было бы медленно и расточительно. Вместо этого результаты разбиваются на страницы. Эндпоинт /api/products/ по умолчанию возвращает до 20 элементов на страницу, но в каталоге всего 372 продукта. Каждый ответ с пагинацией содержит четыре поля верхнего уровня: ```python data = response.json() data['count'] # 372 — всего элементов по ВСЕМ страницам data['next'] # URL следующей страницы или null, если это последняя data['previous'] # URL предыдущей страницы или null, если это первая data['results'] # список элементов на текущей странице ``` Для навигации по страницам есть два варианта: Вариант 1 — следуйте за URL next. Сервер строит URL за вас, поэтому не нужно вычислять номера страниц: ```python url = f'{BASE_URL}/api/products/' while url: data = requests.get(url).json() for product in data['results']: print(product['name']) url = data['next'] # None, когда страниц больше нет ``` Вариант 2 — вычислите общее количество страниц и итерируйте со счётчиком: ```python PAGE_SIZE = 20 first = requests.get(f'{BASE_URL}/api/products/', params={'page_size': PAGE_SIZE}).json() total_pages = -(-first['count'] // PAGE_SIZE) # деление с округлением вверх for page in range(1, total_pages + 1): data = requests.get( f'{BASE_URL}/api/products/', params={'page': page, 'page_size': PAGE_SIZE}, ).json() # обработайте data['results'] ``` Следовать за next проще и надёжнее — это работает даже если общее количество изменяется во время пагинации. Подход со счётчиком полезен, когда заранее нужно знать количество страниц, например чтобы показать индикатор прогресса.

Паттерны пагинации

#
Соберите все названия продуктов по всему каталогу: ```python import requests BASE_URL = 'https://apilearn.tukas.dev' all_names = [] url = f'{BASE_URL}/api/products/' while url: data = requests.get(url).json() for product in data['results']: all_names.append(product['name']) url = data['next'] print(f'Собрано {len(all_names)} продуктов') ``` Пагинация работает с фильтрами — URL next сохраняет все ваши параметры: ```python url = f'{BASE_URL}/api/products/' params = {'category': 'kitchen', 'ordering': 'price'} all_products = [] while url: data = requests.get(url, params=params).json() all_products.extend(data['results']) url = data['next'] params = {} # params уже закодированы в следующем URL print(f'Товаров kitchen: {len(all_products)}') ``` Примечание: после того как начинаете следовать за next, очищайте params — следующий URL уже содержит их. Если передать params снова, некоторые значения могут задублироваться в URL. Найдите самый дорогой продукт по всем страницам: ```python # Эффективнее: сортировка по убыванию и взятие первого результата data = requests.get( f'{BASE_URL}/api/products/', params={'ordering': '-price', 'page_size': 1}, ).json() most_expensive = data['results'][0] print(most_expensive['name'], most_expensive['price']) ``` Когда нужно одно экстремальное значение (максимум, минимум), пусть сервер сделает работу с ordering и page_size=1. Получать все страницы только чтобы найти максимум — расточительство.

Справочник пагинации

#
Поля ответа с пагинацией: ``` count int Всего элементов по всем страницам next str | null URL следующей страницы; null на последней previous str | null URL предыдущей страницы; null на первой results list Элементы на текущей странице ``` Параметры пагинации: ``` page int Номер страницы (по умолчанию: 1) page_size int Элементов на странице (по умолчанию: 20) ``` Формула для общего количества страниц: ```python import math total_pages = math.ceil(count / page_size) # или без math: total_pages = -(-count // page_size) # трюк с делением вверх ``` Паттерны циклов: ```python # Следовать за next (рекомендуется) url = f'{BASE_URL}/api/products/' while url: data = requests.get(url).json() # обработайте data['results'] url = data['next'] # Счётчик страниц for page in range(1, total_pages + 1): data = requests.get(url, params={'page': page, 'page_size': 20}).json() # обработайте data['results'] ```
01

Получить конкретную страницу

#

Получите 3-ю страницу списка продуктов с page_size=10. Выведите название каждого продукта на этой странице.

import requests

BASE_URL = 'https://apilearn.tukas.dev'

# Получите 3-ю страницу и выведите названия продуктов
Решение
import requests

BASE_URL = 'https://apilearn.tukas.dev'

params = {'page': 3, 'page_size': 10}
response = requests.get(f'{BASE_URL}/api/products/', params=params)
for product in response.json()['results']:
    print(product['name'])
02

Вычислить количество страниц

#

Получите список продуктов с page_size=20. Вычислите и выведите общее количество страниц, необходимых для просмотра всех продуктов.

import requests

BASE_URL = 'https://apilearn.tukas.dev'

# Вычислите количество страниц для page_size=20
Решение
import requests
import math

BASE_URL = 'https://apilearn.tukas.dev'

PAGE_SIZE = 20
data = requests.get(f'{BASE_URL}/api/products/', params={'page_size': PAGE_SIZE}).json()
total_pages = math.ceil(data['count'] / PAGE_SIZE)
print(f'{data["count"]} продуктов на {total_pages} страницах')
03

Собрать все продукты категории

#

Соберите все продукты в категории "kitchen" в список, следуя за URL next. Выведите общее количество собранных продуктов.

import requests

BASE_URL = 'https://apilearn.tukas.dev'

all_products = []
url = f'{BASE_URL}/api/products/'
params = {'category': 'kitchen'}

# Пройдите все страницы товаров kitchen
Решение
import requests

BASE_URL = 'https://apilearn.tukas.dev'

all_products = []
url = f'{BASE_URL}/api/products/'
params = {'category': 'kitchen'}

while url:
    data = requests.get(url, params=params).json()
    all_products.extend(data['results'])
    url = data['next']
    params = {}

print(len(all_products))
04

Собрать все названия продуктов

#

Соберите названия всех продуктов в каталоге (все страницы) в список. Выведите общее количество и название последнего продукта в списке.

import requests

BASE_URL = 'https://apilearn.tukas.dev'

# Соберите все названия продуктов по всем страницам
Решение
import requests

BASE_URL = 'https://apilearn.tukas.dev'

all_names = []
url = f'{BASE_URL}/api/products/'

while url:
    data = requests.get(url).json()
    for product in data['results']:
        all_names.append(product['name'])
    url = data['next']

print(len(all_names))
print(all_names[-1])
05

Найти самый дорогой продукт

#

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

import requests

BASE_URL = 'https://apilearn.tukas.dev'

# Эффективно найдите самый дорогой продукт
Решение
import requests

BASE_URL = 'https://apilearn.tukas.dev'

params = {'ordering': '-price', 'page_size': 1}
data = requests.get(f'{BASE_URL}/api/products/', params=params).json()
product = data['results'][0]
print(product['name'])
print(product['price'])