Урок 4 из 8 · 25 мин · обновлено
Отчёт из нескольких таблиц за минуту
Собираем один отчёт из нескольких файлов Excel на Python: перебираем файлы по шаблону, суммируем выручку по магазинам и товарам, сортируем и сохраняем новую книгу с двумя листами и оформлением.
Задача
Каждый понедельник три магазина «Канцмаркета» присылают свои продажи за неделю: магазин_1.xlsx, магазин_2.xlsx, магазин_3.xlsx. Таблицы одинаковые — дата, товар, количество, цена, как в уроке 3. Руководитель ждёт один файл:
- лист «По магазинам» — выручка каждого магазина и итог;
- лист «По товарам» — выручка по товарам, от лидера к отстающим.
Вручную это полчаса: открыть три файла, посчитать суммы, скопировать, отсортировать, оформить. Скрипт сделает то же за секунду.
Собираем данные
План: пройти по всем файлам магазинов, в каждом — по всем строкам, и копить суммы в двух словарях.
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 и раскладываем цифры по листам:
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 — она делает то же самое короче и быстрее. Но принцип не меняется: прочитать, сгруппировать, посчитать, сохранить.
Попробуйте сами. Добавьте в отчёт третий лист «По дням»: выручка всех магазинов за каждый день недели, по порядку дат.
Решение
В цикл по строкам добавьте третий словарь:
by_day = {}
# внутри цикла по строкам:
by_day[date] = by_day.get(date, 0) + amount
А перед сохранением — новый лист:
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 вопроса
Проверьте себя
Выберите вариант, который кажется вам правильным. В конце вы увидите свой результат и объяснение к каждому вопросу.
-
Какие файлы найдёт Path(".").glob("магазин_*.xlsx")?
Звёздочка в шаблоне означает «любые символы». Подойдут «магазин_1.xlsx», «магазин_2.xlsx» и любой новый магазин.
-
by_shop = {}. Что делает строка by_shop[shop] = by_shop.get(shop, 0) + amount?
get(shop, 0) вернёт текущую сумму магазина или 0, если его ещё нет в словаре. К ней прибавляется сумма новой строки.
-
Как отсортировать товары по выручке от большей к меньшей?
items() даёт пары «товар — выручка», key=lambda item: item[1] сортирует по второму элементу пары, а reverse=True — по убыванию.
-
В понедельник открылся четвёртый магазин, и появился файл «магазин_4.xlsx». Что нужно поменять в скрипте?
Скрипт не перечисляет файлы вручную, а ищет их по шаблону. Новый магазин попадёт в отчёт автоматически.