PDF からテキストを抽出する Python コード例です。
Python から Xpdf tools の pdftotext.exe を呼び出して、テキストを抽出します。
pdftotext.exe は、処理が速くて、大量の PDF を扱うときにとても有用でした。
Python ライブラリの pdfminer.six や PyPDF2 も良かったのですが、たくさんの PDF ファイル(たとえば 30 万以上)を処理する用途では、少し時間がかかり過ぎました。
コマンドラインツールの pdftotext.exe は、速さを求めるときにおすすめです。
ほかのプログラミング言語からでも使うことができると思います。
pdftotext.exe をサブプロセス(外部プログラム、外部コマンド、外部アプリケーション)として実行するだけでした。
なので、自分は試していませんが、Ruby、C# (csharp)、go 言語 (golang)、コマンドライン (cmd.exe)、パワーシェル (Powershell) などからでも使用できると思います。
テキストの抽出ができない PDF には qpdf.exe を組み合わせます
テキストの抽出ができない PDF は、『qpdf.exe』を使用してからpdftotext.exe を使用すると、うまくいく場合がありました。
以下の記事で qpdf.exe の使い方を書きました。
⇒ 【Python】PDF を正規表現で検索するコード例【qpdf, pdftotext】
pdftotext.exe を取得 & 日本語設定
取得
『pdftotext.exe』は、Xpdf tools というコマンドラインツールに含まれていました。
Xpdf tools は、Windows 用の『Windows 32/64-bit』を使いました。
以下のページの『Download the Xpdf command line tools:』から取得します。
Xpdf tools ダウンロードページ
https://www.xpdfreader.com/download.html
自分が取得したときは、『xpdf-tools-win-4.02.zip』でした。
あわせて、日本語対応のための言語サポートパッケージも取得します。
同じページにある『Download language support packages for Xpdf:』から取得します。
言語サポートパッケージは、『Japanese』を使いました。
自分が取得したときは、『xpdf-japanese.tar.gz』というファイルでした。
日本語設定
『Xpdf tools』と『言語サポートパッケージ』を、以下のようなフォルダ構成で解凍します。
インストーラーなどは無いので、好きなフォルダに配置してOKです。
バージョン番号などを削って短くしても大丈夫です。
Xpdf tools フォルダ(例)
F:\project\tools\xpdf-tools-win-4.02
言語サポートパッケージ フォルダ(例)
F:\project\tools\xpdf-tools-win-4.02\japanese
日本語設定の方法は、言語サポートパッケージ (xpdf-japanese.tar.gz) の中の『README』に載っていました。
上記のフォルダ構成に合わせて要約すると、手順は以下の通りです。
(1. と 2. のリネームはしなくても動いたのですが、一応、READMEの内容に合わせてリネームします。重要なのは 3. の中身です。)
- 『xpdf-japanese』を『japanese』にリネーム。
- 『add-to-xpdfrc』を『F:\project\tools\xpdf-tools-win-4.02』にコピーして、『xpdfrc』にリネーム。
- 『xpdfrc』の中身を以下のように変更。
変更前
#----- begin Japanese support package (2011-sep-02)
cidToUnicode Adobe-Japan1 /usr/local/share/xpdf/japanese/Adobe-Japan1.cidToUnicode
unicodeMap ISO-2022-JP /usr/local/share/xpdf/japanese/ISO-2022-JP.unicodeMap
unicodeMap EUC-JP /usr/local/share/xpdf/japanese/EUC-JP.unicodeMap
unicodeMap Shift-JIS /usr/local/share/xpdf/japanese/Shift-JIS.unicodeMap
cMapDir Adobe-Japan1 /usr/local/share/xpdf/japanese/CMap
toUnicodeDir /usr/local/share/xpdf/japanese/CMap
#fontFileCC Adobe-Japan1 /usr/..../NotoSansCJKjp-Regular.otf
#----- end Japanese support package
変更後
#----- begin Japanese support package (2011-sep-02)
cidToUnicode Adobe-Japan1 "F:\project\tools\xpdf-tools-win-4.02\japanese\Adobe-Japan1.cidToUnicode"
unicodeMap ISO-2022-JP "F:\project\tools\xpdf-tools-win-4.02\japanese\ISO-2022-JP.unicodeMap"
unicodeMap EUC-JP "F:\project\tools\xpdf-tools-win-4.02\japanese\EUC-JP.unicodeMap"
unicodeMap Shift-JIS "F:\project\tools\xpdf-tools-win-4.02\japanese\Shift-JIS.unicodeMap"
cMapDir Adobe-Japan1 "F:\project\tools\xpdf-tools-win-4.02\japanese\CMap"
toUnicodeDir "F:\project\tools\xpdf-tools-win-4.02\japanese\CMap"
#fontFileCC Adobe-Japan1 /usr/..../NotoSansCJKjp-Regular.otf
#----- end Japanese support package
シャープ『#』から始まる行は削っても大丈夫でした。
begin、fontFileCC、endの行がありますが、全て削除しても大丈夫です。
ファイルパスとフォルダパスは、すべて絶対パスで指定します。
絶対パスさえ合っていれば、途中のフォルダ名などは何でもOKでした。
そして、相対パスは使えないようです。色々試したのですが、うまく行かなかったです。
この『xpdfrc』へのファイルパスは、パイソンから呼び出すときのコマンドラインリストに追加します。
コード例
PDF からテキストを抽出する Python コード例です。
標準出力 (stdout) から、抽出結果を受け取っています。
"""
PDFからテキストを抽出するPythonコード例。
Python から Xpdf tools の pdftotext.exe を呼び出して抽出します。
"""
from pathlib import Path
from subprocess import run, PIPE
def main():
"""メイン関数"""
# (1/7) PDF ファイルを決める
pdf_file = Path(r'F:\project\sample.pdf')
# (2/7) 実行ファイル pdftotext.exe のファイルパスを決める
exe_file = Path(r'F:\project\tools\xpdf-tools-win-4.02\bin64\pdftotext.exe')
# (3/7) 日本語サポートパッケージの xpdfrc のファイルパスを決める
xpdfrc_file = Path(r'F:\project\tools\xpdf-tools-win-4.02\xpdfrc')
# (4/7) コマンドラインの引数リストを作る(タプルでもOK)
# 構文: pdftotext [options] [PDF-file [text-file]]
# テキストを標準出力(stdout)に出すときは、
# [text-file] にハイフン '-' を指定するとのこと。
cmd = (
exe_file,
'-cfg', xpdfrc_file,
'-enc', 'UTF-8', # テキストを出力する時のエンコーディング
# '-q', # メッセージやエラーを表示しない
'-nopgbrk', # '-nopgbrk': テキストに改ページを表す文字列を付けない
pdf_file,
'-', # テキストを標準出力(stdout)に出す
)
# (5/7) PDF からテキストを抽出する
# cp は CompletedProcess の略です。
cp = run(
cmd,
stdout=PIPE, # 標準出力からテキスト受け取るために PIPE を指定
)
# (6/7) もし pdftotext.exe がエラーを返したときは知らせる
if cp.returncode == 0:
# 正常に終了した。
# ただし、壊れたPDFでも 0 が返る。その代わり、処理中に
# Syntax Error (50): Illegal character <2f> in hex string
# などのエラーメッセージが表示された。
# エラーメッセージは '-q' のオプションで非表示にできた。
print(f'ok - {cp.returncode} - {pdf_file.name}')
else:
# なんらかのエラー error を検出した。
# (例) PDFが開けなかった、テキストの出力ファイルが開けなかった、
# PDFのパーミッションに関するエラーでPDFが開けなかった、など。
print(f'error - {cp.returncode} - {pdf_file.name}')
# (7/7) 抽出したテキストデータを受け取る & デコードする
text = cp.stdout.decode('utf-8')
# text = cp.stdout.decode('utf-8', errors='backslashreplace')
text = text.strip() # 先頭と末尾の空白や改行を除去する
# (デバッグ情報)
print(f'(デバッグ) exe_file: {exe_file}')
print(f'(デバッグ) xpdfrc_file: {xpdfrc_file}')
print(f'(デバッグ) pdf_file: {pdf_file}')
print(f'(デバッグ) cp.returncode: {cp.returncode}')
print(f"(デバッグ) cp.stdout.decode('utf-8'):『{text}』")
# (終了)
print('end')
return
if __name__ == "__main__":
main()
サンプル PDF
テキストの抽出に使用した PDF のスクリーンショットです (sample.pdf)。
実行結果
テキストの抽出結果です。
ok - 0 - sample.pdf
(デバッグ) exe_file: F:\project\tools\xpdf-tools-win-4.02\bin64\pdftotext.exe
(デバッグ) xpdfrc_file: F:\project\tools\xpdf-tools-win-4.02\xpdfrc
(デバッグ) pdf_file: F:\project\sample.pdf
(デバッグ) cp.returncode: 0
(デバッグ) cp.stdout.decode('utf-8'):『PDF からテキストを抽出する Python
コード例 サンプル PDF (sample.pdf) あいうえお かきくけこ さしすせそ アイウエオ
カキクケコ サシスセソ』
end
期待した通り、PDF ファイルから『テキストデータ』を取得することができました。
Python マニュアル
コード例で使用した機能のマニュアルです。
Python の subprocess.run() を使用して pdftotext.exe を呼び出しました。
PDF から抽出したテキストデータは、標準出力から受け取ることができました。
pdftotext.exe の標準出力から、データを受け取る方法です。
stdout に subprocess.PIPE を指定したらできました。
サブプロセス ドット パイプ
(Python) subprocess.PIPE
subprocess.run() の戻り値の説明です。
サブプロセス ドット コンプリーテッド プロセス
(Python) class subprocess.CompletedProcess
テキストデータは、戻り値の .stdout
に、バイナリデータとして入っていました。
.stdout.decode('utf-8')
などと書くことで、普通の文字列に変換することができました。
デコードメソッド .decode()
の引数には、pdftotext.exe の呼び出しオプションで指定したエンコーディングを使います。
pdftotext.exe のほうは、大文字と小文字を区別していました。
'UTF-8'
なら 'UTF-8'
、'Shift-JIS'
なら 'Shift-JIS'
しか受け付けませんでした。
大文字と小文字が間違っていた時は、以下のようなエラーが出ました。
Syntax Error: Couldn't find unicodeMap file for the 'SHIFT-JIS' encoding
Config Error: Couldn't get text encoding
pdftotext.exe マニュアル
pdftotext.exe のマニュアルの場所です。
(www.xpdfreader.com) XpdfReader – pdftotext(1)
PDF のテキストを標準出力に出す方法です。
マニュアルの DESCRIPTION のところに、やり方が載っていました。
If text-file is
'-'
, the text is sent to stdout.
コード例で使用したオプションの説明です。
-cfg config-file
指定したコンフィグファイル (xpdfrc) を読み込む。
-enc encoding-name
テキストを出力するときのエンコーディング名。
Python とちがって、大文字と小文字が区別されます。
たとえば、'Shift-JIS'
や 'EUC-JP'
などは、xpdfrc の中に書かれている通りに書くと動きました。
'shift-jis'
などと書いたら、Syntax Error が表示されました。
-q
メッセージやエラーを表示しない。
-nopgbrk
ページとページの間に『改ページを表す文字列 (form feed characters)』を追加しない。
ほかにも色々なオプションがありました。
xpdfrc マニュアル
日本語サポートパッケージの『コンフィグファイル (xpdfrc)』のマニュアルの場所です。