
EXCELファイルのデータベース化についてブログ記事を書いていますが、お客様向けのデータを探している中で、PDFファイルを処理する必要が出てきました。
一般社団法人 日本エクステリア工業会 から提供されているこちらのデータになります。
PDFを開いてみると、このような見た目の内容です:

複数の表が連なっているPDFファイルですが、表部分は比較的単純な形をしていますし、規則性もあります。データベースへ保存するためのデータ抽出が可能か、試してみます。
PDFよりテキストを抽出する
Python では、PyPDF2 というライブラリを使って、PDFファイルを開いて、中身を抽出することが出来ます。
import PyPDF2
# PDFファイルを読み込む
pdfFile = open('./日本エクステリア工業会/data_220727-3.pdf', 'rb')
reader = PyPDF2.PdfFileReader(pdfFile)
pageObj = reader.getPage(0)
# 文字情報を抽出する
pdfText = pageObj.extractText()
# ファイルを閉じる
pdfFileObj.close()
これで取出した内容をみてみます:

以下の4つの特徴があり、データ処理しやすそうです:
- カテゴリ名は【】で囲まれている。データはその下3行で表現される
- カテゴリ名のすぐ下は、表部分のヘッダになっている
- データ行2行あり、それぞれ行頭に「本年度」「前年度」という言葉が入っている
- 各項目は、スペース(’ ‘)で区切られている
つまり、「【」で始まる行の、次の行から3行が、表形式データになり、全て半角スペース(’ ‘)で区切られています。
1行ずつ情報を抽出して、以下のルールで表形式のデータを作成出来ます:
① 「【」で始まる行は、【〇〇〇〇】の〇〇〇〇の部分がデータの種類を表すので、抜き出して保持しておく
②表のヘッダ部分は、ヘッダ情報として保持する
③表のデータ部分は、データ情報として保持する。但し、最初の「本年度」の文字は不要なので、切り取る。また、前年度の行は不要なので無視する。
④ヘッダ情報、データ情報ともに、半角スペース(’ ‘)で分割し、配列にする
⑤カラム別に分割されたヘッダとデータを1つにまとめる
⓺まとめた表データをデータベースへ保存する
PDFの文字列を整形して表形式のデータにする
コードの一部を抜粋します。データはスペース区切りなので、 split して配列にします。不要な要素の削除は、 pop で行っています。
ヘッダ行とデータ行は、どちらも半角スペースで区切られていて、余分なスペースなどは無いようなので、ここでは単純にループしながら配列データを作成します。
# headers: 表の列名が入る配列
# 表のヘッダ行を読み取る処理。スペース区切りなので、スペースで区切る。
(...)
# タイトル行の次の行=ヘッダカラム
headers = line.split(' ')
continue
(...)
# ヘッダ行を読み込んだ次の行を読み込む処理
# ヘッダ行の次の行は、「本年度」のデータが並ぶ行。
# 同じくスペース区切りなので配列に変換し、
# 最初の列は「本年度」という文字列になるので、削除する。
# 残りは、数値になる想定。
if len(headers) > 0:
# データ行
data = line.split(' ')
# 最初の1列目は不要なので削除
data.pop(0)
# ヘッダの数だけデータがある想定。
for index, head in enumerate(headers):
if index < len(data):
temp_dict[head] = [ data[index] ]
(... 以下略 ...)
登録結果
コードは随分と長くなってしまいましたので、これ以上、コードの説明は行いませんが、表形式が11種類あるので、それぞれをディクショナリに格納して、データフレームへ変換し、最後に to_sql でデータベースへ格納します。
結果は、それぞれ別のテーブルとして、MariaDBへ登録されました:

中身も見てみます:

PDFの表部分の、列名がそのままテーブルの列名になり、データが保存されています。
※前年比列は「%」が入っているので、数値に変換できずに消えてしまいました。前年比は計算すれば出ますし、チャートにすれば前年との比較も視覚化されるので、ここではこのまま放っておきます。
Metabase で可視化
データベースまで登録出来れば、あとはBIツールを使って表示出来ます:

苦労したところ
実はPDFが15個あったのですが、途中でカラム名が間違っていたり変わったりしているところがあって、データベースへ登録する際に、何度もエラーが出ました。
「列名が存在しない」という主旨のエラーでした。
DataFrame の to_sql を使って、直接データベースへ入れているため、途中で書式が変わって、列名が変更になると、データベースに登録する際に、このエラーが発生します。
このように、データの構造が違うと、データベースへ登録する際に苦労します。
ドキュメントDBであれば、構造は柔軟に検討出来ますが、リレーショナルDBではそうはいきません。
統計の値は、年によって統計の取り方が変わることもしばしばありますので、場合に応じて対応が必要になります。
(その場で対応が検討出来ないようなときには、一度ドキュメントDBへ格納しておいて、後から構造化の計画を立て直すということも、必要になります。)
まとめ
PDFから表データ部分を抜き出して、データベースに登録することが出来ました。
今回のケースは、元のPDFが単純な書式でしたので、比較的簡単にデータベースへ登録することが出来ましたが、
非構造化データから構造化データを抽出するには、何かしら工夫が必要になることが多いです。
PDFのデータしかないというケースは、あまり多くはないかもしれませんが、データ・プラットフォームを構築する上では、様々なケースが考えられます。
非構造化データを構造化データに変換することが必要なケースがあれば、ご相談頂ければと思います。
Photo by Brett Jordan on Unsplash