たけのこブログ

凡人が頑張って背伸びするブログ

PDFMiner使ってPDFをテキストとして抽出

テキストマイニング初心者が調子に乗ってPDFをテキストに変換してみました

ただの備忘録です(思った以上に苦戦したので汗)。仕事などで本格的に自然言語処理をする機会がありそうなので、何となくテキストマイニングをやってみようと思ったのがきっかけです。スクレイピングは取り敢えずガチャガチャやればできそうという根拠のない自信があったので、まずは持ってきたPDFとかをテキストに変換することにしました。調べてみると、PDFMinerというモジュールがあって、こいつがあればなんでもできそうだなと思って初めて見たら...すごく苦戦しました。

参考記事は下記を参照して作ったのですが、もうPDFMinerのバージョンが古いらしく、この通りに動かすと色々エラーが出ます。試行錯誤してなんとか上手く動いたので、バージョンの違いなどによる変更点などは、文末にある参考URLやプログラムのコメント欄を参考にしていただければと思います。ただ、PDFParserの部分だけは以下のようなエラーが出てどうしようもなかったので、スクレイピングして持ってきたPDFを保存して、そのpdfを使ってPDFMinerで処理を行う想定で作りました。

eneprog.blogspot.com

エラー内容

---> 17 parser = PDFParser(fp)
     18 doc = PDFDocument()
     19 parser.set_document(doc)


UnsupportedOperation                      Traceback (most recent call last)
(以下略)
UnsupportedOperation: seek

まぁ...どうせBeautifulSoupとか使うつもりだったので、良いですよね?(震え声) では、以下構築手順になります。

  • まず、pdfminer.sixをインストールします。pdfminer.sixはpython2/3問わず動くらしいので便利みたいです。ちなみに、環境はanaconda3-5.3.0(python3.7)を使用しています。
pip install pdfminer.six
  • 次に、PDFをテキストに変換します。今回は、先ほどの記事を元にエネルギー計画の改定案のpdfを使用します。バージョンの違いなどはコメント文を参考にして頂ければと思います。
# 7月9日に投稿したプログラムは一部誤りがあったので修正
#from pdfminer.pdfparser import PDFParser, PDFDocument(古いっぽいです)
from pdfminer.pdfparser import PDFParser # (from pdfminer.pdfparser import PDFParserみたいです)
from pdfminer.pdfdocument import PDFDocument # (from pdfminer.pdfdocument import PDFDocumentみたいです)
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.pdfpage import PDFPage

import io

retstr = io.StringIO()

parser = PDFParser(open('energy.pdf','rb'))

try:
    doc = PDFDocument(parser)
except Exception as e:
    print('is not a readable pdf')
parser.set_document(doc)

rsrcmgr = PDFResourceManager()
device = TextConverter(rsrcmgr, retstr)
interpreter = PDFPageInterpreter(rsrcmgr, device)

# doc.get_pages()はバージョンが古くて、PDFPage.create_pages(doc)とするべきみたいです。
for page in PDFPage.create_pages(doc):
#for page in PDFPage.get_pages(doc):
    interpreter.process_page(page)

device.close()

str = retstr.getvalue()

retstr.close()

str

実行結果の一部

' エネルギー基本計画(案) 平成30年5月16日 \x0c 1 目次 はじめに・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・2 第1章 構造的課題と情勢変化、政策の時間軸 第1節 我が国が抱える構造的課題・・・・・・・・・・・・・・・・・・・・・・・・・・4 1.資源の海外依存による脆弱性 2.中長期的な需要構造の変化(人口減少等) 3.資源価格の不安定化(新興国の需要拡大等) 4.世界の温室効果ガス排出量の増大 第2節 エネルギーをめぐる情勢変化・・・・・・・・・・・・・・・・・・・・・・・・・7 1.脱炭素化に向けた技術間競争の始まり 2.技術の変化が増幅する地政学的リスク 3.国家間・企業間の競争の本格化 第3節 2030年エネルギーミックスの実現と2050年シナリオとの関係・・・・・・10 第2章 2030年に向けた基本的な方針と政策対応 第1節 基本的な方針・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・12

ちゃんと出てきました!

あとは、以下の記事を元に正規表現でテキストを整形すると綺麗に出てきました。

eneprog.blogspot.com

以下、プログラムと結果になります。

import re
str2=re.split("\x0c 2\s+",str) 
str2=str2[1]
str3=re.sub(r"\x0c(\s\d+|\d+)\s+","",str2)
str3
str4=re.split("\x0c",str3) 
str4=str4[0]
str5=re.sub("\s","\n",str4)
str6=re.sub(r"([a-zA-Z])\n",r"\1 ",str5)
print(str6)
str7=re.sub(r"([①-⑳])\n",r"\1",str6)
str8=re.sub(".\n",".",str7)
f = open('enegy.txt','w')
f.write(str8)
 
f.close()

整形した結果の一部

はじめに
2011年3月の東日本大震災及び東京電力福島第一原子力発電所事故を受けて、政府は、2014年4月、2030年を念頭に、第4次エネルギー基本計画を策定し、原発依存度の低減、化石資源依存度の低減、再生可能エネルギーの拡大を打ち出した。
第4次エネルギー基本計画の策定から4年、2030年の計画の見直しのみならず、2050年を見据えたパリ協定への対応、より長期には化石資源枯渇に備えた超長期の対応、変化するエネルギー情勢への対応など、今一度、我が国がそのエネルギー選択を構想すべき時期に来ている。このため、今回のエネルギー基本計画の見直しは、2030年の長期エネルギー需給見通し(2015年7月経済産業省決定。以下「エネルギーミックス」という。)の実現と2050年を見据えたシナリオの設計で構成することとした。

どうやら、綺麗に整形できていそうです。あとは、janome,MeCabなどで色々解析ができそうです。

まとめ

今回は、PDFMinerを使ってPDFをテキストにしました。これ以降は、どっかの企業や省庁の公開してるPDFファイルを持ってきて、PDFMinerやってからjanomeとgensim組みわせて解析したり...かな?(余裕があればそのうち)

バージョンの違いなどに関する参考資料(一部)

stackoverflow.com