Lightweight Python Excel Libraries Compared
pandas plus openpyxl is the default answer for Excel in Python, but it is not always the right one. A small command-line tool or AWS Lambda function that only needs to read a sheet does not want pandas, NumPy and their tens of megabytes; a script that must run on a locked-down machine may only be allowed pure-Python packages; and a job reading hundreds of large workbooks wants the fastest reader available. Several smaller libraries fill these gaps: pylightxl (pure Python, no dependencies), python-calamine (a Rust-based reader), and tablib and pyexcel (simple tabular interfaces over other engines). This page compares them on the same files — what they can do, what they pull in, how fast they read, what types they return — and reports two compatibility surprises found in testing. It belongs to Choosing a Python Excel Library.
Prerequisites
pip install pylightxl python-calamine tablib[xlsx] pyexcel pyexcel-xlsx openpyxl
The test files are the same 50,000-row sheet — ID, date, region, quantity, price — written twice: once by openpyxl (as pandas also does by default) and once by XlsxWriter. The versions tested were pylightxl 1.61, python-calamine 0.8, tablib 3.10, pyexcel 0.7 with pyexcel-xlsx 0.6, and openpyxl 3.1 as the baseline; timings are from one run on an ordinary laptop and are for comparison only.
The contenders
- pylightxl — a single pure-Python module with no dependencies. Reads and writes
.xlsxvalues (no formatting, no charts). Its selling point is that it can be copied into a project or installed where nothing else can. - python-calamine — Python bindings to the Rust
calaminecrate. Read-only, but reads.xlsx,.xlsm,.xlsb,.xlsand.ods, and is very fast. It ships as a compiled wheel with no Python dependencies; pandas uses it forengine="calamine". - tablib — a library for tabular datasets with import and export to many formats (CSV, JSON, YAML,
.xlsx). For Excel it uses openpyxl under the hood, so it is light in API, not in dependencies. - pyexcel — a uniform API over plugins;
.xlsxsupport comes from thepyexcel-xlsxplugin, which also uses openpyxl.
Step 1 — Read the same file with each
Each library reads the sheet into rows; the helper times it and shows the first data row so the returned types are visible:
import time
import pylightxl as xl
import pyexcel
import tablib
from openpyxl import load_workbook
from python_calamine import CalamineWorkbook
readers = {
"pylightxl": lambda f: list(xl.readxl(f).ws("Data").rows),
"python-calamine": lambda f: CalamineWorkbook.from_path(f).get_sheet_by_name("Data").to_python(),
"pyexcel": lambda f: pyexcel.get_array(file_name=f),
"tablib": lambda f: (lambda d: [tuple(d.headers)] + list(d))(tablib.Dataset().load(open(f, "rb"), format="xlsx")),
"openpyxl (read-only)": lambda f: list(load_workbook(f, read_only=True)["Data"].iter_rows(values_only=True)),
}
def run(path):
results = {}
for name, read in readers.items():
start = time.perf_counter()
try:
rows = read(path)
results[name] = (round(time.perf_counter() - start, 2), len(rows), rows[1][:4])
except Exception as e:
results[name] = (None, 0, f"{type(e).__name__}: {e}")
return results
xw = run("from_xlsxwriter.xlsx")
for name, (secs, n, first) in xw.items():
print(f"{name:22} {secs!s:>6}s rows={n} first={first}")
All five read the XlsxWriter-made file. python-calamine is in a class of its own — around a tenth of a second where the others take three quarters of a second to a second and a half — because its parser is compiled Rust. openpyxl in read-only mode is the fastest of the pure-Python readers; tablib and pyexcel are slower than openpyxl because they use openpyxl and then convert its output. pylightxl, despite being tiny, is not fast: it parses with the standard library's XML module and builds all rows in memory. tablib keeps the header row separately in headers, so the helper puts it back in front to make the row counts comparable.
Step 2 — Look at the types you get back
Speed is not the only difference. The first data row printed above shows each library's idea of a number and a date:
for name, (_, _, first) in xw.items():
print(f"{name:22}", [type(v).__name__ for v in first], first[1])
The differences matter as soon as values are used. pylightxl returns the date as the string '2026/01/01' — it applies the cell's date format itself and gives you text, so date arithmetic needs parsing first. python-calamine returns a proper datetime.date, but returns whole numbers as floats (0.0), because Excel stores all numbers as floating point and calamine does not guess which were meant as integers; IDs and quantities need converting with int() where that matters. openpyxl, tablib and pyexcel return datetime.datetime for dates and int for whole numbers. None of these is wrong, but swapping one library for another without checking types is a classic source of subtle bugs — see How pandas Data Types Are Written to Excel Cells for the writing side of the same question.
Step 3 — A compatibility surprise
Now read the copy written by openpyxl — the format most Python-generated workbooks, including those from pandas.DataFrame.to_excel, are in:
op = run("from_openpyxl.xlsx")
for name, (secs, n, first) in op.items():
print(f"{name:22} {'failed' if secs is None else f'{secs}s':>7} rows={n} first={first if n else first[:70]}")
Every library reads it except pylightxl 1.61, which fails with a TypeError before reading any cells. The cause is in how it looks up each sheet's relationship ID: it only recognises the r: namespace when it is declared on the root element of workbook.xml, and openpyxl declares it on each <sheet> element instead — perfectly valid XML that pylightxl's lookup misses. Files saved by Excel and by XlsxWriter declare it on the root, so they read fine. The lesson is broader than one bug: small libraries are tested against fewer producers of .xlsx files, so test any lightweight reader against files from every source your job will see before relying on it.
Step 4 — Writing
For writing, the field narrows. python-calamine cannot write at all. tablib and pyexcel write .xlsx through openpyxl. pylightxl writes values with no dependencies:
db = xl.Database()
db.add_ws(ws="Summary")
for r, row in enumerate([["Region", "Qty"], ["North", 120], ["South", 85]], start=1):
for c, value in enumerate(row, start=1):
db.ws("Summary").update_index(row=r, col=c, val=value)
xl.writexl(db=db, fn="summary_pylightxl.xlsx")
data = tablib.Dataset(["North", 120], ["South", 85], headers=["Region", "Qty"])
with open("summary_tablib.xlsx", "wb") as fh:
fh.write(data.export("xlsx"))
for f in ("summary_pylightxl.xlsx", "summary_tablib.xlsx"):
print(f, [[c.value for c in row] for row in load_workbook(f).active.iter_rows()])
Both produce a valid workbook that openpyxl reads back. Neither offers formatting worth the name — no number formats, column widths or fonts in pylightxl; tablib makes the header bold and little more. For anything that will be read by people rather than programs, XlsxWriter (a single pure-Python package with no dependencies of its own) is the better lightweight writer: it is fast, small and supports formatting, charts and tables. It is covered in Write a Formatted Excel Report with XlsxWriter.
Install footprint
On disk, after installation, the packages compared here occupied roughly: pylightxl 0.2 MB, tablib 0.3 MB (plus openpyxl), pyexcel with its io layer and xlsx plugin about 1 MB (plus openpyxl), python-calamine 2.2 MB (a compiled binary), openpyxl 2.8 MB, and XlsxWriter 2 MB. pandas with NumPy is well over 50 MB. For a Lambda function or a container image, the gap between "openpyxl or calamine" and "pandas" is what matters; between the small libraries, it rarely does.
Which to use
- Fastest reading, any Excel format: python-calamine — directly, or through
pandas.read_excel(engine="calamine")when you have pandas anyway. Convert floats back to integers where needed. - No dependencies allowed, simple values: pylightxl — but test it against your actual files, and parse its date strings.
- Many formats through one small API: tablib, accepting openpyxl as a dependency.
- Reading and writing with formatting, still lightweight: openpyxl to read, XlsxWriter to write.
More detailed timing across engines is in Benchmark Python Excel Read and Write Speed.
Common pitfalls
- Assuming a small library reads every
.xlsx. Test with files from Excel, openpyxl, XlsxWriter and any other producer you receive. - Ignoring returned types. Dates as text, integers as floats — check before computing.
- Expecting tablib or pyexcel to avoid openpyxl. For
.xlsxthey use it underneath. - Using calamine to write. It is read-only.
- Choosing a reader by install size alone. Speed and correctness on your files matter more.
Conclusion
The lightweight Excel libraries each have a niche. python-calamine is by far the fastest reader and handles every Excel format, returning floats for all numbers. pylightxl needs nothing but Python and reads and writes plain values, though it returns dates as text and, in version 1.61, could not read files written by openpyxl. tablib and pyexcel offer simple tabular APIs on top of openpyxl. For most jobs that want to stay small, python-calamine for reading and XlsxWriter for writing is the strongest pair — after testing against the files you actually receive.
Frequently asked questions
What is the fastest Python library for reading Excel files? python-calamine, which wraps the Rust calamine crate. It read a 50,000-row sheet in about a tenth of a second in our test, roughly eight times faster than openpyxl in read-only mode. pandas can use it with engine="calamine".
Is there an Excel library for Python with no dependencies? pylightxl is a single pure-Python module with no dependencies that reads and writes cell values. XlsxWriter also has no dependencies but only writes.
Why can't pylightxl read my Excel file? pylightxl 1.61 failed with a TypeError on workbooks written by openpyxl, including pandas output, because it does not find the relationships namespace when it is declared on the sheet elements. Files from Excel and XlsxWriter read correctly.
Do tablib and pyexcel need openpyxl? Yes, for xlsx files. tablib's xlsx support and the pyexcel-xlsx plugin both use openpyxl underneath.
Related
- Up one level: Choosing a Python Excel Library — picking tools for Excel work.
- Pick an Excel Engine for xlsx, xlsm, xls, xlsb and ods — engines by file format.
- openpyxl vs pandas for Excel Automation — the heavyweight pairing.
- Speed Up pandas Excel Reads with the Calamine Engine — calamine inside pandas.
More on choosing a library
- Comparisonxlwings vs pywin32 for Excel Automation
- Comparisonopenpyxl vs xlsxwriter vs pandas.ExcelWriter
- How-toChoose a Scheduler for Python Excel Reports
- ComparisonGoogle Sheets vs Excel Files for Python Automation
- Comparisonopenpyxl vs pandas for Excel Automation
- Comparisonpandas vs DuckDB for Excel Data