Posted at

PDFファイル(3608ページ)から表形式のテキストデータをタブ区切りで抽出するシェルスクリプト


経緯



このようなTweetがTLに流れてきました。

元記事にも指摘があるとおり、PDFファイルが3000ページを超えていました。

さすがにユーザビリティに問題があると考えたのか、9月中に地図上に掲載予定とのことです。

ただ、PDFの状態にどうしても我慢できない人はおり、下記TweetのようにPythonスクリプトでテキストデータに加工した人もいました。



私も同じようにテキストデータに加工したいと考えた口です。

そこで、PDFからテキストデータをシェルスクリプトだけで抽出した手順を示します。


  • 検証環境 … Ubuntu 18.04 LTS


PDFファイルからテキストデータを抽出する

テキストデータの抽出自体はUnix系OSであれば下記CLIコマンドで実施可能です。

# infile.pdf … 加工対象のPDFファイル

# outfile.txt … 抽出したテキストデータの保存先
pdftotext -layout infile.pdf target.txt


読みやすく使いやすい形に加工する

抽出されたテキストデータを確認すると、以下のような形式です。

No.     都道府県    市区町村             事業所名(屋号)                業種              還元率

1 北海道 愛別町 セブン-イレブン愛別町店 小売業 食料品 2%
2 北海道 愛別町 伊藤新聞販売所 小売業 その他小売 5%
3 北海道 愛別町 三愛自動車工業株式会社 その他業種 ー 5%
4 北海道 赤平市 赤平 SS 小売業 ガソリンスタンド 2%
5 北海道 赤平市 赤平平岸SS 小売業 ガソリンスタンド 2%
6 北海道 赤平市 セブン-イレブン赤平文京町店 小売業 食料品 2%
7 北海道 赤平市 セブン-イレブン赤平茂尻店 小売業 食料品 2%
8 北海道 赤平市 出光茂尻SS 小売業 ガソリンスタンド 2%
9 北海道 赤平市 海鮮居食屋暖らん サービス 飲食業 5%
10 北海道 赤平市 株式会社菱友 赤平支店 小売業 その他小売 5%
11 北海道 赤平市 セルフ幌岡SS 小売業 ガソリンスタンド 2%
12 北海道 赤平市 デンキの桐原 小売業 電化製品 5%

これを以下のシェルスクリプト(totsv.sh)を作成してタブ区切りのデータに整形します。

#!/bin/sh

cat "$1" |
awk 'BEGIN{ # 必要な範囲のテキストデータのみを取得
target = 0 # 出力フラグ
}
{
if($0 ~ /固定店舗(EC・通信販売を除く).*令和元年8月21日 現在/){ # 固定店舗を対象とするため、フラグを有効化
target = 1
}
if($0 ~ /EC・通信販売(楽天市場)/){# 通販のリストは除外するため、フラグを無効化
target = 0
}
if(target == 1){ # 有効な区間の文字列を出力
print $0
}
}'
> tmp_result

cat tmp_result |
sed 's/.*キャッシュレス・消費者還元事業.*事務局審査を通過した加盟店一覧.*//' | # 不要な文字列を削除
sed 's/.*固定店舗(EC・通信販売を除く).*//' | # 不要な文字列を削除
sed 's/^ *[0-9]* *$//' | # ページ番号を削除
sed 's/.*※9月中に地図上に掲載予定.*//' | # 不要な文字列を削除
sed 's/.*都道府県.*//' | # 不要な文字列を削除
grep -v '^ *$' | # 不要な空行を削除
awk 'BEGIN{ # 項目ごとに整形
OFS="\t"
}
{
industry1 = $(NF - 2) # 業種1列目
industry2 = $(NF - 1) # 業種2列目
rate = $NF # 還元率
name = "" # 店舗名
for(i = 4; i <= NF - 3; i++){ # 店舗名を半角スペースで結合
if(i == 4){name = $i; continue}
name = name " " $i
}
print $1, $2, $3, name, industry1, industry2, rate # タブ区切りで出力
}'


シェルスクリプトの実行

以下のコマンドでresult.tsvというファイルに出力されます。

# target.txtを引数としてシェルスクリプトを実行し、`result.tsv`として保存

$ totsv.sh target.txt > result.tsv


シェルスクリプトの解説

シェルスクリプトは大きく2つのブロックに分かれます。

1つはテキストデータから必要なデータの範囲のみ抽出する上半分のブロックと、抽出したデータを整形する下半分のブロックです。


必要なデータの抽出

まず必要なデータ範囲ですが、今回は固定店舗のみを対象とします。

元のPDFファイルを見ると表のあるページ先頭には必ず見出しがあります。

この見出しをキーとしてそのページが必要かどうかを判定します。

この判定はawkコマンドのみで実施しました。

出力結果は確認のためにあえてtmp_resultファイルに出力しています。


整形処理前半:不要な文字列の削除

tmp_resultファイルを見て確認した状態をもとに整形処理を作成しました。

整形処理の前半ではsedgrepコマンドで不要な文字列を一掃します。


整形処理後半:タブ区切りに整える

Excel等の表計算ソフトや文字列処理でも扱いやすいことから、タブ区切りに整形しました。

これはawkコマンドのみで済ませました。


処理した結果

result.tsvファイルの中身はそのままスプレッドシートやExcelにコピー&ペーストやインポートが可能な形式になっています。

grepコマンドなどで住所を指定して検索すれば、特定の住所の事業者一覧を作ることができますし、これを元データとしてWeb APIや検索サービスとして提供することも難しくないでしょう。

# 検索の例

$ grep "東京都\s*目黒区" result.tsv

ちなみに上記検索条件では687件表示されました。

このPDFの住所は市区町村までしか記載がないため、これ以上詳しい住所では検索できず、地図へのプロットも大雑把すぎるため断念しました。

先ほど記したように公式では9月中に地図上に掲載予定とのことですが、今回私が作成したものより使いやすいとうれしいですね。