Урок 4 из 8 · 25 мин · обновлено

Отчёт из нескольких таблиц за минуту

Собираем один отчёт из нескольких файлов Excel на Python: перебираем файлы по шаблону, суммируем выручку по магазинам и товарам, сортируем и сохраняем новую книгу с двумя листами и оформлением.

Задача

Каждый понедельник три магазина «Канцмаркета» присылают свои продажи за неделю: магазин_1.xlsx, магазин_2.xlsx, магазин_3.xlsx. Таблицы одинаковые — дата, товар, количество, цена, как в уроке 3. Руководитель ждёт один файл:

  • лист «По магазинам» — выручка каждого магазина и итог;
  • лист «По товарам» — выручка по товарам, от лидера к отстающим.

Вручную это полчаса: открыть три файла, посчитать суммы, скопировать, отсортировать, оформить. Скрипт сделает то же за секунду.

Собираем данные

План: пройти по всем файлам магазинов, в каждом — по всем строкам, и копить суммы в двух словарях.

Python
from pathlib import Path
from openpyxl import load_workbook

by_shop = {}
by_product = {}

for path in sorted(Path(".").glob("магазин_*.xlsx")):
    shop = path.stem.replace("магазин_", "Магазин ")
    ws = load_workbook(path)["Продажи"]
    for date, product, qty, price in ws.iter_rows(min_row=2, values_only=True):
        amount = qty * price
        by_shop[shop] = by_shop.get(shop, 0) + amount
        by_product[product] = by_product.get(product, 0) + amount

print(by_shop)
print("Итого:", sum(by_shop.values()))
Результат
{'Магазин 1': 3850, 'Магазин 2': 5325, 'Магазин 3': 5725}
Итого: 14900

Что здесь важно:

  • glob("магазин_*.xlsx") находит все файлы магазинов. Откроется четвёртый магазин — скрипт подхватит его файл без изменений в коде;
  • path.stem — имя файла без расширения, магазин_1. Из него получаем красивое название Магазин 1;
  • by_shop.get(shop, 0) + amount — тот же приём, что в практике прошлого урока: прибавить к накопленному или начать с нуля.

Сохраняем отчёт

Теперь создаём новую книгу Excel и раскладываем цифры по листам:

Python
from openpyxl import Workbook
from openpyxl.styles import Font

report = Workbook()
ws = report.active
ws.title = "По магазинам"
ws.append(["Магазин", "Выручка, ₽"])
for shop, amount in by_shop.items():
    ws.append([shop, amount])
ws.append(["Итого", sum(by_shop.values())])

ws2 = report.create_sheet("По товарам")
ws2.append(["Товар", "Выручка, ₽"])
for product, amount in sorted(by_product.items(), key=lambda item: item[1], reverse=True):
    ws2.append([product, amount])

for sheet in report.worksheets:
    for cell in sheet[1]:
        cell.font = Font(bold=True)
    sheet.column_dimensions["A"].width = 20
    sheet.column_dimensions["B"].width = 14

report.save("Отчёт за неделю.xlsx")

Разберём по шагам:

  • Workbook() создаёт пустую книгу с одним листом, report.active — этот лист. Его переименовываем в «По магазинам»;
  • append() дописывает строку в конец листа — передаём список значений;
  • create_sheet() добавляет второй лист;
  • sorted(..., key=lambda item: item[1], reverse=True) сортирует пары «товар — выручка» по выручке, от большей к меньшей;
  • sheet[1] — первая строка листа, делаем её жирной, а column_dimensions задаёт ширину колонок, чтобы названия не обрезались.

В файле получится:

По магазинам

Магазин Выручка, ₽
Магазин 1 3 850
Магазин 2 5 325
Магазин 3 5 725
Итого 14 900

По товарам

Товар Выручка, ₽
Тетрадь 48 л. 5 225
Ручка гелевая 4 275
Блокнот А5 3 360
Маркер 2 040

Проверьте результат

Скрипт не ошибается от усталости, но ошибается, если ошиблись вы, когда его писали. Поэтому первые запуски сверяйте с ручным подсчётом: хотя бы итог и одну строку. Полезно проверять и входные данные:

  • пустые строки в конце таблицы дадут None вместо чисел, и умножение упадёт с ошибкой. Пропускайте их: if product is None: continue;
  • другое название листа — например, кто-то переименовал «Продажи» в «Лист1». Тогда скрипт остановится с ошибкой KeyError: 'Worksheet Продажи does not exist.'. Чтобы сразу понять, в каком файле дело, печатайте имя файла перед чтением: print("Читаю", path.name).

Для больших таблиц на сотни тысяч строк есть библиотека pandas — она делает то же самое короче и быстрее. Но принцип не меняется: прочитать, сгруппировать, посчитать, сохранить.

Попробуйте сами. Добавьте в отчёт третий лист «По дням»: выручка всех магазинов за каждый день недели, по порядку дат.

Решение

В цикл по строкам добавьте третий словарь:

Python
by_day = {}
# внутри цикла по строкам:
by_day[date] = by_day.get(date, 0) + amount

А перед сохранением — новый лист:

Python
ws3 = report.create_sheet("По дням")
ws3.append(["Дата", "Выручка, ₽"])
for date, amount in sorted(by_day.items()):
    ws3.append([date, amount])

На листе «По дням» окажется:

Результат
20.10.2026 5550
21.10.2026 4900
22.10.2026 4450

sorted(by_day.items()) сортирует по первому элементу пары — по дате. Здесь это работает, потому что все даты в одном месяце. Даты вида «5.10.2026» и «12.10.2026» в виде текста отсортируются неправильно — как это исправить, разберём в уроке 5.

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

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

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

  1. Какие файлы найдёт Path(".").glob("магазин_*.xlsx")?

  2. by_shop = {}. Что делает строка by_shop[shop] = by_shop.get(shop, 0) + amount?

  3. Как отсортировать товары по выручке от большей к меньшей?

  4. В понедельник открылся четвёртый магазин, и появился файл «магазин_4.xlsx». Что нужно поменять в скрипте?