PythonでPDFを自動処理するコード集|テキスト抽出・表→Excel・結合・透かし・OCR

届く請求書の金額をExcelに記録してるけど…、いまどき手作業ってナンセンスな気がする。よい方法はないかなぁ?

こんな悩みに答えます。

「毎月10社から届くPDF請求書、金額をExcelに手入力しています」

これを聞いたとき、「Pythonで全部自動化できますよ」と言いかけて止まりました。私の悪い癖です。その前に「どのライブラリをどう使うか」を説明しないと始まらないからです。

PDF処理はPythonで確かに自動化できます。でも「テキスト抽出はpdfplumber」「PDF生成はreportlab」「スキャンPDFはtesseract」と、用途によって使うツールが違う。そこを整理しないまま進むと「動かない」「文字化けした」で詰まります。

そこで、この記事では、総務・経理の実務でよく出てくるPDF処理のユースケースに絞って、「何を使えばいいか」と「コピペで動くコード」をまとめます。

結論|用途によってライブラリを使い分ける

PDF処理のライブラリ選択、用途別の答えはこれ。

やりたいこと使うライブラリ難易度
テキスト抽出(普通のPDF)pdfplumber★★☆☆☆
表データ抽出pdfplumber★★★☆☆
PDF結合・分割・ページ操作pypdf★★☆☆☆
PDF生成・透かし挿入reportlab + pypdf★★★☆☆
スキャンPDF(画像PDF)のOCRpytesseract★★★★☆

必要なライブラリのインストール

まず使うライブラリをインストールします。全部まとめてインストールしてもOKです。

pip install pdfplumber
pip install pypdf
pip install reportlab
pip install openpyxl

OCR処理(スキャンPDF)を使う場合は追加で:

pip install pytesseract pillow

※ pytesseractは別途 Tesseract OCR のインストールが必要です(後述)。


テキスト抽出(pdfplumber)——請求書・契約書のテキストを自動取得

基本のテキスト抽出

import pdfplumber

def extract_text_from_pdf(pdf_path):
    """PDFからテキストを抽出して返す"""
    all_text = []

    with pdfplumber.open(pdf_path) as pdf:
        for i, page in enumerate(pdf.pages):
            text = page.extract_text()
            if text:
                all_text.append(f"--- {i+1}ページ ---\n{text}")

    return '\n\n'.join(all_text)

# 実行例
text = extract_text_from_pdf(r'C:\Users\あなたのユーザー名\Documents\請求書.pdf')
print(text)

フォルダ内の全PDFからテキストを一括抽出

import pdfplumber
import os

def extract_all_pdfs(folder_path, output_txt_path):
    """フォルダ内の全PDFからテキストを抽出してテキストファイルに保存"""
    results = []

    pdf_files = [f for f in os.listdir(folder_path) if f.endswith('.pdf')]

    for filename in pdf_files:
        pdf_path = os.path.join(folder_path, filename)

        with pdfplumber.open(pdf_path) as pdf:
            pages_text = []
            for page in pdf.pages:
                text = page.extract_text()
                if text:
                    pages_text.append(text)

        results.append(f"=== {filename} ===\n" + '\n'.join(pages_text))
        print(f"処理完了: {filename}")

    with open(output_txt_path, 'w', encoding='utf-8') as f:
        f.write('\n\n'.join(results))

    print(f"\n{len(pdf_files)}件のPDFを処理しました → {output_txt_path}")

# 実行例
extract_all_pdfs(
    folder_path=r'C:\Users\あなたのユーザー名\Documents\請求書フォルダ',
    output_txt_path=r'C:\Users\あなたのユーザー名\Documents\抽出結果.txt'
)

表データ抽出→Excel書き出し——月次集計の自動化

PDF請求書の中に表形式のデータが含まれている場合、pdfplumberで表を抽出してExcelに書き出せます。

PDF内の表を抽出してExcelに保存

import pdfplumber
import openpyxl
import os
from datetime import datetime

def extract_tables_to_excel(pdf_path, output_excel_path):
    """PDFの全ページから表を抽出してExcelに書き出す"""
    wb = openpyxl.Workbook()
    sheet_count = 0

    with pdfplumber.open(pdf_path) as pdf:
        for page_num, page in enumerate(pdf.pages):
            tables = page.extract_tables()

            for table_num, table in enumerate(tables):
                # シート名:ページ番号_テーブル番号
                sheet_name = f"P{page_num+1}_T{table_num+1}"

                if sheet_count == 0:
                    ws = wb.active
                    ws.title = sheet_name
                else:
                    ws = wb.create_sheet(sheet_name)

                # 表データを書き込む
                for row in table:
                    ws.append([cell if cell is not None else '' for cell in row])

                sheet_count += 1

    if sheet_count == 0:
        print("表が見つかりませんでした")
        return

    wb.save(output_excel_path)
    print(f"{sheet_count}個の表を抽出しました → {output_excel_path}")

# 実行例
extract_tables_to_excel(
    pdf_path=r'C:\Users\あなたのユーザー名\Documents\請求書.pdf',
    output_excel_path=r'C:\Users\あなたのユーザー名\Documents\抽出データ.xlsx'
)

実務活用:複数PDF請求書から金額だけを一括集計

import pdfplumber
import openpyxl
import os
import re

def collect_invoice_amounts(folder_path, output_excel_path):
    """
    フォルダ内の全PDF請求書から「合計金額」を抽出してExcelに集計する
    ※請求書のフォーマットに合わせて正規表現を調整してください
    """
    wb = openpyxl.Workbook()
    ws = wb.active
    ws.title = "請求金額集計"
    ws.append(["ファイル名", "抽出テキスト(金額含む行)", "金額(円)"])

    pdf_files = [f for f in os.listdir(folder_path) if f.endswith('.pdf')]

    for filename in pdf_files:
        pdf_path = os.path.join(folder_path, filename)

        with pdfplumber.open(pdf_path) as pdf:
            full_text = ""
            for page in pdf.pages:
                text = page.extract_text()
                if text:
                    full_text += text

        # 「合計」「請求金額」「御請求金額」などの行を抽出
        amount_line = ""
        amount_value = ""

        for line in full_text.split('\n'):
            if re.search(r'(合計|請求金額|御請求)', line):
                amount_line = line.strip()
                # 数字部分を抽出(カンマ区切り数値)
                numbers = re.findall(r'[\d,]+', line)
                if numbers:
                    # 最大の数値を金額とみなす
                    amounts = [int(n.replace(',', '')) for n in numbers]
                    amount_value = max(amounts)
                break

        ws.append([filename, amount_line, amount_value])
        print(f"処理: {filename} → {amount_value}円")

    wb.save(output_excel_path)
    print(f"\n{len(pdf_files)}件を集計しました → {output_excel_path}")

# 実行例
collect_invoice_amounts(
    folder_path=r'C:\Users\あなたのユーザー名\Documents\請求書フォルダ',
    output_excel_path=r'C:\Users\あなたのユーザー名\Documents\請求金額集計.xlsx'
)

注意: 請求書のフォーマットは会社によって異なります。まず1件のPDFでテキスト抽出を確認し、「合計金額」がどのような形式で書かれているかを確認してから正規表現を調整してください。

PDF結合・分割(pypdf)——月次報告書をまとめる

複数PDFを1ファイルに結合

from pypdf import PdfWriter, PdfReader
import os

def merge_pdfs(input_folder, output_path):
    """フォルダ内の全PDFをファイル名順に結合する"""
    writer = PdfWriter()

    pdf_files = sorted([f for f in os.listdir(input_folder) if f.endswith('.pdf')])

    for filename in pdf_files:
        pdf_path = os.path.join(input_folder, filename)
        reader = PdfReader(pdf_path)

        for page in reader.pages:
            writer.add_page(page)

        print(f"追加: {filename}({len(reader.pages)}ページ)")

    with open(output_path, 'wb') as f:
        writer.write(f)

    print(f"\n結合完了 → {output_path}")

# 実行例
merge_pdfs(
    input_folder=r'C:\Users\あなたのユーザー名\Documents\月次報告書',
    output_path=r'C:\Users\あなたのユーザー名\Documents\月次報告書_結合.pdf'
)

PDFを指定ページで分割

from pypdf import PdfWriter, PdfReader

def split_pdf(input_path, output_folder, pages_per_file=1):
    """PDFを指定ページ数ごとに分割する"""
    import os
    os.makedirs(output_folder, exist_ok=True)

    reader = PdfReader(input_path)
    total_pages = len(reader.pages)
    file_count = 0

    for start in range(0, total_pages, pages_per_file):
        writer = PdfWriter()
        end = min(start + pages_per_file, total_pages)

        for page_num in range(start, end):
            writer.add_page(reader.pages[page_num])

        file_count += 1
        output_path = os.path.join(output_folder, f"分割_{file_count:03d}.pdf")

        with open(output_path, 'wb') as f:
            writer.write(f)

    print(f"{file_count}ファイルに分割しました → {output_folder}")

# 実行例:1ページずつ分割
split_pdf(
    input_path=r'C:\Users\あなたのユーザー名\Documents\大きいPDF.pdf',
    output_folder=r'C:\Users\あなたのユーザー名\Documents\分割後',
    pages_per_file=1
)

PDF透かし・スタンプ挿入(reportlab + pypdf)

「承認済み」「社外秘」「CONFIDENTIAL」などのスタンプを大量のPDFに一括挿入します。

from pypdf import PdfWriter, PdfReader
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
import io
import os

def create_watermark(text, output_io, opacity=0.3):
    """透かし用PDFをメモリ上に作成"""
    c = canvas.Canvas(output_io, pagesize=A4)
    width, height = A4

    # 透かしの設定
    c.saveState()
    c.setFillAlpha(opacity)  # 透明度
    c.setFillColorRGB(0.7, 0.7, 0.7)  # グレー

    # テキストを中央斜めに配置
    c.translate(width/2, height/2)
    c.rotate(45)  # 45度回転
    c.setFontSize(60)

    # 日本語フォントを使う場合は登録が必要
    # pdfmetrics.registerFont(TTFont('IPAGothic', 'C:/Windows/Fonts/msgothic.ttc'))
    # c.setFont('IPAGothic', 60)

    c.drawCentredString(0, 0, text)
    c.restoreState()
    c.save()

def add_watermark_to_pdf(input_path, output_path, watermark_text="社外秘"):
    """PDFの全ページに透かしを追加"""
    # 透かし用PDFをメモリ上に作成
    watermark_io = io.BytesIO()
    create_watermark(watermark_text, watermark_io)
    watermark_io.seek(0)
    watermark_reader = PdfReader(watermark_io)
    watermark_page = watermark_reader.pages[0]

    # 元のPDFに透かしを合成
    reader = PdfReader(input_path)
    writer = PdfWriter()

    for page in reader.pages:
        page.merge_page(watermark_page)
        writer.add_page(page)

    with open(output_path, 'wb') as f:
        writer.write(f)

    print(f"透かし挿入完了 → {output_path}")

def add_watermark_to_folder(input_folder, output_folder, watermark_text="社外秘"):
    """フォルダ内の全PDFに透かしを一括挿入"""
    os.makedirs(output_folder, exist_ok=True)

    pdf_files = [f for f in os.listdir(input_folder) if f.endswith('.pdf')]

    for filename in pdf_files:
        input_path = os.path.join(input_folder, filename)
        output_path = os.path.join(output_folder, filename)
        add_watermark_to_pdf(input_path, output_path, watermark_text)
        print(f"処理: {filename}")

    print(f"\n{len(pdf_files)}件処理しました")

# 実行例
add_watermark_to_folder(
    input_folder=r'C:\Users\あなたのユーザー名\Documents\元のPDF',
    output_folder=r'C:\Users\あなたのユーザー名\Documents\透かし済みPDF',
    watermark_text="社外秘"
)

スキャンPDFのOCR処理(pytesseract)——画像PDFからテキストを取り出す

紙の書類をスキャンしたPDF(画像PDF)は、pdfplumberではテキストが取得できません。そういう場合はOCRを使います。

事前準備

  1. Tesseract OCR をインストール(Windowsの場合)
  2. 日本語データをインストール(インストーラーで「Japanese」を選択)
pip install pytesseract pillow pdf2image

pdf2imageは別途 poppler のインストールも必要です(Windowsの場合はこちらからDL)。

スキャンPDFからテキストを抽出するコード

import pytesseract
from pdf2image import convert_from_path
import os

# Tesseractのパスを指定(インストール先に合わせて変更)
pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'

def ocr_pdf(pdf_path, lang='jpn+eng'):
    """
    スキャンPDFからOCRでテキストを抽出
    lang: 'jpn' = 日本語のみ, 'jpn+eng' = 日本語+英語
    """
    # PDFを画像に変換
    images = convert_from_path(
        pdf_path,
        dpi=300,  # 高解像度ほど精度が上がる
        poppler_path=r'C:\poppler\bin'  # popplerのパス(インストール先に合わせて変更)
    )

    all_text = []
    for i, image in enumerate(images):
        text = pytesseract.image_to_string(image, lang=lang)
        all_text.append(f"--- {i+1}ページ ---\n{text}")
        print(f"{i+1}ページ 処理完了")

    return '\n\n'.join(all_text)

# 実行例
text = ocr_pdf(r'C:\Users\あなたのユーザー名\Documents\スキャン書類.pdf')
print(text)

処理時間の目安: A4 1ページあたり5〜10秒程度(PCのスペックにより異なります)。

日本語PDFで文字化けするときの対処法

日本語PDFで pdfplumber を使っても文字化けしたり空白になる場合があります。

原因と対処法

原因状態対処法
スキャンPDFテキストが画像として埋め込まれているpytesseractでOCR処理
フォント埋め込みなしテキストはあるが文字コードが壊れているpdfminer.six で試す
セキュリティロックパスワード保護されているパスワードを解除してから処理
特殊フォント文字マッピングが崩れているAdobe AcrobatでPDFを開き直して保存

pdfminer.sixで試す場合

pip install pdfminer.six
from pdfminer.high_level import extract_text

text = extract_text(r'C:\Users\あなたのユーザー名\Documents\文字化けPDF.pdf')
print(text)

pdfplumberで文字化けするPDFでも、pdfminer.sixでうまく取れる場合があります。両方試してみてください。

よくある質問

Q. pdfplumberとPyPDF2の違いは何ですか?

pdfplumber はテキスト抽出・表抽出に特化しており、日本語PDFの扱いが比較的安定しています。pypdf(旧PyPDF2)はページ操作(結合・分割・回転)に特化しています。テキスト抽出なら pdfplumber、ページ操作なら pypdf を使うのが定番の組み合わせです。

Q. パスワード付きPDFを処理できますか?

できます。pypdfでパスワードを指定して開けます。

from pypdf import PdfReader
reader = PdfReader('パスワード付き.pdf')
reader.decrypt('パスワード')
text = reader.pages[0].extract_text()

Q. スキャンPDFのOCR精度が悪いです。改善できますか?

解像度(dpi)を上げる(300→400)と精度が上がることが多いです。また、画像をグレースケール変換・コントラスト強調してからOCRにかける前処理が有効です。Pillowで image.convert('L') でグレースケール変換できます。

Q. PDFのページ番号を変更したいです。

pypdfでは直接ページ番号テキストを変更することは難しいです。reportlabで新しいPDFを生成するか、既存のPDFに番号テキストをオーバーレイする方法があります。


まとめ

  • テキスト抽出: pdfplumber を使う。日本語PDFに安定
  • 表抽出→Excel: pdfplumber + openpyxl で月次集計の自動化ができる
  • PDF結合・分割: pypdf で操作
  • 透かし挿入: reportlab で透かしPDFを作り、pypdf で合成
  • スキャンPDF: pytesseract でOCR処理。準備に時間がかかるが強力
  • 文字化け: pdfplumber → pdfminer.six の順で試す

PDF処理を自動化すると、「毎月10件の請求書金額を手入力する」「複数の報告書を1つに結合する」といった作業から完全に解放されます。まずテキスト抽出だけでも試してみてください。

関連記事

PDF処理の自動化は、総務・経理の業務自動化の入口の一つです。

請求書作成・PDF保存・メール送付まで自動化する方法
WindowsPCでPythonを始める方法(総務部向け)
総務DX、何から始める?
Python・DX経験を転職でアピールしたい方はこちら


この記事は、総務部での実務経験をもとに執筆しています。月次のPDF請求書処理を自動化した経験から、非エンジニアでも使える形でコードをまとめています。