PythonでPDFテキスト抽出 — 日本語帳票のつまずきと工夫

  • URLをコピーしました!

「またPDFからコピペか…」――そんなつぶやき、あなたの職場でも聞こえてきませんか?

帳票やレポートから必要な情報を手作業で抜き出すのって、正直しんどい作業ですよね。

私も最初は「Pythonで自動化できるらしい」と聞いて試してみたのですが、日本語PDFの扱いづらさに結構つまずきました。この記事では、そんな私の試行錯誤を交えつつ、PDFのテキストをうまく抽出するためのライブラリ選びや工夫について、わかりやすく紹介していきます。

この記事を読むとわかること

  • 日本語PDFで文字化けが起きる理由とその対処法
  • 目的別に選べるPDF処理ライブラリの特徴と使いどころ
  • pdfplumberを使った帳票処理の基本とよくあるハマりポイント
目次

PythonでPDFのテキストを抽出する理由と課題

以前、別チームの知人が「PDFからテキストコピペ作業がしんどい・・」とぼやいていたのを思い出します。

毎月届く帳票を開いて、必要な部分を手作業で抜き出していたようで、見ていて正直つらそうでした。ページ数が多いと、それだけで1〜2時間飛ぶこともあるようでした。

そんな場面にこそ、Pythonを使った自動化が役立ちます。とはいえ、PDFは見た目のわりに中身の構造が読みづらく、日本語特有の文字化けやレイアウトの崩れといった問題も起こりがちです。最初に試してみたときも、「思ったより素直にいかないな」と感じたのを覚えています。

日本語PDF特有の難しさとは

私が最初に引っかかったのが「文字化け」でした。

英語のPDFは問題なく抽出できるのに、日本語になると一部が「□□□」になったり、全く取れなかったり。これはフォントの埋め込み状況やエンコード方式が関係しており、日本語フォントが正しく認識されないことが原因です。

さらに、改行の位置や段組構造もPDFによってバラつきがあり、「見た目では整っているけど、中身はバラバラ」というケースも多いです。

このため、PDF処理に取り組む際は「日本語対応」「レイアウトの保持」ができるライブラリを選ぶことが重要になります。

主要ライブラリの特徴と選び方

PythonにはPDF処理のためのライブラリがいくつかありますが、目的によって使い分けが必要です。実務では「全部知っている」よりも「使えるものをちゃんと選ぶ」ほうが大切で、私も用途に応じてライブラリを切り替えています。

それぞれ長所・短所があるため、まずは特徴を掴んでから選ぶことをおすすめします。日本語PDFや表を扱うかどうかでも、適切なツールは変わってきます。

ということで、PDF処理に使われる代表的なライブラリを、用途や特徴別に(独断で)整理しました。細かい違いはあっても、まずは「何をしたいか」を軸に選ぶのが一番だと思います。

ライブラリ名特徴向いている用途日本語対応
PyPDF2ページ操作が得意。抽出精度はやや低め結合・分割・ページ単位の操作
pdfplumberレイアウト保持に強く、見た目に近い抽出テキスト抽出(帳票など)
pdfminer.six文字の位置情報を細かく扱える複雑なレイアウト、カスタム抽出処理
PyMuPDF (fitz)表示・画像含めた抽出に強い図入りPDFや帳票系の読み込み
tabula-py表(テーブル)専用。Javaが必要表形式データの抽出

PyPDF2:基本的なPDF操作に便利だが、テキスト抽出は苦手

PyPDF2は古くからある定番ライブラリで、PDFのページ分割・結合、回転といった「構造操作」に強みがあります。

ただし、テキスト抽出の精度はそれほど高くなく、日本語PDFでは文字が崩れたり、バラバラに取れることも珍しくありません。

from PyPDF2 import PdfReader

reader = PdfReader("sample.pdf")
page = reader.pages[0]
print(page.extract_text())

手軽には使えますが、文字を正確に読み取りたい場合は他のライブラリを検討したほうが無難です。ファイル操作中心の用途に向いています。

pdfplumber:帳票系に強く、日本語レイアウトにも強い味方

pdfplumberは、実務的な帳票処理で非常に頼りになるライブラリです。テキストの配置を保ったまま抽出でき、日本語も比較的正確に取れます。

今回紹介する中でも、初学者にとって扱いやすい選択肢だと思います。

import pdfplumber

with pdfplumber.open("sample.pdf") as pdf:
    for page in pdf.pages:
        print(page.extract_text())

実際、私もこれを使ってPDF帳票の処理を自動化しました。複雑な設定が不要で、まず試してみるにはぴったりのライブラリです。

pdfminer.six:細かい制御ができるが、やや上級者向け

pdfminer.sixは、文字の位置情報やサイズを細かく扱えるのが特徴です。表現力は高いのですが、抽出処理をカスタマイズしようとするとコード量が多くなりがちで、最初は少しハードルを感じるかもしれません。

from pdfminer.high_level import extract_text

text = extract_text("sample.pdf")
print(text)

デフォルトでもある程度は取れますが、「どの位置にあるテキストか」といった情報が欲しい場面で力を発揮します。抽出精度を突き詰めたいときに向いています。

PyMuPDF(fitz):画像や図入りPDFも含めた処理が可能

PyMuPDF(別名fitz)は、PDFのページ内容を画像として扱ったり、テキストの詳細情報を取得したりできる多機能ライブラリです。表示系やグラフィカルな帳票を扱う場合に特に役立ちます。

import fitz  # PyMuPDF

doc = fitz.open("sample.pdf")
page = doc[0]
text = page.get_text()
print(text)

見た目を保った抽出や、ページを画像として保存する処理も簡単にできるので、柔軟性の高いライブラリを探している方におすすめです。

tabula-py:表があるならまず試してほしい一手

PDFに表形式のデータが含まれているなら、tabula-pyが有力候補になります。Javaが必要という難点はありますが、表をDataFrameで取得できる利便性はとても高いです。

import tabula

dfs = tabula.read_pdf("sample.pdf", pages="all", multiple_tables=True)

for df in dfs:
    print(df.head())

ただし、うまく認識できないPDFもあるため、必要に応じてareaなどのパラメータ調整が必要です。安定稼働には少し工夫がいります。


pdfplumberを使ったテキスト抽出の基本実装

比較的軽量で導入しやすく、日本語の帳票でもレイアウトを保ったままテキストが取れる――そんな理由から、冒頭の知人の課題を解決する際はpdfplumberを選びました。

ここでは、実際の使い方を紹介しながら、ページをまたぐ処理や文字化け対策についても触れていきます。

インストールとPDF読み込みの手順

まずはライブラリのインストールから。pdfplumberpipで簡単に導入できます。

pip install pdfplumber

基本的な読み込みは以下のようなコードです。

import pdfplumber

with pdfplumber.open("document.pdf") as pdf:
    first_page = pdf.pages[0]
    print(first_page.extract_text())

extract_text()はそのページのテキストを読み取り、改行も比較的自然に再現してくれます。最初にこの出力を見たとき、「あれ?けっこうそのまま読めるな」とちょっと驚きました。

複数ページをまたぐ処理の書き方

複数ページを一括で処理したい場合は、ループで全ページを走査すればOKです。下記のように書けば、ページごとのテキストをまとめて扱えます。

all_text = ""

with pdfplumber.open("document.pdf") as pdf:
    for page in pdf.pages:
        all_text += page.extract_text() + "\n"

print(all_text)

ループ処理でまとめて読み込むだけですが、大量ページの帳票でもこれだけで処理できるのは嬉しいポイントです。私は出力をそのままCSVにしたり、正規表現でデータ抽出したりしています。

文字化け・レイアウト崩れへの対応法

pdfplumberでも万能ではなく、日本語PDFによっては部分的に文字が欠けたり、レイアウトが崩れたりすることがあります。

その場合、まず試してほしいのが「別のビューワーで保存し直す」こと。Adobe Acrobatなどで「印刷」→PDF保存することで、テキスト情報が補完されることもあります。

また、うまくいかないPDFでは、思い切って他のライブラリ(PyMuPDFやPDFMiner)に切り替えるのも一手。完璧に取れるケースは少ないので、「使える部分だけうまく抜く」ぐらいの柔軟さが必要かもしれません。


実際にやってみて詰まったところ

ここからは、私がpdfplumberを使っていて「思った通りにいかないな」と感じた場面をいくつか紹介します。使い方はシンプルでも、PDFファイルの中身によっては意外と手こずることもありました。

なぜか空文字しか取れないときの対処

extract_text()を呼び出しても、なぜかテキストが空っぽ・・。

初めてこの現象に遭遇したときは、「コードが間違ってる?」と何度も見直しましたが、原因はPDFファイル側にありました。具体的には、文字情報がフォントとして埋め込まれておらず、実際のテキストデータがPDF内部に存在しないケースです。

このような場合は、テキスト抽出ライブラリではどうにもならず、OCR(光学文字認識)を使う必要があります。pytesseractを使えば、PDFページを画像として認識して文字を抽出することが可能です。

from pdf2image import convert_from_path
import pytesseract

images = convert_from_path("unreadable.pdf")

for image in images:
    text = pytesseract.image_to_string(image, lang="jpn")
    print(text)

精度は多少落ちますが、「どうしても読めないPDF」を救済する手段として覚えておくと安心です。

ページによって取れたり取れなかったりする問題

pdfplumberを使って複数ページを処理していると、「あるページではきれいに取れるのに、別のページでは全く取れない」ということがありました。

フォーマットが同じに見えるPDFでも、実際にはページごとに構造が異なっているケースがあります。

私のケースでは、特定のページだけ画像として埋め込まれていて、他はテキストデータだったというパターンがありました。そんなときは、まずpage.extract_text()の戻り値がNoneになっていないか確認するのが第一歩です。

with pdfplumber.open("report.pdf") as pdf:
    for i, page in enumerate(pdf.pages):
        text = page.extract_text()
        if not text:
            print(f"{i+1}ページ目:テキストが取得できませんでした")

こうして確認しておくと、「コードが悪いのか、PDFの中身なのか」が早めに切り分けられるので、無駄にハマらずに済みます。

まとめ

PDFって見た目は整ってるのに、中身は意外とバラバラ。最初は思うように抽出できず戸惑いましたが、慣れてくるとライブラリの特性もつかめて、処理がグッと楽になります。

全部を完璧に取るのは難しくても、「使える情報だけでもサッと抜ける」ようになれば、業務効率はかなり変わります。

今回紹介した内容が、“PDF疲れ”解消のヒントになれば嬉しいです。

この記事を書いた人

業務システムとWebアプリの開発に20年以上携わるフリーランスエンジニア。
製造業や物流業界のシステム保守・改修を中心に、要件定義から運用改善まで幅広く対応してきました。Laravelや業務改善、AI活用など、現場で実際に試し・使い続けている技術や設計の工夫を、トラブル対応の視点も交えてブログに記録しています。

日々の業務で直面した「困ったこと」をベースに、再現性のあるノウハウをシンプルな言葉で伝えることを意識しています。

目次