Python Working with PDFs

In the last lesson you learned Web Scraping Basics. There you pulled data out of web pages. PDFs are the same kind of problem in a different shape. A report, an invoice, a bank statement, a contract. They all arrive as PDFs. And the moment you have more than a handful of them, doing things by hand gets slow and boring.

πŸ€” Why Work with PDFs in Code?

Here is the pain. Say Riya gets fifty invoices every month as PDFs. She needs the total from each one. Opening fifty files by hand and copying numbers is a whole afternoon gone. Or Alex has ten separate report pages and the boss wants them as one single file to email.

Python fixes this. You can read the text out of a PDF. You can search it. You can join many PDFs into one. All of it takes just a few lines. The tool most people reach for is pypdf, a small library made just for this.

It is a third-party library, so it does not come with Python. You install it with pip first.

Terminal window
pip install pypdf

That one command downloads pypdf and makes it ready to import in your code.

πŸ“„ Reading a PDF and Pulling Out Text

Think of a PDF like a stack of printed pages. pypdf lets you pick up the stack, count the pages, and read the words off any page you want.

Here is the smallest useful example. We open a file called report.pdf and print how many pages it has.

from pypdf import PdfReader
reader = PdfReader("report.pdf")
print(f"This PDF has {len(reader.pages)} pages.")

Output

This PDF has 3 pages.

Let me walk through what each line does.

  • from pypdf import PdfReader brings in the PdfReader class. That is the tool that opens and reads a PDF.
  • PdfReader("report.pdf") opens that file and hands you back a reader object.
  • reader.pages is a list-like thing holding every page, so len(...) counts them.

Now the part you actually came for. Getting the text. Each page has an extract_text() method that returns the words on that page as a normal string.

This reads the first page and prints its text.

from pypdf import PdfReader
reader = PdfReader("report.pdf")
first_page = reader.pages[0]
text = first_page.extract_text()
print(text)

Output

Quarterly Sales Report
Q1 2026
Total revenue grew by 12% compared to the previous quarter.
The strongest region was North America with 4.2M in sales.

Note

The output above is example output. Real PDFs hold all sorts of layouts, so the exact text and spacing you get back will depend on the file you open. Scanned PDFs are pictures of pages, so they hold no real text at all. For those, extract_text() returns an empty string.

reader.pages[0] grabs the first page. Counting starts at zero, so page one is index 0. Then extract_text() reads the words off it. The thing is, PDFs were built for printing, not for clean data. So you often get extra spaces or odd line breaks. That is normal. You clean it up afterwards like any other messy string.

πŸ” Reading Every Page at Once

One page is rarely enough. Usually you want the text from the whole document. So you loop over reader.pages and join it together.

This goes through every page, pulls the text, and combines it into one big string.

from pypdf import PdfReader
reader = PdfReader("report.pdf")
all_text = ""
for page in reader.pages:
all_text += page.extract_text() + "\n"
print(all_text)

Output

Quarterly Sales Report
Q1 2026
Total revenue grew by 12% compared to the previous quarter.
The strongest region was North America with 4.2M in sales.
Regional Breakdown
Europe held steady at 2.1M.
Asia Pacific climbed to 3.4M, a new record.
Outlook
We expect continued growth into Q2.

We start with an empty string all_text. Then for each page we add its text plus a newline \n so pages do not run into each other. After the loop, all_text holds the whole document. Now it is ready to search or save.

Want to find something specific? Once the text is in a string, plain Python does the rest. This checks whether the word β€œrevenue” shows up anywhere in the PDF.

if "revenue" in all_text.lower():
print("Found a mention of revenue.")
else:
print("No mention of revenue.")

Output

Found a mention of revenue.

We lowercase the text first with .lower() so the search ignores capital letters. Then the simple in check tells us if the word is there.

🧷 Merging Several PDFs into One

Now the other common job. Joining files. Say Alex has chapter1.pdf, chapter2.pdf, and chapter3.pdf and wants one combined book.pdf. pypdf has a PdfWriter for exactly this.

Think of PdfWriter as an empty binder. You add pages into it, then save the binder as a new file.

This takes three PDFs and stitches all their pages into a single output file.

from pypdf import PdfWriter
writer = PdfWriter()
for name in ["chapter1.pdf", "chapter2.pdf", "chapter3.pdf"]:
writer.append(name)
with open("book.pdf", "wb") as output_file:
writer.write(output_file)
print("Merged into book.pdf")

Output

Merged into book.pdf

Here is what is happening, step by step.

  • PdfWriter() makes the empty binder.
  • writer.append(name) adds every page of that file to the binder. Looping over the list does all three in turn.
  • open("book.pdf", "wb") opens a new file for writing in binary mode. The "wb" matters. PDFs are binary files, not plain text.
  • writer.write(output_file) saves everything into it.

Tip

writer.append() takes the whole file. If you only want some pages, use writer.append("report.pdf", pages=(0, 2)) to add just pages one and two. The range stops before the second number, the same way slicing works in Python.

🧰 A Real Example: Pulling Invoice Totals

Let us tie it together. Riya has a folder of invoice PDFs and wants the total line from each. We read every PDF in the folder, find the line with β€œTotal”, and print it next to the file name.

This loops through a list of invoice files and prints the total from each one.

from pypdf import PdfReader
invoices = ["invoice_001.pdf", "invoice_002.pdf", "invoice_003.pdf"]
for filename in invoices:
reader = PdfReader(filename)
text = reader.pages[0].extract_text()
for line in text.split("\n"):
if "Total" in line:
print(f"{filename}: {line.strip()}")

Output

invoice_001.pdf: Total: 1,240.00
invoice_002.pdf: Total: 890.50
invoice_003.pdf: Total: 2,015.75

Note

This is example output. The real lines depend on how each invoice is written. The idea stays the same though. Read the page, split it into lines, and keep the one you care about.

For each invoice we read the first page and split the text into separate lines with .split("\n"). Then we look at each line and keep only the one holding the word β€œTotal”. .strip() trims off extra spaces so the result looks clean. What used to be an afternoon of copying is now a script that runs in a second.

✍️ Creating PDFs from Scratch

pypdf reads and joins PDFs, but it does not draw new ones from nothing. When you need to build a fresh PDF, say a generated receipt or a report, reach for reportlab. It is a separate third-party library made for drawing PDFs.

You install it the same way.

Terminal window
pip install reportlab

Here is a tiny taste. This creates a brand-new PDF with one line of text on it.

from reportlab.pdfgen import canvas
c = canvas.Canvas("hello.pdf")
c.drawString(100, 750, "Hello from Python!")
c.save()
print("Created hello.pdf")

Output

Created hello.pdf

canvas.Canvas("hello.pdf") opens a blank page. drawString(100, 750, ...) writes text at an x,y spot on the page, measured in points from the bottom-left corner. c.save() writes the file. We will keep reportlab short here. Just know it is the go-to when you need to make PDFs, not just read them.

🧱 Which Library for Which Job

A quick map so you grab the right tool.

You want to… Use
Read text from a PDF pypdf (PdfReader)
Merge or split PDFs pypdf (PdfWriter)
Create a new PDF from scratch reportlab
Read a scanned (image) PDF an OCR tool like pytesseract

⚠️ Common Mistakes

A few things trip people up early on.

  • Forgetting binary mode when writing. PDFs are binary files. Opening with "w" instead of "wb" corrupts the output.
# ❌ Avoid: text mode breaks the PDF
with open("book.pdf", "w") as f:
writer.write(f)
# βœ… Good: binary mode
with open("book.pdf", "wb") as f:
writer.write(f)
  • Expecting text from a scanned PDF. If the PDF is really a photo of a page, extract_text() gives you an empty string. There is no text in there to read, only an image. You need OCR for that.
  • Assuming clean formatting. Extracted text often has odd spacing and line breaks. Always clean the string before you trust it.
  • Using the wrong page number. Pages start at index 0. reader.pages[1] is the second page, not the first.

βœ… Best Practices

Small habits that keep PDF code tidy.

  • Check len(reader.pages) before reading a page so you never ask for a page that is not there.
  • Wrap file reads in try/except since a missing or damaged PDF will raise an error.
  • Strip and clean extracted text before searching or saving it.
  • Pick the right tool. Use pypdf to read and merge, and reportlab to create.
  • Always open output PDFs in binary mode with "wb".

🧩 What You’ve Learned

A quick recap of what you can now do.

  • βœ… Install and import pypdf, the go-to library for reading PDFs.
  • βœ… Open a PDF with PdfReader and count its pages.
  • βœ… Pull text off a page with extract_text(), and loop over every page.
  • βœ… Search the extracted text with plain Python like in and .split().
  • βœ… Merge several PDFs into one with PdfWriter and .append().
  • βœ… Know that reportlab is the tool for creating PDFs from scratch.
  • βœ… Spot common traps like text mode, scanned PDFs, and zero-based page numbers.

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    Which class from pypdf do you use to open and read an existing PDF?

    Why: PdfReader opens an existing PDF so you can count and read its pages; PdfWriter is for building a new one.

  2. 2

    What does extract_text() return for a scanned PDF that is really a picture of a page?

    Why: A scanned PDF has no real text inside, only an image, so extract_text() returns an empty string and you need OCR instead.

  3. 3

    Why must you open the output file with "wb" when writing a merged PDF?

    Why: PDFs are binary, not plain text, so writing them in text mode "w" corrupts the file; "wb" writes the bytes correctly.

  4. 4

    Which page does reader.pages[0] give you?

    Why: Page numbering starts at index 0, so reader.pages[0] is the first page and reader.pages[1] is the second.

πŸš€ What’s Next?

You can now read, search, and join PDFs. That is real automation you will use again and again. Next up, learn how to make your scripts run on their own, on a schedule, with no one watching.

Scheduling Python Scripts

Share & Connect