PDF is a format invented for printing and viewing, not for storing data. That's exactly why reliably getting information out of it is the hardest job of any format in our overview of document parsing. Invoices, contracts, spec sheets, price lists, bank and government statements all arrive as PDFs — and nearly always you need to turn them into structured data. This guide walks through PDF scraping in practice: pulling text and tables out of files, handling scans with OCR, and choosing the right library for the job.
The main fork: text or image
The first thing that decides your whole strategy is how the text is stored in the file. In a "digital" PDF (generated from a word processor, a layout tool, or a browser) the text sits there as selectable characters — you can extract it directly. In a scanned PDF the page is an image, and without recognition (OCR) there are no characters in it — you'll extract nothing. The check is simple: if you can select the text with your mouse in a viewer, it's digital; if you can't, you need OCR. These two cases call for completely different tools, so you should always start with this check.
Extracting text from digital PDFs
Python
For extracting text and, more importantly, tables, the best choice is pdfplumber. It understands the coordinates of characters and lines, so it can reconstruct table structure.
import pdfplumber
with pdfplumber.open("invoice.pdf") as pdf:
page = pdf.pages[0]
text = page.extract_text()
print(text)
for table in page.extract_tables():
for row in table:
print(row) # row is a list of the cells in a table row
When you only need the text and speed matters, reach for PyMuPDF (imported as fitz) — it's very fast and holds layout well.
import fitz # PyMuPDF
doc = fitz.open("contract.pdf")
for page in doc:
print(page.get_text())
For simple jobs like page-by-page text extraction or working with metadata, pypdf (the successor to the deprecated PyPDF2) does the trick.
from pypdf import PdfReader
reader = PdfReader("report.pdf")
for page in reader.pages:
print(page.extract_text())
JavaScript / Node.js
In Node, the quick way to get a document's entire text is pdf-parse.
const fs = require("fs");
const pdf = require("pdf-parse");
const buffer = fs.readFileSync("invoice.pdf");
pdf(buffer).then((data) => {
console.log(data.text); // all the text
console.log(data.numpages);
});
When you need low-level control or want to run in the browser, use Mozilla's pdf.js — it gives you access to individual text fragments together with their coordinates.
Java
In enterprise projects Apache PDFBox is common. Watch out for the API change between versions: in PDFBox 3.x you open a document with Loader.loadPDF(...), whereas in 2.x it was PDDocument.load(...).
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import java.io.File;
try (PDDocument doc = Loader.loadPDF(new File("report.pdf"))) {
String text = new PDFTextStripper().getText(doc);
System.out.println(text);
}
PHP
In PHP the go-to library is Smalot\PdfParser (install it with composer require smalot/pdfparser). It reads text and metadata from digital PDFs with no external dependencies.
<?php
require 'vendor/autoload.php';
use Smalot\PdfParser\Parser;
$parser = new Parser();
$pdf = $parser->parseFile('invoice.pdf');
echo $pdf->getText();
// document metadata
$details = $pdf->getDetails();
print_r($details);
Smalot handles the common cases well; for scanned PDFs you'll still need to shell out to an OCR engine like Tesseract.
Tables are a separate headache
There's no concept of a "table" in PDF: what a human sees as a table is really a set of text blocks and lines with coordinates. So table extraction is always heuristic. pdfplumber reconstructs tables from grid lines and works well when the table is ruled. For complex tables with no lines, Python users turn to Camelot (a layout-based method) or tabula-py (a wrapper around the Java Tabula engine). If you have many tables and they're all alike, you usually settle on one tool and calibrate it to that specific layout.
Often the most convenient thing is to dump the result straight into a spreadsheet — then downstream processing goes through Excel or CSV, just like any other tabular source.
Scans and OCR
If the text won't select, you have to recognize the page before you can extract it. The classic Python stack renders pages to images (with that same PyMuPDF) and recognizes them with the Tesseract engine via the pytesseract wrapper.
import fitz
import pytesseract
from PIL import Image
import io
doc = fitz.open("scan.pdf")
for page in doc:
pix = page.get_pixmap(dpi=300) # render the page to an image
img = Image.open(io.BytesIO(pix.tobytes()))
text = pytesseract.image_to_string(img, lang="eng")
print(text)
OCR quality depends heavily on resolution (use 300 DPI or more), the cleanliness of the scan, and the language. For non-English documents, be sure to install and specify the right language pack (for example deu for German, fra for French, or spa for Spanish). Don't expect 100% accuracy from OCR — the result almost always needs post-processing and review.
Which library should I use?
| Your file | Best fit | Notes |
|---|---|---|
| Digital PDF, text only | PyMuPDF (Python), pdf-parse (Node), PDFBox (Java), Smalot (PHP) | Fast, direct extraction. |
| Digital PDF, ruled tables | pdfplumber | Reconstructs tables from grid lines. |
| Digital PDF, borderless tables | Camelot / tabula-py | Layout-based; needs calibration. |
| Scanned PDF (image) | PyMuPDF + Tesseract (pytesseract) | Render to image, then OCR. |
| Metadata / page splitting | pypdf | Simple, lightweight. |
When you shouldn't scrape a PDF head-on
Sometimes it turns out the document you need also exists in another format — for instance, the same statement is available as a CSV export, or the data is served by an API as JSON. That's almost always more reliable than extracting a table from the printed version, so it's worth checking the source before you commit to PDF scraping.
FAQ
Can I extract data from a password-protected PDF?
If you have the password, yes — most libraries accept it (for example pdfplumber.open("file.pdf", password="...") or PdfReader with a decrypt call). Without it, you can't, and you shouldn't try to bypass it.
Why does my extracted text come out jumbled or in the wrong order? PDF stores text by position, not reading order. Multi-column layouts and unusual fonts confuse naive extractors. Try a coordinate-aware tool like pdfplumber or PyMuPDF's block/word extraction, which lets you sort fragments by position.
How do I scrape hundreds of PDFs at once? Wrap any of the examples above in a loop over your files, write each result to a row, and export to a spreadsheet or database. For recurring, high-volume feeds, a done-for-you pipeline is usually cheaper than maintaining your own.
PDF is a format where no "universal" solution exists: an invoice, a contract, and a scan each need different tools. If you have a steady stream of similar documents — invoices, delivery notes, spec sheets — scraping.pro will set up PDF data extraction tuned to your specific layout, including tables and OCR for scans, with the output delivered straight into your system. Take a look at our data extraction service or ongoing data-as-a-service feeds.