2017年3月3日金曜日

Python3でPDFのテキストを抽出する

PDFからテキストを抽出するにはPDFを開いてコピペでもできるけれど、一度に大量のPDFを処理するとか、抽出したテキストでさらに何かの処理をしたいときなどは、やはりプログラムでやりたい。というわけで、Python3でPDFからテキストを抽出する方法を調べてみた。

見つけたのがPDFMinerというPDFの構造解析をするPythonライブラリ。これを使ってPDFからテキストを抽出できる。ただしPython2系用なので、Python3にはフォークバージョンのPDFMiner.sixを使う(Python3.4/3.5に対応)。PDFMiner.sixを使ってPDFからテキストを抽出するまでの手順をまとめた。

環境
Raspberry Pi 3 Model B
$ cat /etc/redhat-release
CentOS Linux release 7.3.1611 (Core)
$ python3 -V
Python 3.5.2


1.必要パッケージインストール


PDFMiner.sixを使うにはPythonパッケージのpycryptoとsixが必要。



2.PDFMinerインストール


ソースからインストールする方法もあるが、pipを使った方が簡単。



3.コードを書く


PDFMinerの解説はこちらにある。他にHow do I use pdfminer as a libraryも参考にした。

テキストを抽出するPDFは、青空文庫にある宮沢賢治の「雨ニモマケズ」青空キンドルでPDFにしたもの。


以下はPDF内の全テキストを出力するコード。

from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage
from io import StringIO

rsrcmgr = PDFResourceManager()
rettxt = StringIO()
laparams = LAParams()
# 縦書き文字を横並びで出力する
laparams.detect_vertical = True
device = TextConverter(rsrcmgr, rettxt, codec='utf-8', laparams=laparams)
# 処理するPDFを開く
fp = open('sample.pdf', 'rb')
interpreter = PDFPageInterpreter(rsrcmgr, device)

# maxpages:ページ指定(0は全ページ)
for page in PDFPage.get_pages(fp, pagenos=None, maxpages=0, password=None,caching=True, check_extractable=True):
    interpreter.process_page(page)

print(rettxt.getvalue())

fp.close()
device.close()
rettxt.close()

4.結果




なぜか2つめの「ヰ」だけ「蔭」が「?」になってしまっている。それ以外は問題なし。他のPDFで試したら文字化けはなし。とりあえずこの件は保留。

匿名さんからの情報で、表示段階で文字化けしているよう。結果をファイルに出力してWindowsのnotepadで開くと文字化けしない。
(2017.6.26追記)


5.暗号化PDF対応


PDFにパスワードが設定してあるとPDFが暗号される。PDFMinerでは暗号化PDFからテキスト抽出できない。

どうするかというと、PDF Miner PDFEncryptionErrorでqpdfというツールでPDFを復号化する方法が紹介されている。

まずはqpdfをインストールする。


上記sample1.pyのfp = open('sample.pdf', 'rb')を以下のように変更する。これで暗号化されたPDFからもテキスト抽出ができる。
※「from subprocess import call」などのようにsubprocessモジュールのcallメソッドのインポートもしておく(2018.1.4追記)。
pdf_file = 'sample.pdf'
decrypted_file = pdf_file + '.decrypted'
call('qpdf --password=%s --decrypt %s %s' %('', pdf_file, decrypted_file), shell=True)
fp = open(decrypted_file, 'rb')


PDFMinerを使うと、ページ単位、あるいはテキストブロックや画像、図表単位で抽出できるので、PDFのデータを抽出して何かやるには便利に使えそうなライブラリである。

5 件のコメント:

  1. [雨ニモマケズ]で表示が?になったのは
    䕃 (utf-8:0xE49583, unicode:0x4543)というCJK統合漢字拡張Aの文字ですが、Shift-JIS や EUC ではこの文字がマッピングされていません。

    おそらく、コンバータまではうまくいっていて、それを表示する段階で?になっています。

    返信削除
    返信
    1. 結果をファイルに出力して、Windowsのnotepadで開くと文字化けしないので、言われるように表示段階で文字化けしているようです。
      コメントありがとうございました。

      削除
  2. 暗号化PDFを試したところ、callが未定義とエラーが出ています。対処方法を教えていただければ助かります。

    返信削除
    返信
    1. subprocessをimortしておいてsubprocess.callでうまくいきました。自己解決です。

      削除
    2. 説明にimportが抜けていたので追記しておきました。ご連絡ありがとうございました。

      削除