Text extraction is tricky
Some PDFs contain real text.
Others are scans (images), which require OCR.
Extract with pypdf
from pypdf import PdfReader
reader = PdfReader("input.pdf")
text_parts = []
for page in reader.pages:
text_parts.append(page.extract_text() or "")
text = "\n".join(text_parts)
print(text[:1000])When you need OCR
If extract_text() returns empty or garbage:
- use OCR tools (like Tesseract)
- or use specialized PDF extractors
Why extraction is unreliable
A PDF is not a text document. It is a set of drawing instructions: put these glyphs at these coordinates on this page. There is no built-in notion of a paragraph, a table, or reading order. A library like pypdf has to guess how the glyphs group into lines, and the guess can be wrong.
There are three kinds of PDF, and the right tool depends on which you have:
- Text PDFs made from Word, LaTeX, or a web page.
extract_text()works, though layout may be messy. - Scanned PDFs where each page is a picture.
extract_text()returns an empty string. You need OCR. - Protected PDFs that require a password.
reader.is_encryptedisTrue, and you callreader.decrypt(password)first.
Checking the result
Never assume the text came out. A small check catches scans early.
from pathlib import Path
from pypdf import PdfReader
def extract_pdf_text(path: Path) -> str:
reader = PdfReader(path)
if reader.is_encrypted:
raise ValueError(f"{path.name} is password protected")
pages = []
for number, page in enumerate(reader.pages, start=1):
text = (page.extract_text() or "").strip()
if not text:
print(f"page {number}: no text found, may be a scan")
pages.append(text)
return "\n\n".join(pages)
text = extract_pdf_text(Path("input.pdf"))
print(len(text), "characters")
print(text[:500])The or "" matters because some versions return None for an empty page. Counting pages with no text tells you whether the whole file is a scan or only a few pages are.
Cleaning the text
Extracted text often has line breaks in the middle of sentences, hyphenated words split across lines, and repeated headers and footers. Typical fixes are to join lines that do not end in a full stop, to remove a known footer with str.replace, and to use a regular expression for values you need, such as invoice numbers or dates. For tables, extract the text first and see what you get. If the columns do not line up, a table-aware tool such as pdfplumber is a better choice.
Adding OCR
For scans, render each page to an image and run OCR on it. The usual combination is pdf2image (needs the Poppler tool installed) with pytesseract (needs Tesseract installed). It is slower and less accurate than text extraction, so check a sample by eye.
Common mistakes
- Trusting the output. Always look at a sample page.
- Using the old
PyPDF2name in new code.pypdfis the maintained project. - Forgetting to handle encrypted files.
- Loading hundreds of large PDFs into one string. Process and write out one file at a time.
Adapting it
Loop over a folder of PDFs, save each as a .txt with the same stem, and search the text files with the earlier search lesson. Or pull invoice numbers with re.findall into a CSV file.
๐งช Try It Yourself
Exercise 1 โ List Files with os.listdir
Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.
Press Run to see what your code prints.Exercise 2 โ Join Paths with os.path.join
Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.
Press Run to see what your code prints.Exercise 3 โ Write and Read a File
Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.
Press Run to see what your code prints.