[解決!Python]PDFファイルからテキストや画像を抽出するには:解決!Python
pdfminer.sixパッケージを用いて、PDFファイルからテキストや画像を抽出する方法を紹介する。
from pdfminer.high_level import extract_text
from pathlib import Path
# PDFファイルからテキストを抽出
source = Path('atmarkit_ebook116.pdf')
text = extract_text(source)
print(text)
# extract_text_to_fp関数を使う
from pdfminer.high_level import extract_text_to_fp
dest = Path('out.txt')
with open(source, 'rb') as fp_in, open(dest, 'wb') as fp_out:
extract_text_to_fp(fp_in, fp_out)
print(Path('out.txt').read_text(encoding='utf-8'))
# extract_text_to_fp関数でメモリにテキストを読み込むにはStringIOを使う
from io import StringIO
fp_out = StringIO()
with open(source, 'rb') as fp_in:
extract_text_to_fp(fp_in, fp_out)
text = fp_out.getvalue()
text = text.replace(chr(12), chr(10))
print(text)
# 画像ファイルの保存
output_dir = 'imgs'
fp_out = StringIO()
with open(source, 'rb') as fp_in:
text = extract_text_to_fp(fp_in, fp_out, output_dir=output_dir)
PDFからテキストを抽出するのに使えるパッケージ
PythonでPDFファイルからテキストや画像を抽出するのに使えるパッケージは多数存在する。有名なものとしては以下のものが挙げられるだろう。
これらのうち、今回はpdfminerを使ってPDFファイルからテキストや画像を抽出する方法を紹介する。
pdfminer.six
pdfminer.sixはYusuke Shinyma氏が開発していたpdfminerの開発が停滞していたのを、コミュニティーがフォークしたものだ。2020年まではPython 2しかサポートされていなかったpdfminerに対して、(Python 2と3の両者で動作するコードを書きやすくするための)sixパッケージを用いてPython 3をサポートするなどの拡張が行われている。現在では、Python 2がend of lifeを迎えたことから、pdfminer.sixもそのサポートを終了し、pdfminer.sixの「.six」は意味がなくなっているが、名前としては残されている。
pdfminer.sixはPythonには標準で添付されていないので、使うにはインストールが必要である。これには「pip install pdfminer.six」「py -m pip install pdfminer.six」などのコマンドラインを実行する必要がある。また、画像も抽出したいのであれば、「pip install 'pdfminer.six[image]'」「py -m pip install 'pdfminer.six[image]'」などのコマンドラインを実行する(Pillowパッケージが追加でインストールされるようなので、既にこれをインストールしているのであれば、最初のコマンドラインでも十分だろう)。
pdfminer.sixのドキュメントにはそれほど多くの情報が記述されていないので、このパッケージが提供する多くの関数やクラスについてはリポジトリ上のソースコードを読み解いていくしかない。ただし、テキストや画像の抽出であれば、pdfminer.high_levelモジュールが提供する以下の関数を使うだけで実現できる。
- pdfminer.high_level.extract_text関数:PDFファイルから抽出したテキストを戻り値とする
- pdfminer.high_level.extract_text_to_fp関数:PDFファイルから抽出したテキストを何らかの出力(ファイルオブジェクトなど)に送り、出力先ディレクトリが指定されていれば画像ファイルをそこに保存する
PDFファイルからのテキスト抽出
まずはpdfminer.high_level.extract_text関数によるテキスト抽出の方法を紹介する。といってもコードはとても簡単だ。なお、サンプルのPDFは@ITが配布しているeBookの中から「セル結合を回避しながら表の見た目も確保するなど、「構造化Excelテク」12本まとめ」としている。ファイル名は「atmarkit_ebook116.pdf」だ。
from pdfminer.high_level import extract_text
from pathlib import Path
# PDFファイルからテキストを抽出
source = Path('atmarkit_ebook116.pdf')
text = extract_text(source)
print(text)
単にextract_text関数をインポートしたら、PDFファイルのファイル名を渡すだけだ。Visual Studio Codeで実行した結果を以下に示す。
この関数のパラメーターについても紹介しておこう。
- pdf_title:PDFファイルのファイル名、またはそれを参照するファイルオブジェクト
- password:暗号化されているPDFをオープンするのであれば、そのパスワード
- page_numbers:テキストを抽出するページを要素とするリスト(ページは0始まり)
- maxpages:パースするページの最大値
- caching:リソースをキャッシュするかどうかの指定
- codec:デコードに使うコーデックの指定
特定のページからだけテキストを抜き出したいのであれば、page_numbersパラメーターに必要なページを「page_numbers=[0, 1, 2]」などのように指定すればよい。他のパラメーターについても特に説明の必要はないだろう。この他にlaparamsパラメーターもあるが、これについては指定しなくても多くの場合は問題ないだろう。
pdfminer.high_level.extract_text_to_fp関数でもテキストを抽出できる。以下に例を示す。
from pdfminer.high_level import extract_text_to_fp
dest = Path('out.txt')
with open(source, 'rb') as fp_in, open(dest, 'wb') as fp_out:
extract_text_to_fp(fp_in, fp_out)
print(Path('out.txt').read_text(encoding='utf-8'))
extract_text_to_fp関数は、extract_text関数とは異なり、PDFファイルのファイル名は受け取らずファイルオブジェクトなどを受け取る仕様になっている。そのため、上のコードにあるようにopen関数でPDFファイルをオープンしてから、得たファイルオブジェクトを渡す必要がある。出力先も同様だ。ここではバイナリ書き込み用にファイルをオープンして、そこに抽出したテキストを書き込むように指定している。なお、最後のPath.read_textメソッドではencodingパラメーターを指定しているが、Windows環境では恐らくこのようにしないと例外が発生するだろう。
実行結果を以下に示す。
extract_text関数のときと実行結果が異なるように見えるが、これは元のレイアウトに近い出力となるように、extract_text関数ではかなり多くの改行文字「\n」をテキストに埋め込むためだと思われる。純粋にテキストを扱いたいのであれば、extract_text_to_fp関数の方が使い勝手は良いかもしれない。また、以下でも触れるが、extract_text_to_fp関数では改行が「\n」ではなく「\x0c」で表記されている点にも注意しよう。
なお、ファイルではなく、メモリにテキストを抽出したいのであれば、以下のようにStringIOクラスのインスタンスを出力先とする。
from io import StringIO
fp_out = StringIO()
with open(source, 'rb') as fp_in:
extract_text_to_fp(fp_in, fp_out)
text = fp_out.getvalue()
text = text.replace(chr(12), chr(10))
print(text)
StringIOクラスのインスタンスを生成したら、それを出力先として指定するだけだ。ただし、改行文字として「\n」ではなく「\x0c」が使われているので、下から2行目のように改行文字の置換をした方がよいかもしれない。以下に実行結果を示す。
画像の抽出
画像ファイルを抽出するには以下のように、extract_text_to_fp関数でoutput_dirパラメーターにディレクトリ名を指定する。これにより、そのディレクトリに画像ファイルが保存される。extract_text_to_fp関数にはextract_text関数と同じパラメーター群に加えて、他にも多くのパラメーターがあるが、それらについてはソースコードを参照のこと。
以下に例を示す。
output_dir = 'imgs'
fp_out = StringIO()
with open(source, 'rb') as fp_in:
text = extract_text_to_fp(fp_in, fp_out, output_dir=output_dir)
ここでは画像の抽出先として「imgs」ディレクトリを指定している。テキストの出力先は先ほどと同じくStringIOクラスのインスタンスだ(ここでは無視する)。これだけで画像の抽出が可能だ。以下に実行結果を示す。
Copyright© Digital Advantage Corp. All Rights Reserved.