「PDFMiner」を使ってテキストを抽出

[pukiwiki]
”日本語”のPDFファイル300個ほどからデータを抜き出すことになりました。
で、プロテクトもかかってないし、手作業で行おうかと思ったのですが、以前から興味のあった、Pure Python なライブラリPDFMinerを使い、テキストデータを抽出してみました。
[/pukiwiki]


[pukiwiki]
なぜか、付属してきたサンプルプログラムはhtml等を出力するようになってたので、プレーンテキストを出力するサンプルスクリプトを書くことに。
*インストール
環境はVista ,Python2.5
都合により、公式サイトに書かれた手順とは少し異なります。

-こちらのサイトからダウンロード
–[[PDFMiner:http://www.unixuser.org/~euske/python/pdfminer/index.html]]
–今回使用したアーカイブ
pdfminer-dist-20090201.tar.gz
-同ページにてCMapファイルをダウンロード
CMap.tar.bz2
–解凍後、展開されたフォルダCMapを”pdflib”フォルダ内に移動
–同pdflibフォルダ内に空フォルダCDBCMapを作成
-実行スクリプトからimportできるところにpdflibフォルダを置く
–常用する場合はpdflibをPython2.5/Lib/site-package へ移動

*プレーンテキストを抽出してみる。
こちらのコードを、ファイル名 plaintext.py として保存してください
_python plaintext.py hoge.pdf

そのまま実行すると、hoge.pdfの文章のみを、hoge.pdf.txtにプレーンテキストとして保存します。(utf8)

[/pukiwiki]

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""plaintext.py"""

import pdflib
#from pdflib.pdfparser import PDFDocument, PDFParser, PDFPasswordIncorrect
from pdflib.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdflib.pdfdevice import PDFDevice, FigureItem, TextItem, PDFPageAggregator
from pdflib.pdffont import PDFUnicodeNotDefined
from pdflib.cmap import CMapDB
from pdflib.pdf2txt import TextConverter,enc

class PlainTextConverter(TextConverter):
  idx=[ [],[1,3],[0,2]]
  def __init__(self, rsrc,  codec='ascii',\
               merge=1, \
               #同一行とおぼしきものは連結
               # 0 None  1 Horizontal 2 Virtical
               joinner="\n"):
    TextConverter.__init__(self,rsrc,None,codec=codec)    
    self.text=""
    self.lines=[]
    self.joinner=joinner
    self.merge=merge
    return

  def end_page(self, page):
    TextConverter.end_page(self, page)
    page = self.cur_item
    def f(item,xy=[None,]*4):
      item2=item
      if isinstance(item, TextItem):
        if True in [ item.bbox[i]==xy[i]  for i in self.idx[self.merge]]:
        #バウンダリボックスから、同一行かどうかを判定
          txt=enc(item.text, self.codec)
          self.lines[-1]+=txt
        else:
          txt=enc(item.text, self.codec)
          
          self.lines.append(txt)
          xy[:]=item.bbox[:]
      if hasattr(item,"child") :
        
        f(item,child)
    self.lines=[]
    for child in page.objs:
      f(child)
    
    self.text+=self.joinner.join(self.lines)+self.joinner
    return

rsrc=PDFResourceManager()
import sys
stdout = sys.stdout
import os

#ここでこうする都合で、CMapフォルダをpdflib内に移動してます
dr=os.path.split(pdflib.__file__)[0]
cmapdir = os.path.abspath('%s/CMap'%dr)
cdbcmapdir = os.path.abspath('%s/CDBCMap'%dr)
CMapDB.initialize(cmapdir, cdbcmapdir)

device= PlainTextConverter(rsrc,codec="utf-8",merge=1,joinner="\n")

def convert(fname):
  device.text=""
  pdflib.pdf2txt.convert(rsrc,device,fname)
  return device.text
if __name__ == "__main__":
  import sys
  fname=sys.argv[1]
  #fname="test.pdf"
  convert( fname)

  fp=open(fname+".txt","wb")
  fp.write(device.text)
  fp.close()



[pukiwiki]
上記をモジュールとして使う場合のテストスクリプト

[/pukiwiki]

from  pdflib import plaintext

import glob

flist=glob.glob("*.pdf")

for f in flist[:3]:
  txt=plaintext.convert(f)
  open(f+".txt","wb").write(txt)


[pukiwiki]
*感想
今回、私が使うPDFはきれいにプレーンテキストに変換できました。ヤッター

官報のような入り組んだレイアウトだと、なぜか本文が消えちゃうことがあります。
CMap関連をちゃんとインスコしてないからかも。
(ホントは、makeして文字コード関連のデータベースを作成するように書いてあるのですが、手元のツールが足りなかったため、行ってません。)

個人的には、いいかげん官報はRSSリーダーで読めるようになって欲しいと思うのですけれども。それはさておき。

世の中には、pdfをプレーンテキストとして出力してくれるフリーソフトがいくつかありますので、それらをsubprocessモジュール(Popen関数)を使って実行してやるのもよいかも。

ですが、今回のサンプルコードで行ったように、(改行を入れる位置など)きめ細かい操作が行えるので、PDFMinerも場合によっては便利ですー

[/pukiwiki]

コメントを残す

メールアドレスが公開されることはありません。