Pull tables out of PDFs into clean CSV / Excel / JSON — with a preview-and-confirm step so you can eyeball what will be extracted before anything is written. Built for people who just need the data, not a parsing project. It puts the extraction stage of the PDF pipeline walkthrough into one command.
A table in a PDF isn't really a table. On the page it's just scattered glyphs
at fixed x/y coordinates — the "rows" and "columns" you see are an illusion your
eye assembles from alignment and whitespace. There's no grid stored anywhere.
Reconstructing which glyphs belong to which cell, stitching a table that runs
across page breaks, and filling the blanks left by merged label cells is fiddly
work that doesn't scale past a handful of pages. pdf-table-extractor does that
reconstruction for you and hands back clean, tabular data — the extraction stage
of the larger PDF automation
pipeline.
Not on PyPI — clone this repo and install from the clone:
git clone https://github.com/document-data-automation/pdf-table-extractor
cd pdf-table-extractor
pip install .- pdfplumber is required to read PDFs and is installed automatically.
- Excel output needs openpyxl — install the extra when you want
--format xlsx:
pip install ".[xlsx]"You can also run it straight from a clone without installing:
python -m pdf_table_extractor preview sample.pdf1. Preview — detect tables and print each as a compact grid with its page,
table index, and rows×cols shape, so you can decide which ones you want:
pdf-table-extractor preview report.pdf --pages 1-3[1] page 1, table 1 (5x4)
Region Product Units Revenue
Europe Widget 120 1199.00
Gadget 45 899.55
Asia Widget 80 799.20
Gadget 60 1199.40
[2] page 2, table 1 (5x4)
Region Product Units Revenue
...
The number in brackets ([1], [2], …) is a global table number you can pass
to --table.
2. Extract — write the tables you want:
# every table, one CSV per table, into ./out
pdf-table-extractor extract report.pdf --out ./out --format csv
# just table 2
pdf-table-extractor extract report.pdf --out ./out --table 2
# stitch the multi-page table and forward-fill merged cells, as Excel
pdf-table-extractor extract report.pdf --out ./out --format xlsx --combine --fill-downBy default you get one file per table, named report__p<page>_t<index>.csv.
For --format xlsx you get a single workbook (report.xlsx) with one sheet per
table.
| Flag | Command | Description |
|---|---|---|
--pages RANGE |
both | Pages to look at: 1-5, 1,3,7, or 2- (page 2 to the end). |
--strip |
both | Trim leading/trailing whitespace from every cell. |
--out DIR |
extract | Output directory (created if missing; default .). |
--format csv|xlsx|json |
extract | Output format (default csv). |
--table N |
extract | Extract only table number N from the preview listing. |
--combine |
extract | Stitch tables that share a header into one dataset. |
--fill-down |
extract | Forward-fill blank cells beneath merged label cells. |
--version |
— | Print the version and exit. |
Exit codes: 0 success, 1 a runtime problem (file missing, no tables found,
a needed dependency absent), 2 a usage error (bad flags or --pages value).
Multi-page stitching (--combine). When a table continues onto the next
page, PDF viewers repeat the header row and the tool detects each fragment as a
separate table. --combine groups tables by their header signature (the
normalized, case-insensitive header cells) and concatenates their rows into one
dataset — so a table spread over five pages becomes a single CSV/sheet with one
header and all the rows.
Merged-cell fill-down (--fill-down). A label cell that visually spans
several rows (a merged cell) leaves the rows beneath it blank in the raw data:
Region Product Units
Europe Widget 120
Gadget 45 <- "Europe" is blank here
Asia Widget 80
Gadget 60 <- "Asia" is blank here
--fill-down propagates each value downward into the blanks below it, so every
row is self-contained (Europe, Gadget, 45). Headers are always normalized too:
whitespace trimmed, embedded newlines collapsed, and blank/duplicate header
cells given stable names.
from pdf_table_extractor import extract_tables
tables = extract_tables(
"report.pdf",
pages="1-3", # optional page spec
fill_down=True, # forward-fill merged cells
strip=True, # trim whitespace
combine=True, # stitch multi-page tables
)
for table in tables:
print(table.page, table.shape) # e.g. 1 (41, 4)
print(table.header) # ['Region', 'Product', 'Units', 'Revenue']
for row in table.rows: # list[list[str]], header excluded
print(row)extract_tables() returns Table objects with .page, .index, .header,
.rows, .number, and convenience .shape / .all_rows. Writers live in
pdf_table_extractor.formats (write_csv, write_json, write_xlsx).
See examples/ for a runnable script and a helper that generates a
sample PDF.
git clone https://github.com/document-data-automation/pdf-table-extractor
cd pdf-table-extractor
pip install -e ".[dev]"
pytestThe pure logic — page-range parsing, header normalization, fill-down, multi-page
stitching, the writers, and the preview formatter — is unit-tested offline with
no PDF needed. A single integration test generates a one-page PDF at runtime and
skips cleanly when pdfplumber/reportlab aren't installed, so CI (Python
3.9–3.12) stays green either way.
MIT — see LICENSE.
Built by document-data-automation.com — Python workflows for document & data automation.