Search
Write a publication
Pull to refresh

Навайбкодил самый быстрый xlsx editor

Level of difficultyEasy
Reading time3 min
Views10K

Предыстория: я фрилансер, основные деньги получаю, делая инструменты для редактирования xlsx файлов. Когда работы с Excel много, часто скапливаются задачи, что можно автоматизировать, но они делаются вручную. Я и пишу программы, что получают xlsx файл и обрабатывают данные с листов в удобный вид. Всё быстро, просто, понятно (одну кнопку нажать) и удобно.

пример

https://github.com/krakotay/steps-excel

Здесь не финальная версия, выложена с согласия заказчика

Но столкнулся с проблемой — редактирование уже имеющихся xlsx файлов это очень медленный процесс, гораздо медленнее чем чтение и запись. Поддержка такого режима есть только у openpyxl, там все операции очень долгие и требуют очень много ресурсов. "Точкой невозврата" стал случай, когда решение на openpyxl работало полчаса, съело всю доступную (из 32 гб) оперативку и 50 гб файла подкачки, и всё равно не справилось. Терпение кончилось быстрее. Потому я и навайбкодил написал своё решение. Новое решение (на компьютере клиента) справилось за 2 минуты с крошечным потреблением ОЗУ.

Вот сам проект. Ключевая часть написана на чистом Rust, имеются python-bindings. Лично я использую только с python 3.13, но и на 3.10-3.12 всё (по идее) работает.

Ключевые возможности

  1. Вставка таблицы, в конец или по указанным координатам.

    editor.append_table([["1", "2"], ["3", "4"]]) # в конец Worksheet
    editor.append_table_at([["=_xlfn.SUM(A1:B1)"], ['=_xlfn.SUM(A2:B2)']], "C1")
    editor.save("tests/test_sum_appended.xlsx")

    Поддерживаются формулы - их нужно писать в английской нотации, желательно начиная с =_xlfn. (считать их будет сам Excel)

  2. Сканирование файла

    from xlsx_append_py import PyXlsxScanner
    
    scanner = PyXlsxScanner(file_path)
    print(scanner.get_sheets())
    assert scanner.get_sheets() == ["dog", "cat"]
    
    editor = scanner.open_editor("dog")
    assert editor.last_row_index("A") == 0
    assert editor.last_row_index("B") == 0
    assert editor.last_row_index("C") == 0
    assert editor.last_row_index("D") == 0
    assert editor.last_rows_index("A:D") == [0, 0, 0, 0]
    
    editor = scanner.open_editor("cat")
    assert editor.last_row_index("A") == 2
    assert editor.last_row_index("B") == 9
    assert editor.last_row_index("C") == 22
    assert editor.last_row_index("D") == 15
    assert editor.last_rows_index("A:D") == [2, 9, 22, 15]
    

    last_row_index это про индекс последней строчки, где имеются данные в нужном столбце. Т.е. если в столбце A есть данные в ячейке A1 и A1000, он покажет 1000 (ведь Excel считает с единицы). Если данных нет, то 0 (соответственно пустой столбец).

    Несколько разных листов тоже поддерживаются, в том числе на запись. Только каждый editor должен быть save(new_file_path) отдельно... а не всё вместе. Можно записывать в один файл

  3. Поддержка polars (не дефолтная, требует компиляции вручную)

    editor = PyXlsxEditor(os.path.join(base_dir, "../../test/test_polars.xlsx"), sheets[0])
    editor.with_polars(df, None)
    editor.with_polars(df, "B15")
    editor.save(os.path.join(base_dir, "../../test/test_polars_appended.xlsx"))
    

все примеры из моих же тестов

UPD:

Провёл тестирование скорости

Command

Mean [s]

Min [s]

Max [s]

Relative

python xlsxeditor_speed.py

7.444 ± 0.005

7.439

7.449

1.00

python openpyxl_speed.py

179.239 ± 1.327

177.820

180.450

24.08 ± 0.18

Ускорение в 24 раза. При падении потребления ОЗУ с ~3000+ мбайт до 37.
Ссылка на замеры

причём openpyxl налажал
требует восстановить
требует восстановить
восстановил
восстановил
Хоть всё и работает. Кстати, интерфейс таблицы слетел
Хоть всё и работает. Кстати, интерфейс таблицы слетел
а в моей библиотеке всё ок
а в моей библиотеке всё ок

UPD 2:

Ускорил ещё сильнее

Command

Mean [ms]

Min [ms]

Max [ms]

Relative

python xlsxeditor_speed.py

471.7 ± 8.0

465.7

480.7

1.0

Что удивительно, код на Rust написан полностью силами chatgpt o3, и он удивительно хорошо работает... Конечно без меня не обошлось, но я отвечал за биндинги к питону и дебаггинг. Тестировал, отлавливал ошибки. Надеюсь, проект вам понравился, а ещё лучше если пригодится!

Пока release выложены только на github. Как восстановлю доступ к PyPI, выложу и туда.

Tags:
Hubs:
+19
Comments37

Articles