Automated Text Extraction from Scanned PDFs to CSV Using Python and Tesseract OCR

Prerequisites and System Dependencies

Processing image-based PDF documents requires a specific stack. The workflow relies on pdf2image for rasterizing document pages, pytesseract as the Python binding for Tesseract OCR, and native system utilities for PDF parsing and character recognition.

Install the core Python packages:

pip install pdf2image pytesseract

Ensure the host operating system has poppler-utils (for PDF rendering) and the standalone Tesseract OCR engine installed. Both must be reachable via the system PATH environment varible.

Converting PDF Pages to Images

Scanned files contain bitmap data rather than selectable text layers. The first step is converting each page into a high-resolution PNG or JPEG. The convert_from_path method handles this transformation while allowing memory optimization through explicit page range targeting.

Optical Character Recognition Pipeline

Once rendered, each page image is processed by the OCR engine. Tesseract performs layout analysis and returns raw string output. Scanned tables often produce fragmented tokens, requiring post-processing to reconstruct columnar relationships accurately.

Data Normalization and CSV Export

Raw OCR results typically contain newline artifacts, bounding box brackets, and trailing whitespace. A dedicated normalization routine filters empty entries, strips formatting noise, and splits rows by whitespace. The structured records are then flushed to a CSV file using Python's standard library, guaranteeing proper delimiter handling and encoding consistency.

Complete Implementation

The following script consolidates conversion, recognition, cleaning, and export into a single executable module with type hints and error handling.

import os
import csv
import logging
from pathlib import Path
from pdf2image import convert_from_path
import pytesseract

logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')

def configure_tesseract(binary_path: str = None):
    """Override Tesseract binary location when not configured in system PATH."""
    if binary_path:
        pytesseract.pytesseract.tesseract_cmd = binary_path

def process_scanned_pdf(
    source_document: str,
    target_csv: str,
    language: str = "eng",
    page_bounds: tuple = None
) -> None:
    if not os.path.isfile(source_document):
        raise FileNotFoundError(f"Target file not located: {source_document}")

    configure_tesseract()

    cache_dir = Path(os.path.dirname(os.path.abspath(source_document))) / "render_cache"
    cache_dir.mkdir(exist_ok=True)

    logging.info("Rasterizing document pages...")
    render_args = {
        'dpi': 300,
        'fmt': 'png',
        'output_folder': str(cache_dir),
        'first_page': page_bounds[0] if page_bounds else None,
        'last_page': page_bounds[1] if page_bounds else None
    }

    try:
        page_renderings = convert_from_path(source_document, **render_args)
    except Exception as e:
        raise RuntimeError("PDF rendering failed. Verify poppler-utils installation.") from e

    logging.info(f"Running OCR on {len(page_renderings)} pages...")
    raw_segments = []

    for img in page_renderings:
        segment_text = pytesseract.image_to_string(img, lang=language)
        raw_segments.append(segment_text)

    logging.info("Cleaning tokens and structuring data...")
    formatted_records = []
    
    for block in raw_segments:
        # Split by whitespace to isolate columns
        tokens = block.split()
        # Remove empty strings and strip bracket artifacts common in OCR output
        cleaned_tokens = [
            token.strip().strip('[]')
            for token in tokens
            if token.strip()
        ]
        
        # Adjust length condition based on expected table structure
        if len(cleaned_tokens) == 3:
            formatted_records.append(cleaned_tokens)

    logging.info(f"Saving {len(formatted_records)} records to {target_csv}...")
    with open(target_csv, mode='w', encoding='utf-8', newline='') as sheet:
        writer = csv.writer(sheet)
        writer.writerow(['Column_A', 'Column_B', 'Column_C'])
        writer.writerows(formatted_records)

    # Purge temporary cache
    for file in cache_dir.iterdir():
        file.unlink()
    cache_dir.rmdir()

if __name__ == "__main__":
    pdf_file = "scanned_report.pdf"
    csv_output = "extracted_data.csv"
    process_scanned_pdf(pdf_file, csv_output, language="chi_sim+eng", page_bounds=(1, 5))

Troubleshooting Environment Configuration

Two dependency errors commonly interrupt execution:

  1. PDFInfoNotInstalledError: Occurs when poppler binaries are missing. Install via your package manager (apt install poppler-utils on Debian/Ubuntu, brew install poppler on macOS) and verify the /bin directory is included in PATH. Alternatively, pass the absolute path directly to convert_from_path using the poppler_path argument.

  2. TesseractNotFoundError: Indicates the OCR engine is absent or unregistered. Download the official installer, complete setup, and add the installation bin folder to system environment variibles. For immediate script-level overrides without modifying OS settings, assign the executable path explicitly: pytesseract.pytesseract.tesseract_cmd = r'absolute\path\to\tesseract.exe' before invoking any OCR functions.

Tags: python OCR PDF Processing Text Extraction CSV Automation

Posted on Sun, 16 Aug 2026 16:54:49 +0000 by castor_troy