SE_BOKUのまとめノート的ブログ

SE_BOKUが知ってること・勉強したこと・考えたことetc

PDFからテキストデータをうまく抜けるか実験(pdfminer.six)/Pythonサンプル

f:id:arakan_no_boku:20180824234522j:plain

目次

PDF形式のデータから、テキストを抜き出す

PDF形式のデータから、テキストを抜き出して、何か処理する時のインプットデータにできないかと試してみた結果のご報告です。

一口にPDFと言っても、様々なバージョンがあります。

暗号化もできます。 

ja.wikipedia.org

そいういうバリエーションを吸収しないといけないなどと考えると、プログラミングでデータで使うにはあまりに面倒な形式で、ライブラリなしでやる気にはなりません。

Pythonで使えるPDFライブラリ比較

Pythonで使える、PDFを扱えるライブラリをあたります。 

pipでPDFをキーワードで検索すると、htmlをPDFに変換するとか、Markdown形式ファイルをPDF化するなど、特定の用途に適したものがいっぱいあります。 

テキストを抽出する用途で探すと、「pdfminer」が、かんたんにつかえて、テキスト抽出の精度も高いと、口コミ評価が高かったので、これで試してみます。  

pdfminerと一口に言っても、種類があります。

pip search pdfminer で探すと、3つのバージョンがでてきます。

  • pdfminer   / python2.xx系
  • pdfminer3k  / python3.xx系
  • pdfminer.six  / python2/3系共通

現在でも更新されているのは「pdfminer.six」だけみたいです。

github.com

python2と3でコンパチブルなのも、pdfminer.sixだけですし、これを使わない理由が見当たりません。 

pdfminer.six

pdfminer.sixをインストールします。

pip install pdfminer.six

f:id:arakan_no_boku:20210502002926p:plain

pdfminer.six付属のツールpdf2txt.py

pdfminer.sixには、pdf2txt.py というツールが付属してます。 

インストールが正常に終了していれば、anacondaをインストールしたフォルダ(activateして仮想環境にしている場合はenvsの仮想環境名フォルダの下)のScriptsフォルダにインストールされてます。 

Linux環境なら、パスが通っているのでそのまま実行できます。

でも、windowsでは無理なので、カレントフォルダにコピーして使います。

サンプルにするPDFは、こういうものにします。

f:id:arakan_no_boku:20200601202836p:plain

これを「sample.pdf」として保存して、作業フォルダに置き、同じところに「pdf2txt.py」をコピーして、以下のようにします。

python pdf2txt.py sample.pdf

  抽出されたテキストはこんな感じでした。

キーワー ド:ビ ッグファイブパーソナリテ ィ 質問紙法 尺度構成 , 信頼性, 妥当性

問題

Openness to Experience (Openness; 開放性)で

ある 。

近年のパーソナリティ特性論において, もっと

日本における語裳研究は背木 ( 1971 ) の先駆的

も確固たる 知 見を積み重ねているのは. Big Five

な研究に始まり,その後も継続的に行われている

( ビッグファイブ; Goldberg, 1990, 1992 ) や Five

(柏木・辻・藤島・山田 2005 ;柏木 ・和田・ 青

Factor Model ( 5 因 子モデル; ~、1cCrae & Costa,

木 , 1993; 村 上, 2003; 和田, 1996 ) 。 また , 日

1987 ) である 。

 化けたりはしてませんが、改行や空白がランダムにはいっています。

付属ツールといえど、2段組みだとこうなってしまうようです。

pdfminerを使ったPythonプログラム 

今度は、pdfminerを使ってテキストを抽出するpythonプログラムを書きます。

(Python3.7、3.8、3.9で動作確認しています。)

gettext() という名前のメソッドにしました。 

# -*- coding: utf-8 -*-

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
import re 

def gettext(pdfname):
    # PDFファイル名が未指定の場合は、空文字列を返して終了
    if (pdfname == ''):
        return ''
    else:
        # 処理するPDFファイルを開く/開けなければ
        try:
            fp = open(pdfname, 'rb')
        except:
            return ''
        
    # リソースマネージャインスタンス
    rsrcmgr = PDFResourceManager()
    # 出力先インスタンス
    outfp = StringIO()
    # パラメータインスタンス
    laparams = LAParams()
    # 縦書き文字を横並びで出力する
    laparams.detect_vertical = True
    # デバイスの初期化
    device = TextConverter(rsrcmgr, outfp, codec='utf-8', laparams=laparams)
    # テキスト抽出インタプリタインスタンス
    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)
    #取得したテキストをすべて読みだす
    ret = outfp.getvalue()
    # 後始末をしておく    
    fp.close()
    device.close()
    outfp.close()
    # 空白と改行をとりさり一塊のテキストとして返す
    return re.sub(r"\s| ",'',ret)

 テキスト出力を、上記のメソッドを使ってやる場合はこうします。

import pdf2text as pt

ret = pt.gettext('C:\\myWork\\00_python\\workspace\\src\\proc\\sample.pdf')
print(ret)

シンプルなレイアウトのPDFで試す

最初にシンプルなPDFを処理してみます。

f:id:arakan_no_boku:20200601205719p:plain

これから抜き取ったテキストは以下のとおりです。

マンション標準管理規約(団地型)○○団地管理規約第1章総則(目的)第1条この規約は、○○団地の管理又は使用に関する事項等について定めることにより、団地建物所有者の共同の利益を増進し、良好な住環境を確保することを目的とする。(定義)第2条この規約において、次に掲げる用語の意義は、それぞれ当該各号に定めるところによる。一区分所有権建物の区分所有等に関する法律(昭和37年法律第69号。以下「区分所有法」という。)第2条第1項の区分所有権をいう。二区分所有者区分所有法第2条第2項の区分所有者をいう。三団地建物所有者区分所有法第65条の団地建物所有者をいう。四占有者区分所有法第6条第3項の占有者をいう。五専有部分区分所有法第2条第3項の専有部分をいう。

シンプルなPDFなら、きれいにテキストが抜き取れているようです。

 

2段組みの複雑なPDFで試す 

今度は、PDFを2段組のちょっと複雑なものに適用してみます。

付属ツールの出力に使ったものと同じものをやってみます。

f:id:arakan_no_boku:20200601202836p:plain出力結果です。 

キーワード:ビッグファイブパーソナリティ質問紙法尺度構成,信頼性,妥当性問題OpennesstoExperience(Openness;開放性)である。近年のパーソナリティ特性論において,もっと日本における語裳研究は背木(1971)の先駆的も確固たる知見を積み重ねているのは.BigFiveな研究に始まり,その後も継続的に行われている(ビッグファイブ;Goldberg,1990,1992)やFive(柏木・辻・藤島・山田2005;柏木・和田・青Model(5因子モデル;~、1cCrae&Costa,rotcaF木,1993;村上,2003;和田,1996)。また,日1987)である。

 二段組の処理ができてなくて、左と右の文章が混じってしまってます。 

やはり、段組みとかの複雑な構造を認識して、文章としてとりだすのは難しいです。

 

結論:プログラムのインプットにPDFは不適

個人的な結論を言えば。

プログラム他で 加工や解析をするインプットデータとして受け取る形式としては、PDFファイルは適切ではない。

です。 

PDFからのテキストの抜き出しは、「やってみないと、上手く行くかどうかわからない」うえに、「うまくいくもの」「うまくいかないもの」の事前の見極めが単純にいかず、結果見てびっくりになりやすいからです。

理由1:うまくいくPDFとうまくいかないPDFがある

上記のpythonプログラムで色々なPDFファイルから、テキストを抽出してみました。 

うまく行ったものは、もちろんありました。

しかし、自分の手持ちのPDFだと、うまくいかないものも多かったです。

うまくいかなかったパターンは。 

  • テキストに見えていても画像がはりついていて、抽出できない。 
  • ファイルが暗号化されていて読めない。 
  • 読めて、テキストが抽出できても、2バイト文字が化ける。 

などなど。

 

理由2:特にうざい2バイト文字が化ける問題

特に最後の文字化けはかなりうざいです。 

PDFで見ていると同じように見えるのに、テキストを抽出してみると、例えば、以下のように表示されたりします。

以下は青空文庫芥川龍之介のクモの糸の最初のほうですが、こんな風になってしまいました。

蜘蛛(cid:12506)糸芥川龍之介(cid:13268)(cid:10187)(cid:13269)(cid:13268)(cid:10186)(cid:13269)一(cid:12680)(cid:12695)(cid:12765)(cid:12681)(cid:12693)(cid:12734)

(cid:12506)というのはアドビ社のCIDフォントに割り振られている番号です。

まあ、それがそのまま出力されている=一致する2バイト文字を見つけられなかった・・ということなのでしょうが、事前に予想ができないので、後でプログラムで処理してたら異常終了して・・とか、ダメージうけそうです。 

 

ではでは。

#python