とりあえずワンライナー
pdftotext -raw -f 3 -l 2795 kameiten_touroku_list.pdf - | grep -E '^[0-9]+(,[0-9]+)* |[25]%$' | perl -pe 's/(?<![25]%)\n/ /g' | perl -pe 's/^(\S+) (\S+) (\S+) (.+) (\S+) (\S+) (\S+)$/"$1","$2","$3","$4","$5","$6","$7"/g' > kameiten.csv
※変換しているのは「①固定店舗(EC・通信販売を除く)」のページです。
※変換結果のCSVは、14MBくらいです。
背景
国のキャッシュレスポイント還元PRサイト、「使えるお店一覧」から18万店を網羅した3608ページのPDFに飛ばす(ねとらぼ)
最近話題の「キャッシュレス・消費者還元事業」のPRサイトで公開されているPDFについて、「せめてCSVにしろよ…」という声が多数あったので、ワンライナーでCSVに変換してみました。
ちなみにすでにCSVに変換している人はいます。早い。
twitterの方は結果をGoogleドライブに公開されています!
PDFファイル(3608ページ)から表形式のテキストデータをタブ区切りで抽出するシェルスクリプト
ひまだったので、pdfの全ページをテキストファイルにコピペして、あれこれやってcsvにしてみました。
— かみ かずしげ (@kami_kazushige) 2019年9月2日
Googleドライブに入れておきますので、どうぞ御自由にお使いください。https://t.co/WjVJALXixj
Webサービス公開している人まで…。早すぎ。
zaimも公開していますね。
また、公式サイトには「※9月中下旬には、地図上に対象店舗を表示するウェブ機能やアプリを公表予定」との記載があります。
ただ、この記事の余談で書いているようにデータ不備があったりしますが、大丈夫なのでしょうか。。
ワンライナーの解説
一応解説します。
pdftotextでテキストに変換
pdftotext -raw -f 3 -l 2795 kameiten_touroku_list.pdf -
pdftotext
という、その名の通りPDFをテキスト変換してくれるコマンドがあります。
オプションには以下を指定しています。
-f : 開始ページを指定
-l : 終了ページを指定
-raw : テキストをコンテンツストリームの順序通りに出力する(非推奨)
-layout
でもよいのですが、余計なスペースが出力されてしまうので、-raw
を指定しています。
ただ、-raw
は非推奨(Use of raw mode is no longer recommended.)らしいです。まあお遊びなので良いでしょう。
これを実行すると、以下のようなテキストに変換されます。(とりあえず最初のページのみ対象)
pdftotext -raw -f 3 -l 3 kameiten_touroku_list.pdf -
キャッシュレス・消費者還元事業 事務局審査を通過した加盟店一覧
①固定店舗(EC・通信販売を除く) 令和元年8月21日 現在
※9月中に地図上に掲載予定
No. 都道府県 市区町村 事業所名(屋号) 業種 還元率
1 北海道 愛別町 セブン-イレブン愛別町店 小売業 食料品 2%
2 北海道 愛別町 伊藤新聞販売所 小売業 その他小売 5%
…(中略)…
49 北海道 旭川市 旭川お城の鯉寿し サービス 飲食業 5%
50 北海道 旭川市 セブン-イレブン旭川買物公園店 小売業 食料品 2%
1
必要なレコード行のみ抽出
grep -E '^[0-9]+(,[0-9]+)* |[25]%$'
ページごとに、説明文と表のヘッダーとページ数が挿入されているので、grep
で必要なレコード行のみ抽出します。
判定として、「先頭が数字で直後に半角スペースがある行」または「末尾が'2%'または'5%'の行」としています。
なぜ2つの条件を OR としているかは、次のステップで説明します。
まあ、判定にはもっとスマートな方法がありそうですが。。
pdftotext -raw -f 3 -l 3 kameiten_touroku_list.pdf - | grep -E '^[0-9]+(,[0-9]+)* |[25]%$'
1 北海道 愛別町 セブン-イレブン愛別町店 小売業 食料品 2%
2 北海道 愛別町 伊藤新聞販売所 小売業 その他小売 5%
…(中略)…
49 北海道 旭川市 旭川お城の鯉寿し サービス 飲食業 5%
50 北海道 旭川市 セブン-イレブン旭川買物公園店 小売業 食料品 2%
2行に渡っているレコードを1行にする
perl -pe 's/(?<![25]%)\n/ /g'
先ほどの結果でほぼ表形式のテキストが取得できていますが、一つ注意点があります。
例として、「さいたま市岩槻区」の箇所を見てみます。
pdftotext -raw -f 468 -l 468 kameiten_touroku_list.pdf - | grep -E '^[0-9]+(,[0-9]+)* |[25]%$'
23,251 埼玉県 さいたま市 マクドナルド埼大通り店 サービス 飲食業 2%
…(中略)…
23,288 埼玉県 さいたま市 ワンステップ大宮店 サービス 理容・美容業 5%
23,289 埼玉県 さいたま市岩槻区
アトリエビザビ 小売業 衣料品 5%
23,290 埼玉県 さいたま市岩槻区
伊太利家 Pizzeria サービス 飲食業 5%
…(中略)…
23,300 埼玉県 さいたま市岩槻区
セブン-イレブン岩槻本丸店 小売業 食料品 2%
なんと、レコードが2行になってしまっています。
これは、「さいたま市岩槻区」という市区町村名が、PDFの表の幅からはみ出しているために起こっているようです。
前のステップで「先頭が数字で直後に半角スペースがある行」 または 「末尾が'2%'または'5%'の行」としていたのは、これが理由です。
では、改行を置換して1行にしてみます。sed でも出来そうですが、perl 使った方が簡単かなと思います。
置換は「末尾が'2%'でも'5%'でもない行」について、「改行を半角スペースに変換」としています。
pdftotext -raw -f 468 -l 468 kameiten_touroku_list.pdf - | grep -E '^[0-9]+(,[0-9]+)* |[25]%$' | perl -pe 's/(?<![25]%)\n/ /g'
23,251 埼玉県 さいたま市 マクドナルド埼大通り店 サービス 飲食業 2%
…(中略)…
23,288 埼玉県 さいたま市 ワンステップ大宮店 サービス 理容・美容業 5%
23,289 埼玉県 さいたま市岩槻区 アトリエビザビ 小売業 衣料品 5%
23,290 埼玉県 さいたま市岩槻区 伊太利家 Pizzeria サービス 飲食業 5%
23,291 埼玉県 さいたま市岩槻区 出光上野SS 小売業 ガソリンスタンド 2%
23,292 埼玉県 さいたま市岩槻区 出光平林寺SS 小売業 ガソリンスタンド 2%
23,293 埼玉県 さいたま市岩槻区 セブン-イレブン岩槻江川店 小売業 食料品 2%
23,294 埼玉県 さいたま市岩槻区 セブン-イレブン岩槻駅東口店 小売業 食料品 2%
23,295 埼玉県 さいたま市岩槻区 セブン-イレブン岩槻鹿室店 小売業 食料品 2%
23,296 埼玉県 さいたま市岩槻区 セブン-イレブン岩槻慈恩寺店 小売業 食料品 2%
23,297 埼玉県 さいたま市岩槻区 セブン-イレブン岩槻西原台1丁目店 小売業 食料品 2%
23,298 埼玉県 さいたま市岩槻区 セブン-イレブン岩槻府内1丁目店 小売業 食料品 2%
23,299 埼玉県 さいたま市岩槻区 セブン-イレブン岩槻本町店 小売業 食料品 2%
23,300 埼玉県 さいたま市岩槻区 セブン-イレブン岩槻本丸店 小売業 食料品 2%
1行になっていますね。
CSV形式に変換
perl -pe 's/^(\S+) (\S+) (\S+) (.+) (\S+) (\S+) (\S+)$/"$1","$2","$3","$4","$5","$6","$7"/g'
では最後、半角スペース区切りのデータを、ダブルクォーテーションで囲んでカンマで区切るだけです。
…だけ…なのですが、意外とここが一番苦労しました。
なぜなら、事業所名にも半角スペースが入っているからです。
たとえば、先頭から4つ目のレコードを見ると、
4 北海道 赤平市 赤平 SS 小売業 ガソリンスタンド 2%
となっています。
事業所名は「赤平 SS」なので、結果としては以下のようにしたいです。
"4","北海道","赤平市","赤平 SS","小売業","ガソリンスタンド","2%"
つまり、先頭から1,2,3番目のカラムはそのまま半角スペース区切り。
末尾から1,2,3番目のカラムも半角スペース区切り。
で、その間の n 個の要素については、ひとまとめの項目にしたい、ということになります。
awk でループ回して…というのも考えましたが、ループ使うのもアレなので、正規表現でどうにか置換しました。
見てわかるように、前から3つ、後ろから3つのスペース以外の文字のまとまりと、その間の要素…という感じでゴリゴリ書いて、置換しています。
この辺のもっと良い書き方は、誰か教えていただけると嬉しいです。。正規表現難しい。。
pdftotext -raw -f 3 -l 3 kameiten_touroku_list.pdf - | grep -E '^[0-9]+(,[0-9]+)* |[25]%$' | perl -pe 's/(?<![25]%)\n/ /g' | perl -pe 's/^(\S+) (\S+) (\S+) (.+) (\S+) (\S+) (\S+)$/"$1","$2","$3","$4","$5","$6","$7"/g'
"1","北海道","愛別町","セブン-イレブン愛別町店","小売業","食料品","2%"
"2","北海道","愛別町","伊藤新聞販売所","小売業","その他小売","5%"
"3","北海道","愛別町","三愛自動車工業株式会社","その他業種","ー","5%"
"4","北海道","赤平市","赤平 SS","小売業","ガソリンスタンド","2%"
…(中略)…
"49","北海道","旭川市","旭川お城の鯉寿し","サービス","飲食業","5%"
"50","北海道","旭川市","セブン-イレブン旭川買物公園店","小売業","食料品","2%"
ちょっと無理やりですが、何とか変換できました!
余談というか注意点
データ眺めていて気づいたのですが、かなり不備がありますね、これ…。
たとえば、No.37と38のところ。
半角スペースが入っているだけで、全く同じデータが入っています。
"37","北海道","旭川市","旭川末広SS","小売業","ガソリンスタンド","2%"
"38","北海道","旭川市","旭川末広 SS","小売業","ガソリンスタンド","2%"
明らかに入力ミスっぽいやつとか。
"3,896","北海道","札幌市中央区","函館開陽亭すすきの プラス 大三坂","サービス","飲食業","5%"
"3,897","北海道","札幌市中央区","函館開陽亭すすきの プラス 大三","サービス","飲食業","5%"
"3,898","北海道","札幌市中央区","函館開陽亭すすきの プラス 大","サービス","飲食業","5%"
事業所名が謎の「本社」だったり。事業所名ってそういうものなんでしょうか?
「本社ダミー」とか、明らかにおかしい気がするけど…。
"852","北海道","厚岸町","本社","小売業","その他小売","2%"
"1,040","北海道","岩見沢市","本社","小売業","ガソリンスタンド","2%"
"1,252","北海道","遠軽町","本社ダミー","小売業","ガソリンスタンド","2%"
"1,288","北海道","長万部町","本社","小売業","その他小売","2%"
"1,672","北海道","帯広市","本社","小売業","その他小売","2%"
"2,178","北海道","釧路市","本社POS","小売業","その他小売","2%"
利用する際には、注意しましょう。そのうち直るのかな…。