Rubyで請求書pdfファイルから入金額を抽出する

前回、金額情報を抽出する正規表現を書いた。今回は請求書のPDFファイルから入金額を抽出してみます。

参考

RubyでPDFをパースしたい - Qiita

事前準備

pdf-readerというgemを使います。

GitHub - yob/pdf-reader: The PDF::Reader library implements a PDF parser conforming as much as possible to the PDF specification from Adobe.

$ gem install pdf-reader

とりあえず抜き出してみる

Googleで検索して使えそうなpdfファイルを落としてきます。このとき、pdfファイルに文字情報が入っている必要があります。画像ファイルなどをpdfに変換している場合は文字情報が抽出出来ないのでこのやりかたは出来ない。

https://www.tsr-net.co.jp/userweb/seikyu_sample.pdf

請求書1

このようにテキスト情報が抜き出せます。

$ irb

require 'pdf-reader'
reader = PDF::Reader.new("seikyu_sample.pdf")
str = reader.pages.first.text

=> "                                         請 求 書                        請求日:2018/03/07\n                                            1 / 1                     請求書番号:000000000000000\n\n\n         〒103-0027\n         東京都中央区日本橋1-99-99\n\n                                                               ˟100-6810ɹઍ\u0B45ా۠େखொ1-3-1ɹ+\"Ϗϧ\n         サンプル企業株式会社                                            5&-▯▯03-6910-3132ɹ'\"9▯▯03-5221-0705\n         経理部 御中\n\n                                                               注)ご契約内容についてのお問い合わせは\n         ▯▯▯▯▯▯▯▯▯▯\n\n▯\n\n▯▯▯▯▯▯▯▯▯                                契約担当支社店へお願いいたします。\n\n         ▯(▯▯▯▯▯▯▯▯(\n                           222-00001#\n                                                               2018/7より請求書の\n                                                               フォーマットを変更いたしました。\n\n\n\n\n                                              <振込先金融機関>\n  下記の通りご請求申し上げます。                                    金融機関            支店        種別    口座\n                                              三井住友銀行(0009)      新橋支店(216)      普通   0666559\n      顧客コード             請求金額                  みずほ銀行(0001)       東京営業部(001)     普通   1947034\n                                              三菱UFJ銀行(0005)     本店(001)        普通   4650232\n    C0000000000000            3,240円\n                                              りそな銀行(0010)       東京営業部(300)     普通   0025475\n                                              口座名:カ)トウキヨウシヨウコウリサーチ\n  ・支払期限:請求日から60日以内にお支払いください。\n  (別途契約等がある場合はその期日)                           継続商品(TSR情報誌等の情報関連、T-WATCH)につきましては、\n  ・お振込みの折は、右記金融機関をご利用ください。                    更新日前に請求書を送らせていただいております。\n  ・振込手数料はお客様負担でお願いいたします。                      引続き、ご継続いただき本請求額のお振込みをお願いいたします。\n\n   <商品別内訳>\n\n                   品名                      数量         金額                  備考\n   tsr-van2基本料金                                 1          400 AA001001\n   tsr-van2情報料金                                 4        2,600\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n   仮受消費税                                                   240\n\n\n   <契約先内訳明細>                                                             <弊社担当支店 本社・東京>\n   <C0000000000000> サンプル企業株式会社 経理部                                               03-6910-3200\n   納品日 伝票番号            品名                期間      ポイントNo 数量    金額              備考\n\n             tsr-van2基本料金            18/02/01-18/02/28    1       400 AA001001\n             tsr-van2情報料金            18/02/01-18/02/28    4      2,600\n             仮受消費税                                                240\n\n             本体価格小計                                              3,000\n             小計                                                  3,240"

このテキストから正規表現で金額情報を抽出してみる。んー思ってたんとちゃう。

irb(main):032:0> str.scan(/\d{1,3}(?:,\d{3})+|\d+/)
=> ["2018", "03", "07", "1", "1", "000000000000000", "103", "0027", "100", "6810", "1", "3", "1", "5", "03", "6910", "3132", "9", "03", "5221", "0705", "222", "00001", "2018", "7", "0009", "216", "0666559", "0001", "001", "1947034", "0005", "001", "4650232", "0000000000000", "3,240", "0010", "300", "0025475", "60", "2", "1", "400", "001001", "2", "4", "2,600", "240", "0000000000000", "03", "6910", "3200", "2", "18", "02", "01", "18", "02", "28", "1", "400", "001001", "2", "18", "02", "01", "18", "02", "28", "4", "2,600", "240", "3,000", "3,240"]

今回の請求書だと、請求番号や日付などに含まれる数値情報も抽出されてしまった。なので、末尾に「円」がついている箇所だけ抽出してみる。

irb(main):039:0> str.scan(/(?:\d{1,3}(?:,\d{3})+|\d)+円/)
=> ["3,240円"]

うむ。出来た。

¥マークの場合

次に末尾に「円」ではなく先頭に「¥」がつく請求書で試してみる。ちょうどいい請求書が落ちてなかったのでMISOCAで適当に作った。

請求書2

$ irb

reader = PDF::Reader.new("hoge.pdf")
str = reader.pages.first.text
=> "                                                         2019年06月08日\n\n                                                    請求番号: 20190608‑021\n\n\n\n                             請求書\n\n\n                                    もげもげ株式会社\nほげほげ株式会社 様\n\n                                    〒150-0031\n件名 : ご請求の件                          東京都渋谷区桜丘14‑10\n                                    渋谷コープ403\n下記のとおりご請求申し上げます。\n\n\nご請求金額                ¥ 106,790 -\n\n\n\n                 品番・品名                     数量      単価       金額\n\n 作業量                                         12 h   8,240     98,880\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n                                         小計                   98,880\n\n\n                                         消費税 (8%)             7,910\n\n                                         合計                  106,790"

当然、単位「円」はついていないのでこれまでの正規表現では抽出出来ない。

str.scan(/(?:\d{1,3}(?:,\d{3})+|\d)+円/)
=> []

先頭に「¥」を入れてみてもうまくいかない。あれれ。

 str.scan(/¥(?:\d{1,3}(?:,\d{3})+|\d)+/)
=> []

よく見たらスペースが入ってるぽいので、スペースを考慮した正規表現にしてみるが、やっぱりダメ。

 str.scan(/¥\s(?:\d{1,3}(?:,\d{3})+|\d)+/)
=> []

どうも腑に落ちないので、上で出力された¥マークと金額との間のスペースの文字コードを調べてみると、160、つまりノーブレークスペースであることが分かった(htmlでいう&nbsp)。

irb(main):076:0> ' '
=> " "
irb(main):077:0> ' '.ord
=> 160

ノーブレークスペースも拾える正規表現にしてみる。

参考

文字列中の &nbsp; (C2A0) を正規表現で削除 - Qiita

str.scan(/¥[[:space:]](?:\d{1,3}(?:,\d{3})+|\d)+/)
=> ["¥ 106,790"]

できたー!



mofmof inc. 採用情報

mofmof inc.では、一緒に仕事をしてくださるエンジニアを募集しております。
募集要項については以下をご確認ください。
興味を持っていただけましたら、ぜひ一度ゆっくりお話出来れば嬉しいです。

mofmofは何をしたいのか
https://www.mof-mof.co.jp/recruit/want-to-do