LoginSignup
1
2

More than 3 years have passed since last update.

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

Posted at

経緯


このような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月中に地図上に掲載予定とのことですが、今回私が作成したものより使いやすいとうれしいですね。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2