PDF EXTRACTION

PythonでPDFからテキストを抽出する

pypdf・pdfplumber・PyMuPDF の3つで同じ日本語PDFを読み、速度と表・2段組の出方を比べました。抽出できないPDFを見分けるコードと、フォルダ内のPDFを横断検索するコードも全文を載せています。

🎯 対象: PDFの文字をPythonで取り出したい初心者〜初級者 ⏱️ 読了: 約10分 🧪 実行確認: Windows 11・Python 3.12.10

1. 結論:文字はpypdf、表はpdfplumber、速さはPyMuPDF

今回の検証PDF(日本語・テキスト層あり)は3つとも化けずに読めました。差は表・段組みの出方、速度、ライセンスです。

✅
判断の軸は「文字だけか、形も要るか」

本文の文字だけなら pypdf。表を行と列のまま取りたい・文字の位置も使いたいなら pdfplumber。大量のPDFを短時間で処理したい・ページの画像化もしたいなら PyMuPDF。PyMuPDF を選ぶ場合は下のライセンス欄も確認してください。

やりたいこと担当理由
本文を文字列にして検索・保存するpypdf純Pythonで pip だけで入る
PDFの結合・分割もしたいpypdf書き出しもできる(pdfplumber は抽出・解析専用で書き出し機能がない)
罫線つきの表を行・列のまま取り出すpdfplumberextract_tables() が表を崩さず復元した
文字の位置(座標)で範囲を切り出すpdfplumberextract_words() が単語ごとに位置を返す
大量のページを短時間で読むPyMuPDF60ページの抽出が46ms(pypdf 177ms・pdfplumber 504ms)
ページの画像化・表示もしたいPyMuPDF描画エンジン MuPDF のバインディング
パッケージ(2026年9月29日時点)最新版ライセンス対応Python
pypdf6.19.0BSD-3-Clause3.9以上
pdfplumber0.11.10MIT3.10以上(依存の pdfminer.six による)
PyMuPDF1.28.2AGPL-3.0 または Artifex 商用3.10以上
⚠️
PyMuPDF を組み込んだツールを配る・公開するとき

PyMuPDF は AGPL-3.0 と商用のデュアルライセンスです。提供元の Artifex はライセンス案内で、サーバー型のアプリやサービスに組み込むなら自分のアプリのソースを AGPL で開示する必要があるという見解を示しています。配布やサーバー提供を考えているなら、AGPL の条件を満たせるか、商用ライセンスが必要かを公式の案内で確認してください。当サイトは法的な解釈を示す立場にありません。

2. 3つの違い:同じPDFを抽出して比べた

ℹ️
検証環境(2026年9月29日)

Windows 11 / Python 3.12.10 / pypdf 6.19.0 / pdfplumber 0.11.10 / PyMuPDF 1.28.2。検証PDFは reportlab でメイリオを埋め込んで作った日本語3ページ(段落・罫線つきの表・2段組)と、それを150dpiの画像にしたスキャン相当のPDFです。自作PDFでの結果なので、Wordや複合機のPDFでは出方が変わることがあります。

pip install pypdf pdfplumber pymupdf

全ページの文字を1つの文字列にする最小コードです。PyMuPDF は import pymupdf と書きます(旧名 fitz は第5章)。

# pypdf
from pypdf import PdfReader
reader = PdfReader("sample_text.pdf")
text = "\n".join(page.extract_text() for page in reader.pages)

# pdfplumber
import pdfplumber
with pdfplumber.open("sample_text.pdf") as pdf:
    text = "\n".join((page.extract_text() or "") for page in pdf.pages)
    tables = pdf.pages[1].extract_tables()

# PyMuPDF
import pymupdf
with pymupdf.open("sample_text.pdf") as doc:
    text = "\n".join(page.get_text() for page in doc)

速度:60ページで46ms〜504ms

3ページ版は10回、60ページ版(同じPDFを20回連結)は開いてから全ページ抽出までを5回測った中央値です。

ライブラリ3ページ60ページ
pypdf extract_text()9.2ms177ms
pypdf(layout モード)8.6ms160ms
pdfplumber extract_text()18.2ms504ms
PyMuPDF get_text()2.0ms46ms

この検証PDFでは PyMuPDF が最も短く、最も長い pdfplumber でも60ページで約0.5秒でした。数ファイルなら速度より「出方」で選べば十分です。

出方の違い一覧

項目pypdf(既定)pdfplumberPyMuPDF(既定)
表1セル1行に分解1行1レコード(半角スペース区切り)1セル1行に分解
2段組左右の行が交互に出る同じ高さの左右を1行に連結左右の行が交互に出る
合字(fi・fl)合字のままfi・fl に分解合字のまま
画像だけのPDF3つとも空文字(エラーなし)

pypdf の extraction_mode="layout" は空白で紙面の配置を再現し、文字数は425字から584字に増えました。公式は実験的な機能としています(API の説明)。

表:行と列のまま欲しいなら extract_tables()

2ページ目の表を extract_text() で読むと、pypdf と PyMuPDF は「月/対応件数/平均時間(分)/…」とセルが縦に16行並びました。pdfplumber は 7月 98 14.2 通常 の1行1レコードですが、半角スペース区切りなのでセル内に空白があると列の境目が曖昧になります。表として取るなら pdfplumber の page.extract_tables() で、今回の表は完全に復元されました。

[[['月', '対応件数', '平均時間(分)', '備考'],
  ['7月', '98', '14.2', '通常'],
  ['8月', '114', '13.8', '夏季休業あり'],
  ['9月', '128', '15.1', '月末集中']]]

「表 → 行 → セル」の入れ子リストなので、csv.writer に渡せばCSVで保存できます(数字も文字列で返る点に注意)。PyMuPDF の page.find_tables() でも同じ表を取り出せましたが、実行時に pymupdf_layout パッケージを勧める案内が出ました。

2段組:どれも「読む順」は保証しない

3ページ目の2段組は、pypdf と PyMuPDF の既定では左右の行が1行ずつ交互に出ました。pdfplumber は 左段: 抽出対象のPDFは 右段: 同じ見た目でも と同じ高さの左右を1行につなげます。どちらも「左段を読み終えてから右段」の順にはならず、PyMuPDF の get_text(sort=True) も空白で左右の位置を再現するところまでです。

3. 抽出できないPDFの見分け方とOCRの選択肢

pypdf の公式(Extract Text)のとおり、スキャンしただけのPDFは中身が画像なので文字を抽出できません。厄介なのは、読んでもエラーにならないことです。スキャン相当のPDFは4通りの読み方すべてで3ページとも空文字 '' が返り、例外は出ませんでした。検索に混ざると「該当なし」と区別できないので、先にテキスト層の有無を判定します。

# -*- coding: utf-8 -*-
"""PDFにテキスト層があるかを判定する(記事掲載用コード)"""
import sys
from pathlib import Path
from pypdf import PdfReader

def has_text_layer(path, min_chars=20):
    """先頭3ページのどれかに min_chars 文字以上の文字があればテキスト層ありとみなす"""
    reader = PdfReader(path)
    if reader.is_encrypted:
        return None  # パスワードが要る(判定不能)
    for page in reader.pages[:3]:
        text = (page.extract_text() or "").strip()
        if len(text) >= min_chars:
            return True
    return False

if __name__ == "__main__":
    sys.stdout.reconfigure(encoding="utf-8")
    folder = Path(sys.argv[1] if len(sys.argv) > 1 else ".")
    for pdf in sorted(folder.glob("*.pdf")):
        result = has_text_layer(pdf)
        label = {True: "テキスト層あり", False: "テキスト層なし(OCRが必要)", None: "暗号化(要パスワード)"}[result]
        print(f"{pdf.name}: {label}")

has_text_layer.py として保存し、フォルダを引数に渡します。通常・スキャン相当・暗号化の3ファイルでの出力です。

> python has_text_layer.py docs_dir
locked.pdf: 暗号化(要パスワード)
report_2026_09.pdf: テキスト層あり
scanned_memo.pdf: テキスト層なし(OCRが必要)

「先頭3ページに20文字以上」は筆者の目安です。表紙だけ文字があるPDFも「あり」になるので、全ページ見るなら reader.pages[:3] を reader.pages に変えます。reconfigure の行は、Windows のコンソールで表示できない文字による停止を防ぎます。

テキスト層がなかったときの選択肢

画像の文字を読むにはOCR(光学文字認識)が要ります。代表的な方法は次の3つで、どれも OCR エンジン Tesseract 本体の別途インストールが必要です。当サイトではOCRは未検証で、紹介のみです。

方法向いている使い方
pytesseractページ画像から文字を読む(日本語は jpn、縦書きは jpn_vert の言語データ)
ocrmypdfスキャンPDFに文字の層を足し、検索できるPDFにするコマンドラインツール
PyMuPDF の get_textpage_ocr()いつもの get_text() の流れでOCRする

PyMuPDF の公式は、OCRは通常の抽出の約1000倍遅いと説明しています(OCR のレシピ)。OCRは読み間違いも起こり得るため、判定で「なし」のファイルだけをOCRに回すと時間と誤読を減らせます。

Tesseract 本体と日本語データのOS別の入れ方は、『退屈なことはPythonにやらせよう 第3版』22章「画像中の文字認識」の書評記事の解説にまとめています。リンク先は当サイトの書評記事で、アフィリエイト広告を含みます。

5. つまずきやすい点(暗号化・合字・縦書き・旧名)

(1) パスワード付きのPDF

AES-256で暗号化し、閲覧パスワードを付けたPDFでの結果です。

ライブラリパスワードなしで読むパスワードの渡し方
pypdfFileNotDecryptedErrorreader.decrypt(pw)(誤りなら0、今回の正解では2)
pdfplumberopen() で PdfminerExceptionpdfplumber.open(path, password=pw)
PyMuPDFValueError: document closed or encrypteddoc.authenticate(pw)(0なら失敗、今回の正解では6)

pypdf でAES暗号化を解くには cryptography が必要で、単体なら pip install pypdf[crypto] で入ります(公式のインストール手順)。今回は pdfplumber の依存として入っていました。

(2) 合字:「efficient」で検索しても見つからない

「fi」「fl」は、作成ソフトやフォントによっては1文字の合字(fi・fl)で埋め込まれます。検証PDFの「efficient」は、pypdf と PyMuPDF では合字のまま出て "efficient" in text が False でした。pdfplumber は自動で分解します。unicodedata.normalize("NFKC", ...) をかけると、3つとも True になりました。

import unicodedata

text = "英単語の合字 efficient / flow"
print("efficient" in text)                                 # False
print("efficient" in unicodedata.normalize("NFKC", text))  # True

副作用として、NFKC は半角カナを全角に、全角英数字を半角に変えます(カタカナ ABC123 → カタカナ ABC123、UAX #15)。原文は保存し、検索用のコピーと検索語にだけかけるのが無難です。

(3) 縦書きのPDF

縦書きは当サイトで未検証です。公式の仕様で確認できたのは次の3点です。

  • pypdf の layout モードは、回転した文字を既定で除外する
  • pdfplumber は既定値が横書き前提(line_dir・char_dir 引数で変更可)
  • PyMuPDF は get_text("dict") の各行に、縦書きかどうか(wmode)を返す

縦書きが多いなら、自分のPDFで3つを試してください。

(4) 文字化けや (cid:数字) が出る

PDF内の「字形と文字コードの対応表」が不完全だと、どのライブラリでも正しい文字に戻せない場合があります。pdfminer.six(pdfplumber の土台)のFAQは、この場合 (cid:x) の形で出ると説明しています。目安は「PDFビューアーでコピー&貼り付けして読めるか」で、貼っても化けるならOCRが近道です。

(5) PyPDF2 も import fitz も旧名

古い記事でよく見る PyPDF2 は pypdf の旧名です。2022年12月の pypdf 3.1.0 で名前が pypdf に戻り、PyPDF2 は3.0.1で更新が止まりました(3.1.0 のリリースノート)。PyPDF2 も今なお入りますが、新しく書くなら pip install pypdf です。

PyMuPDF も 1.24.3 から import pymupdf が正式な書き方です。1.28.2 で import fitz を使うと、動作はしますが次の警告が出ました。

The `fitz` API is deprecated and will be removed in future. Use `import pymupdf` instead.

6. 画面付きのツールにしたいなら

PDFを扱う中級者向けアプリの記事(完成コードつき)が参考になります。

記事この記事との対応
PDFビューアーPyMuPDF でページを画像化して表示。コードは旧名 import fitz なので、1.28.2 では第5章(5)の警告が出る
PDFマージツールpypdf で複数のPDFを1つにまとめる
請求書作成アプリPDFを「作る」側。本記事の検証PDFも同じ reportlab で生成

ほかの定番ライブラリはPythonライブラリ一覧にあります。

PDFからOCRまで一冊で通すなら、17章「PDFとWord文書」と22章のOCRを含む『退屈なことはPythonにやらせよう 第3版』(2026年3月刊)が候補です。章構成は書評記事、紹介は参考書比較ページにあります。リンク先は当サイトの書評記事・書籍紹介ページで、アフィリエイト広告を含みます。

7. よくある質問(FAQ)

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

同じプロジェクトの旧名と現在の名前です。2022年12月の pypdf 3.1.0 で改名され、PyPDF2 は3.0.1で更新が止まりました。新しいコードは from pypdf import PdfReader で書きます。

Q. スキャンしたPDFから文字を取り出せますか

pypdf・pdfplumber・PyMuPDF だけでは取り出せません。中身が画像のため、検証ではどれも空文字を返しました。OCR が必要で、本記事では Tesseract を使う方法(pytesseract・ocrmypdf・PyMuPDF の OCR 機能)を紹介しています。

Q. PDFの表をCSVやExcelに変換できますか

罫線のある表なら、pdfplumber の extract_tables() で行と列のリストとして取り出し、csv モジュールや pandas で保存できます。当サイトで検証したのは罫線つきの表だけなので、罫線のない表では結果を目で確認してください。

Q. PyMuPDF は仕事で使っても大丈夫ですか

PyMuPDF は AGPL-3.0 と商用のデュアルライセンスです。ツールの配布やWebサービスでの提供では、AGPL の条件を満たすか商用ライセンスが必要になり得るため、Artifex の公式案内や社内の担当部署で確認してください。合わなければ BSD-3-Clause の pypdf や MIT の pdfplumber が候補です。

本記事は、生成AIを活用して下書きし、運営者が内容を確認・編集したうえで公開しています。AIの利用方針は免責事項をご覧ください。

8. まとめ

  • 文字だけなら pypdf、表や位置も要るなら pdfplumber、大量処理なら PyMuPDF(配布前にライセンスを確認)
  • 画像だけのPDFはエラーにならず空文字が返る。検索の前にテキスト層を判定し、「なし」だけをOCRに回す
  • 合字は NFKC で揃うが、半角・全角の区別も消えるので検索用のコピーにだけかける
  • PyPDF2 は旧名。新しく書くなら pypdf、PyMuPDF は import pymupdf

まずは手元のPDFフォルダで第3章の判定コードを流してください。