国が公開している資料が割とPDFで公開されていることが多く、このようなデータをアプリケーション内で使いたいと思ったときにPDFを頑張ってゴニョゴニョする必要があると思うのですが。。。
そういうときにpdf-reader
gemがシンプルにつかえて便利だったので使い方とかをMEMOしておきます。
pdf-reader
の使い方
pdf-reader
の使い方は簡単で、以下のようにgemをinstallするか、
gem install pdf-reader
以下をGemfileに追記してbundle install
します。
gem 'pdf-reader'
あとは以下のような形で本文にアクセス出来ます。
reader = PDF::Reader.new("somefile.pdf") reader.pages.each do |page| puts page.text # 本文 end
readerをカスタマイズする
自分は以下のような形でカスタマイズして使ってみました。
単純にpdfをパースすると、空白文字や空行が大量にあったり、メタ情報が上部に毎回表示されたりするので、CustomReader::Page#formatted_records
で、それらを除去して扱いやすいようにしています。
require 'pdf-reader' class BaseReader def initialize(file_path:) @client = PDF::Reader.new(file_path) end attr_reader :client def pages client.pages.map { |page| Page.new(page) } end class Page def initialize(page) @page = page end attr_reader :page def text page.text end end end class CustomReader < BaseReader def pages client.pages.map { |page| Page.new(page) } end class Page < BaseReader::Page START_RECORD_INDEX = 18 END_RECORD_INDEX = -2 CLUMN_DELIMITER = "\t" def formatted_records # NOTE: 以下を行い整形 # * 2文字以上の空白文字を列の区切りとみなし区切り文字に変換 # * 空白な行を削除 # * 各ページのheader部分の行を削除 text.gsub(/\x20{2,}/, CLUMN_DELIMITER).split(/\R/).reject(&:empty?)[START_RECORD_INDEX..END_RECORD_INDEX] end end end
またBaseReader
でclinetをインスタンス変数で持つようにしたので、client.pages
にアクセスできる、かつ、それが本文を返すtext
メソッドを持つオブジェクトの配列であれば任意のクライアントに変更できるので、別のgemを使いたくなったときにも変更しやすいのかなと・・・!
おわりに
pdfのパースやる前はかなり大変なのかなと思っていたのですが、pdf-reader
シンプルに本文を取り出せるので便利ですね✨
しかし、pdf内の表のセルの中で改行が入っていたりすると、同一セル内でも別の行と判断されフォーマットが激しく崩れるのでpdfのデータをいい感じにパースするのは厳しい。。。😢
CSV等の扱いやすいデータ形式で提供されておらず、傾向だけとか、一部データだけでも取り出したいようなケースではpdfをパースしてゴニョゴニョするのは良さそうですね👍