Help us understand the problem. What is going on with this article?

キャッシュレス還元対象店一覧(PDF 3608ページ)をワンライナーでCSV形式にしてみた

とりあえずワンライナー

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ページ)から表形式のテキストデータをタブ区切りで抽出するシェルスクリプト

Webサービス公開している人まで…。早すぎ。

ポイント還元ドットコム

zaimも公開していますね。

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%"

利用する際には、注意しましょう。そのうち直るのかな…。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした