プログラムでおかえしできるかな

定年を過ぎて何かの役に立てないかなと始めた元SEのブログです

PDFからテキストを抽出(コマンド)【Python】

このエントリーをはてなブックマークに追加

PDFからテキストを抽出する方法を紹介します。
Python環境で、コマンドだけで抽出します。
2段組み構成のPDFも抽出可能です。
ただし、苦手な文書はあります。その点はご容赦ください。

PythonPythonのパッケージ pdfminer.six を使用します。
PythonPythonパッケージのインストールなど基本的な使い方は諸先輩に譲ります。

目次

◆できること

PDFから抽出したテキストをテキストファイルに出力します。
段組みがあっても抽出できます。
図やグラフは抽出しません。

表については文字の抽出はできます。表としてのレイアウトは崩れます。

◆段組みされていない文書からの抽出

Pythonpdfminer.six パッケージでは、pdf2txt コマンドが提供されています。
これを使用して、プログラムを作成せずに pdfminer.six パッケージがインストールされた Python が動作する環境でPDFからテキストを抽出できます。

コマンドの例: pdf2txt.py example.pdf -o example.txt

この例は、example.pdfファイルからテキストを抽出してexample.txtファイルに出力します。
詳しい pdf2txt コマンドの使用方法はこちら(内部)⤵本家はこちら(外部)⤴ を参照してください。

段組みされていないほとんどの文書は、この方法でテキストを抽出できると思います。

◆段組みされている文書からの抽出

段組みされている文書の場合、オプションを指定して段落を認識させます。

コマンドの例: pdf2txt.py example.pdf -L 1 -o example.txt

L オプションを使用します。

L オプションは、行マージンで、どのくらい離れた行をひとつの段落にするかを指定します。

この例では、行間が少し空いているので「1」にしています。デフォルト(無指定)は「0.5」です。
※値の適正値は正直よくわかりません。あまり大きくすると字下げされた行を段落と判断しなくなるようです。
(pdfminer.sixの解析方法はこちらを参照⤵)

◆pdf2txtコマンドのオプション

pdf2txtコマンドのオプションは次の通りです。

オプションの説明…

  • -h
    • ヘルプメッセージを表示
  • -v
    • バージョン番号を表示
  • -p PAGENOS
    • 解析するページ番号のコンマ区切りのリスト
  • -m MAXPAGES
    • 解析するページの最大数(デフォルト:0)
  • -P PASSWORD
    • PDFファイルの復号化に使用するパスワード(デフォルト:"")
  • -R ROTATION
    • 他のタイプの処理の前にPDFを回転させる角度(デフォルト:0)
  • -n
    • レイアウト分析パラメータを無視する必要がある場合(デフォルト:False)
  • -V
    • レイアウト分析中に垂直テキストを考慮する必要がある場合(デフォルト:False)
  • -M CHAR_MARGIN
    • 2つの文字を同じ行と判断するための文字間隔(デフォルト:2.0)
      マージンは、文字の幅を基準にして指定
  • -W WORD_MARGIN
    • 同じ行の2つの文字を離れている(単語の境界)と判断するための文字間隔
      読みやすくするために中間スペースが追加される
      マージンは、文字の幅を基準にして指定(デフォルト:0.1)
  • -L LINE_MARGIN
    • 2つの行を同じ段落と判断するための行間隔
      マージンは、行の高さを基準にして指定(デフォルト:0.5)
  • -F BOXES_FLOW
    • 行の順序の決定方法(テキストの水平方向と垂直方向の位置がどの程度重要かを指定)
      値は、-1.0(水平位置のみが重要)から+1.0(垂直位置のみが重要)
      disabled:レイアウト分析を無効にし、テキストボックスの左隅の位置に基づいてテキストを返す(デフォルト:0.5)
  • -A
    • 図のテキストに対してレイアウト分析を実行する必要がある場合(デフォルト:False)
  • -o OUTFILE
    • 出力されるファイルへのパス
      または、「-」(デフォルト):stdoutに出力
  • -t OUTPUT_TYPE
    • 出力のタイプ{text、html、xml、tag}(デフォルト:「text」)
  • -c CODEC
  • -O OUTPUT_DIR
    • 抽出された画像を配置する出力ディレクト
      指定されていない場合、画像は抽出されない
  • -Y LAYOUTMODE
    • htmlのレイアウトのタイプ {normal、exact、loose}
      normal:各行はhtml内で別々に配置(デフォルト)
      exact:各文字はhtml内で別々に配置
      loose:normalと同じ結果、ただし、各テキスト行の後に改行を追加
      output_typeがhtmlの場合にのみ使用される
  • -s SCALE
    • HTMLファイルを生成するときに使用するズームの量
      output_typeがhtmlの場合にのみ使用される(デフォルト:1.0)
  • -S
    • テキストから制御ステートメントを削除
      output_typeがxmlの場合にのみ使用される(デフォルト:False)

外部Doc⤴

◆結果

pdf2txt コマンドを使用して段組みされている文書からでもテキストを抽出する方法を紹介しました。

ただし、文書によってはうまく抽出できない場合があるようです。

具体的には、「段組み」の構成通りに『段落』が抽出されないと、期待した結果にならないようです。
ここで言っている『段落』とは本来の意味の段落ではなく、文章の塊と思ってください。

言い換えると、文書が2段組みの場合、『段落』として1ページの中に左と右の二つが抽出できないと、『段落』と「段組み」が一致せず、期待した結果になりません。

なぜなら、「段組み」の中が複数の『段落』に分かれるような文章の場合、左右の「段組み」に複数存在する『段落』が高い位置の順に交互に出力されてしまうためです。

これは、抽出される『段落』の出力順を制御できれば解決するのですが、pdf2txt コマンドにそのようなオプションは用意されていないようです。

この辺りの制御を取り入れたアプリを別記事「PDFからテキストを抽出(プログラム)【Python】」で紹介しています。良かったら見てください。

pdf2txt コマンドが苦手な文書

  • 段組みの構成が途中で変わるもの
  • 文中に図や表などのイメージがあるもの
  • ヘッダーやフッターがあるもの

次に、あるサンプル文書での抽出結果を示します。

サンプルにした文書(抜粋)

文書 文書 一般社団法人 情報科学技術協会

サンプルにした文書での結果

うまく抽出できる部分(抜粋)

境としては以下 2 つが有名である。
・Google Colaboratory3)
・Microsoft Azure Notebooks4)
3.具体的な処理
3.1 導入
2019 年にノーベル化学賞を受賞した吉野彰氏が発明者
となっている(日本)特許出願群を題材に,出願データの
前処理と集計,可視化を行う。
具体的には,出願件数,出願件数推移,特許分類ランキ
ング,共同発明者集計,クロス集計,請求項中の重要語の
集計を行い,結果を可視化する。
以下,Google colaboratory 上で pandas を使って特許
データの処理を行う。上記の Python のインストール済
PC や,ブラウザ上の Python 実行環境であれば,同じコー
ドで動く。
下記の URL を開き,ソースコードをコピペするか,手
打ちで入力して実行しながら処理を体験してもらうと,理
解が進むのでお勧めする。

うまく抽出できていない部分(抜粋)

これを実行すると,図 8 のような結果が現れる。

図 9 出願件数推移の可視化実行結果

この plot() は色々変形可能であるので,棒グラフを出し
たり円グラフを出したりしたい場合でも 1~2 行コードを
追加するだけでよい。公式サイトの情報等も参照された
い 8)9)。

3.6 出力
上記の各セルでの実行結果は,csv や xlsx 形式で保存
可能である。
これも plot() と同じく,上記での実行結果の最後に,
to_csv() や to_excel() を追加するだけである。例えば,3.4.2
において,出願件数の時系列推移の集計結果をエクセルで
出力したい場合,下記のように,3.4.2 のコードの最後の
行の下に to_excel(出力ファイル名)を入れるだけでよ
い。図 10 のように「3.4.2 結果.xlsx」というファイルが
Colaboratory 上のフォルダに出力される。右クリック⇒
ダウンロードでローカル PC にファイルをダウンロードで
きる。
##############################################
df_yoshino["出願年"]= df_yoshino["出願日"]¥
 .str[0:4]
df_yoshino.groupby("出願年")¥
 .size()¥
 .to_excel("3.4.2 結果.xlsx")
##############################################

―  201  ―

情報の科学と技術 70 巻 4 号(2020)

図 8 テキスト分析の実行結果

3.5 可視化
pandas にはデータ可視化の機能が組み込まれているの
で上記のコードに 1~2 行追加するだけで,いい感じにグ
ラフを出してくれる。

うまく抽出できている部分では、前節の終わりに3章が続き、何も割り込んでいません。

うまく抽出できない部分は、

  • 図8のような結果が現れる (左の段の3.4節の最後)
  • 図9出願件数推移のかしか実行結果・・・(右の段の図9と説明)
  • 3.6 出力(右の段の3.6節)
  • 3.5 可視化(左の段の3.5節)

というように、右の段の内容と左の段の内容が少しずつ混ざるように出力されてしまいます。

◆pdfminer.sixの解析方法

pdfminer.six は次のような方法で文書を解析しています。外部Doc⤴
内容は、マニュアルを自分なりに和訳したものになっています。

段落の作成は「2.行をボックスにグループ化」で行っています。

  1. 文字を単語と行にグループ化
    1. 初めに、文字をグループ化
    2. 水平方向と垂直方向の両方で近い文字は、1行にグループ化
    3. PDF形式にはスペース文字の概念がないため、文字の間にスペースを挿入
    4. 各行は文字のリストで構成
    5. これらの文字は、PDF存在するLTChar文字、またはスペースを表す挿入されたLTAnno文字
  2. 行をボックスにグループ化

    1. 次に、行をグループ化
    2. 水平方向に重なり、垂直方向に近い行をグループ化
    3. 行が垂直方向に近いかは、line_marginによって判断
    4. line_marginは、境界ボックスの高さを基準に指定
    5. 行の境界ボックスの上部と下部の間のギャップが、line_marginに境界ボックスの高さを乗したものより小さい場合、行は近いと判断
    6. このステージの結果は、テキスト・ボックスのリスト
    7. 各ボックスは行のリストで構成
  3. テキストボックスを階層的にグループ化

    1. 最後に、テキストボックスをグループ化
    2. 最も近い2つのテキストボックスを繰り返し結合

◆必要なパッケージ

  • pdfminer.six
    • インストール:pip install pdfminer.six 追加:2022-08-02

◆さいごに

pdfminer.six は、pdf2txt コマンドだけでもかなりの結果が得られます。
文章だらけの文書であれば、十分に利用に耐えると思います。
それでも苦手な文書はあるので、それらについて、Pythonで取り組んでみたいと思います。
段組みの対応については、別記事「PDFからテキストを抽出(プログラム)【Python】」で紹介しています。良かったら見てください。

◇ご注意

本記事は次のバージョンの下で動作した内容を基に記述しています。

  • Python 3.8.5
  • pdfminer.six 20201018

ご利用に際しては、『免責事項』をご確認ください。
お気づきの点がございましたら『お問い合わせ』からお問い合わせください。
追加:2022-08-02

◆参考

投稿: 、更新: