
Привет, Хабр!
Как часто вы парсите Excel-таблицы?
Лично я очень часто. И почти никогда эти файлы не выглядят так, что их можно без боли скормить pandas и сразу получить аккуратный DataFrame.
Думаю, многим знакома ситуация, когда:
таблица начинается где-то с 7-й строки
заголовок размазан на несколько рядов
названия колонок незначительно отличаются от файла к файлу
Со временем я все больше усложнял и полировал код парсинга, стараясь сделать его читаемым и поддерживаемым.
Буквально пару недель назад, проводя код-ревью, меня внезапно накрыло осознание: огромный кусок логики наших мини-приложений - это чтение и парсинг Excel-файлов. При этом целая команда разработчиков решает одну и ту же задачу, но каждый по-своему.
Стало немного больно.
Поэтому я написал xlea
xlea - это легкая библиотека, которая в духе SQLAlchemy или pydantic позволяет декларативно описывать схему таблицы и парсить Excel-файлы без танцев с бубном.
Неважно:
с какой строки начинается таблица - с первой или с двадцать пятой
сколько строк занимает заголовок - одна или семь
Вы просто описываете схему, а дальше происходит магия.
Как это выглядит
Пример таблицы
Допустим, у нас есть Excel-файл, где таблица начинается где-то с пятой или десятой строки, это не принципиально.
Выглядит она, скажем, так:
Номер сотрудника |
Ф И О |
Должность |
Дата рождения |
Возраст |
|---|---|---|---|---|
123987 |
Скайоукер Люк Эникенович |
Джедай |
19 ДБЯ |
19 |
987123 |
Уилсон Уэйд Дедпулович |
Наемник |
01.02.1991 |
35 |
… |
… |
… |
… |
… |
Предположим, нам нужны только:
номер сотрудника
ФИО
возраст
При этом:
ФИО может называться
ФИО,Ф.И.О,Ф. И. Ои как угодно ещевозраст в одних файлах просто
Возраст, а в другихВозраст сотрудника
Описываем схему
Начнем со схемы:
class Person(Schema): id: str = Column("Номер сотрудника") fullname: str = Column("Ф.*И.*О.*", regexp=True) age: int = Column( lambda n: n.startswith("Возраст"), validator=lambda v: v.isnumeric(), skip_invalid_row=True )
Мы создаем класс Person, наследуемый от Schema, и с помощью Column привязываем колонки таблицы к атрибутам:
id: колонка «Номер сотрудника»fullname: ФИО, описанное через регулярное выражениеage: возраст, который определяется через лямбда-функцию по имени столбца
Для age я также добавил валидатор: он проверяет, что значение числовое.
Если строка не проходит валидацию, она будет пропущена (skip_invalid_row=True).
По умолчанию, если значение не проходит валидацию, парсинг останавливается с ошибкой
InvalidRowError.
На этом схема готова, дальше можно парсить файл.
Парсим файл
Есть два варианта.
Вариант первый: автораспознавание.
В xlea встроены три ридера: openpyxl, xlrd и pyxlsb. Нужный выбирается автоматически по расширению файла:
persons = xlea.autoread("persons.xlsx", schema=Person)
Вариант второй: свой провайдер.
Можно реализовать протокол xlea.providers.proto.ProviderProto и передать его напрямую:
persons = xlea.read(OpenPyXlProvider("persons.xlsx"), schema=Person)
В протоколе нужно реализовать всего один метод - rows, который возвращает Iterable[Iterable].
То есть это может быть любой источник данных, не только Excel.
Таким образом, xlea полностью независима от конкретных ридеров. Вы можете:
реализовать чтение любых форматов файло
расширять функциональность под свои нужды
Также можно подключить свой провайдер к механизму автораспознавания:
class MyProvider(ProviderProto): def rows(self): ... # ваша логика xlea.register_provider(".myextension", MyProvider)
После этого autoread
будет использовать MyProvider для файлов с нужным расширением.
Результат парсинга - итератор объектов Person:
for p in persons: print(p.id, p.fullname, p.age) print(p.asdict())
Можно:
обращаться к атрибутам напрямую
получить индекс исходной строки (
row_index)получить словарь через
asdict()
Многоуровневые заголовки
Иногда таблицы выглядят так:
Номер сотрудника |
Работа |
Дата рождения |
Возраст |
|
|---|---|---|---|---|
Ф И О |
Должность |
|||
123987 |
Скайоукер Люк Эникенович |
Джедай |
19 ДБЯ |
19 |
987123 |
Уилсон Уэйл Дедпулович |
Наемник |
01.02.1991 |
35 |
… |
… |
… |
… |
… |
Здесь столбцы объединены в группу «Работа».
Парсится это тоже довольно просто:
@config(header_rows=2) class Person(Schema): id: str = Column("Номер сотрудника") fullname: str = Column("Работа;Ф.*И.*О.*", regexp=True) age: int = Column( lambda n: n.startswith("Возраст"), validator=lambda v: v.isnumeric(), skip_invalid_row=True )
В декораторе config указываем количество строк заголовка,
а уровни заголовков в Column разделяем символом ; (его тоже можно переопределить параметром delimiter в конфиге).
Установка и репозиторий
Установка стандартная:
pip install xlea
Репозиторий на GitHub: https://github.com/artanador/xlea
Звезды приветствуются ?
А как вы обычно решаете задачу парсинга «кривых» Excel-файлов?
Пишете кастомный код каждый раз или у вас уже есть свои универсальные инструменты?
Комментарии (25)

Vindicar
01.02.2026 09:13Ссылка на репозиторий битая
Что насчёт объединённых ячеек? С ними есть какая-то логика работы? Например, если объединено несколько строк, повторять значение на каждой строке.
Не пробовали использовать совместно с парсингом таблиц в PDF? Там тоже крайне весело.

ArtCapCorn Автор
01.02.2026 09:13Ссылку поправил, спасибо!
про объединенные ячейки пока не думал, запишу себе, спасибо за мысль)
С pdf тоже не работал, но подозреваю что можно реализовать provider :)

Vindicar
01.02.2026 09:13Я примерно в этом направлении и думал. Провайдер как раз потребуется, но не возникнет ли заморочек.
А то мне тут довелось парсить замороченную таблицу... было весело.

ArtCapCorn Автор
01.02.2026 09:13Хм, будет сложно, но вполне реально) либо по верху заголовок забрать, либо всю иерархию и накинуть валидаторы.
В теории сработает)

neuro_monarch
01.02.2026 09:13Актуально, как никогда. Сейчас собираю через базу аналитику рекламы и ботов, важно все эти тонны данных грузить в таблицу для дальнейшей визуализации и приходится использовать сложные app scripts. Пока строк десятки тысяч не вылажу за лимиты в 6 минут, но они уже приближаются в сотне.
Изучу вашу библиотеку, может найду какое-то решение для себя.Спасибо!

Grapple228
01.02.2026 09:13Тут уже нужно какой нибудь питон использовать с пандас и нумпаем, и подобными библиотеками, а данные хранить в БД вроде Sqlite
Эксель при большом количестве данных начинает сильно тупить. Да и само взаимодействие с ним крайне неудобное

MilPavel
01.02.2026 09:13Лучше Excel никто не парсит Excel. Обратите внимание на скрытые строки, высоту строк, объединенные ячейки. Запишите свои действия в макрос Excel VBA. Спросите DeepSeek, как лучше решать поставленную задачу, максимально расписав требования.

miletoda
01.02.2026 09:13Много приходится парсить абсолютно разных Excel, тоже уже давно написали свою библиотеку, по функционалу следующее сделано (писали на .NET + Epplus и результаты льются в postgre):
Шаблоны загрузки пишет аналитик в конфиге JSON. Вынесли, чтобы не тратить время программистов на внесение данных в код;
Конфиг каждый содержит инфу - режим парсинга (у нас их несколько, так данные могут иметь сложную структуру); с какого листа читать, с какой строки/колонки, какое количество колонок, также содержит весь маппинг колонок excel на конечные поля БД;
Парсер выполняет два блока проверок: механические и логические. Набор проверок для каждой колонки также прописывается в конфиге флагами;
Механические проверки - контроль уникальности значений по колонке, проверка соответствия типов данных целевым, проверка на кириллицу/латиницу там где нужно, удаление непечатетаемых символов, удаление лишних пробелов там где нужно;
В механические проверки также входит нормализация данных - замены по справочнику (справочник ведётся в postgre) - меняются "штук" на "шт" и и.д., и замены на id, там где нужна совсем чистая нормализация;
Логические проверки - много всяких дополнительных правил нетиповых, которые сложно засунуть в конфиг;
-
Также пришлось решать много специфических вопросов касательно самого excel - сбросы фильтров, обработки ошибок в формулах, пересчет формул для книг где задан ручной расчет формул, работа с формулами, использующими промежуточные значения в таблицах, парсинг xlsb (пришлось использовать отдельную библиотеку Sylvan)
Библиотека сэкономила кучу времени, так как внести изменения или добавить новый источник теперь занимает минут 20, при этом сам парсер трогать не надо, только поработать с конифгами и при необходимости с конечными таблицами в SQL.

Ivan22
01.02.2026 09:13Рекомендую сперва заливать в бд данные из Экселя as is так сказать. А все проверки, нормализации, удаления непечатетаемых символов и т.п. делать уже следующим шагом.
А то будет сложно понять почему в базе сейчас что-то не то что ожидается, в случае когда исходный эксель уже исчез/изменился/удалился/не доступен

miletoda
01.02.2026 09:13соглашусь, мы уже почти пришли к этому - raw слой для записи первичных данных. единственное что пока сдерживает - большая волатильность части источников, когда колонки могут меняться местами, добавляться/удаляться - в таком случае при наличии "сырого" слоя, приходится помимо просто правки конфигов, еще пересоздавать "сырые" таблицы, и учитывать это в пайплайне - в каких периодах времени, какие виды таблиц использовать. а так для стабильных исходников - первичный слой вполне удобно.

saigor33
01.02.2026 09:13Я делал похожую задачу для геймдизайнеров, для выгрузки json-конфигов игры из Excel/GoogleSheet.Только делал полностью универсальное решение.
Дизайнеры настраивают семантику синтаксиса прямо в документе Excel (чаще сразу в GoogleSheet), поддерживается неограниченный уровень вложенности (описывается семантикой).Заполнение конфигов(20-30к строк) при ребалансе игры делается не неделю, а 15 минут).
Программистов к парсингу больше не привлекают, даже после изменения структуры конфигов/таблиц, дизы всё сами делают.Если интересно вот ссылка на гитхаб (внутри есть инструкция как с этим работать)
https://github.com/saigor33/TableToJsonUniversalParser

babysas
01.02.2026 09:13Слышал у аналитиков "какую бы крутую талзу вы не испольовали" все равно на каком-то шаге есть эксель ;)
Спасибо за инструмент, поиграюсь.

sashamsuper
01.02.2026 09:13Для .NET написал обертку над NPOI. Для python pandas, на мой взгляд, хватает. Тут если действительно поток кривых файлов надо обрабатывать, но в любом случае под каждый тип свой обработчик писать, хоть и с библиотекой.

Winand
01.02.2026 09:13Когда-то решал обратную задачу: красиво вывести DataFrame в Excel с учётом объединения ячеек и разбивки по страницам для печати. Сильно ускорило подготовку отчётов

Ravenkey
01.02.2026 09:13У каждого своя головная боль. Я например как менеджер проекта по разделам энергетика, слаботочка вынужден иметь дело с десятками спецификаций и их сканов. Данные приходят в виде чего угодно. Как знаете проектировщики предпочитают свои спецификации не создавать в чем-то удобном, а напрямую рисовать в виде линий и фрагментов многострочного текста прямо в Автокад. Плюс еще логисты, бухгалтера и сметчики извращаются кто во что горазд. Поставщики тоже стараются.
Достал меня весь этот зоопарк и за вечер с помощью ии написал для себя библиотеку макросов для пользовательских команд. То что раньше занимало часы, теперь занимает минуты 4.
От эксель избавляться не нужно. Нужно просто правильно им пользоваться.

Ahizhnyak
01.02.2026 09:13Гм... Админить эту фигню будет интересно...
А когда коту нечего делать, он яйца лижет...

MilPavel
01.02.2026 09:13Ничего, нейросети разберутся. Но где еще такое можно? из MS Excel или MS Access открыть pdf-файл с картинкой через MS Word, который распознает картинку OCR и делает из неё текст, по этому тексту пробегается макрос VBA и парсит, заливает данные в СУБД. И для этого не нужно ставить никакие библиотеки!
Fedorkov
Хватит использовать Excel.
I Will Fucking Dropkick You If You Use That Spreadsheet
Ivan22
с чего вдруг хватит? эксель еще всех переживет
beswalod
Статья смешная, спасибо, но я не особо понял причин, по которой автор хочет отказаться от Excel. Скорость работы, долгое погружение разработчика в чужие скрипты, неоптимизированный бизнес-процесс? То есть понятно, что для данных существуют БД, но если нужно сделать что-то быстро и на коленке...
Fedorkov
Автор указал на основные проблемы.
Другие частые проблемы:
перенос строки внутри ячейки;
пользователь вместо того, чтобы удалить столбец, решил скрыть его;
формулы, макросы, объединение ячеек и прочие продвинутые фичи, затрудняющие машиночитаемость.
Я в качестве быстрого решения предпочитаю CSV, который так же открывается в в Excel и LibreOffice. Чтобы с без лишних телодвижений открывалось на любом компьютере независимо от софта и языка ОС, использую Unicode little endian + BOM. Разделитель - точка с запятой (чтобы не кавычить запятые).
Ещё есть TSV, который удобнее читать и править в блокноте, но открытие в Excel требует лишних телодвижений.
Для более важных или более объёмных данных - только БД.
gkaliostro8
Если бы читали внимательно, то поняли суть проблемы: автор не предлагает отказываться от excel, автор предлагает без танцев с бубном простой способ парсить их
beswalod
Ммм...
Ivan22
автор коментария "Хватит использовать Excel." имеется ввиду.