Skip to content

Natural PDF

A Python library for PDF extraction built on pdfplumber. Find and extract content using CSS-like selectors and spatial navigation. Simple code that makes sense.

Demos:

pip install natural-pdf
# All the extras
pip install "natural-pdf[all]"
from natural_pdf import PDF
pdf = PDF('https://github.com/jsoma/natural-pdf/raw/refs/heads/main/pdfs/01-practice.pdf')
page = pdf.pages[0]
# Find the title and get content below it
title = page.find('text:contains("Summary"):bold')
content = title.below().extract_text()
# Exclude everything above 'CONFIDENTIAL' and below last line on page
page.add_exclusion(page.find('text:contains("CONFIDENTIAL")').above())
page.add_exclusion(page.find_all('line')[-1].below())
# Get the clean text without header/footer
clean_text = page.extract_text()

The documentation is organized into six sections:

Use CSS-like selectors to find text, shapes, and more.

# Find bold text containing "Revenue"
page.find('text:contains("Revenue"):bold').extract_text()
# Find all large text
page.find_all('text[size>=12]').extract_text()

Move around the page relative to elements, not just coordinates.

# Extract text below a specific heading
intro_text = page.find('text:contains("Introduction")').below().extract_text()
# Extract text from one heading to the next
methods_text = page.find('text:contains("Methods")').below(
until='text:contains("Results")'
).extract_text()

Easily extract text content, automatically handling common page elements like headers and footers (if exclusions are set).

# Extract all text from the page (respecting exclusions)
page_text = page.extract_text()
# Extract text from a specific region
some_region = page.find(...)
region_text = some_region.extract_text()

Extract text from scanned documents using various OCR engines.

# Apply OCR using the default engine
page.apply_ocr()
# Select the OCR text if you need the created elements
ocr_elements = page.find_all("text[source=ocr]")
# Extract text (will use OCR results if available)
text = page.extract_text()

OCR supports recognition (default), detection refresh (detect_only=True), and custom recognition (function=). Replacement is strict: replace="ocr", "all", or "none"; booleans and the removed ocr_function= keyword are rejected. Detection preserves existing native and recognized text. PDF.apply_ocr also supports pages= and show_progress=; PDFCollection.apply_ocr supports max_workers=.

Use AI models to detect document structures like titles, paragraphs, and tables.

# Detect document structure
page.analyze_layout()
# Highlight titles and tables
page.find_all('region[type=title]').show()
page.find_all('region[type=table]').show()
# Extract data from the first table
table_data = page.find('region[type=table]').extract_table()

Use guides and row anchors when a table is visually clear but automatic table extraction misses rows or columns.

header = page.find('text:contains("CASE #")')
texts = list(page.find_all("text"))
header_row = [el for el in texts if abs(el.top - header.top) <= 2]
row_anchors = [
el
for el in texts
if el.extract_text().strip().startswith("23-") and el.top > header.top
]
df = page.extract_table_guided(
header_row,
row_anchors,
cell_extract="words",
cell_overlap="center",
).to_df()

For row-shaped content that is not a full table, use stable visual anchors:

rows = pdf.pages.extract_anchored_rows(
lambda page: [
el
for el in page.find_all("text")
if el.extract_text().strip().isdigit() and el.x1 < 70
],
side="right",
)
clean_lines = [row.text for row in rows]

Ask natural language questions directly to your documents.

# Ask a question
result = page.ask("What was the company's revenue in 2022?")
print(f"Answer: {result.answer}")

Debug and understand your extractions visually.

# Highlight headings
page.find_all('text[size>=14]').show(color="red", label="Headings")
# Launch the interactive viewer (Jupyter)
page.viewer()