郵便番号データのCSVが使いにくいので、pythonで整形する

日本郵便で公開されている郵便番号データをデータベースのSQLiteで利用しようと思い、CSVファイルをダウンロード。京都の住所は通り名の有り無しなど、同じ郵便番号でも複数の住所表記の仕方があるので、CSVでどのように処理されているか京都の郵便番号データで確認してみます。すると、このCSVをそのまま使える人はいるのか?っていうぐらい、面倒なデータだということがわかりました。そこで、使いやすいようにpythonでデータを整形することにします。

日本郵便 郵便番号データダウンロードはこちら

CSVファイルを分析

同じ郵便番号なのに町名が複数行に分割

同じ郵便番号なのに町域名(漢字)が複数行に分割されています。町域名(漢字)以外の項目はすべて同じです。しかも、複数行に分ける区切り方がよくわかりません。

通り名にフリガナの有無と複数行に分割

町域名の通り名にフリガナが有るのと無いのがあります。しかも、複数行に分割してあります。法則が全くわかりません。

郵便番号データファイルの形式等

法則性がよくわからないので、日本郵便のホームページで確認します。

出典:日本郵便 郵便番号データの説明

全角となっている町域部分の文字数が38文字を越える場合、また半角となっているフリガナ部分の文字数が76文字を越える場合は、複数レコードに分割しています。

どうやら文字制限が全角38文字のようです。

他の町域名データ見てみると、どうやら10番目の項目が1のときに、町域名の()部分にフリガナがつけられるらしい、ということがわかりました。しかし、10番目の項目は必要ないので出来れば取り込みたくありません。そこで、「、()」の有無で条件分岐する方法を考えてみました。

pythonでCSVデータを整形

同じ郵便番号の町域名(漢字)を1つに結合できました。

フリガナも1つに結合。これで何とか使えそうです。

抽出したデータ項目

  • 郵便番号 7桁 …… 半角数字
  • 都道府県名 ………… 半角カタカナ(コード順に掲載)
  • 市区町村名 ………… 半角カタカナ(コード順に掲載)
  • 町域名 ……………… 半角カタカナ(五十音順に掲載)
  • 都道府県名 ………… 漢字(コード順に掲載)
  • 市区町村名 ………… 漢字(コード順に掲載)
  • 町域名 ……………… 漢字(五十音順に掲載)

整形したCSVデータ

使用・再配布・移植・改良について
郵便番号データに限っては日本郵便株式会社は著作権を主張しません。自由に配布していただいて結構です。

出典:日本郵便

らしいので、自分で使う用に整形したCSVファイルを置いておきます。京都の郵便番号データが欲しかっただけなので、すべてのデータが綺麗に整形出来ているかは確認していません。

→少しだけチェックしてみました。

京都の郵便番号データ (CSV)

全国の郵便番号データ (CSV)

ソースコード (python)

複数行に分割されている、町域名(漢字)を結合したいので、「、()」の有無で条件分岐しています。郵便番号が1つ前と同じで、()の片方が残っている場合、町域名を結合しています。

import csv

# 空のリストを作成
zip_data = []

# 全国一括の郵便番号のCSVファイルを開いて読込
with open('KEN_ALL.CSV', 'rt', encoding='Shift-JIS') as f:
    reader = csv.reader(f)

    # 必要な項目を取出して、1行ずつリストに書込
    for row in reader:
        zcode = row[2]      # 郵便番号
        kana_ken = row[3]   # 都道府県名(半角カタカナ)
        kana_shi = row[4]   # 市町村名(半角カタカナ)
        kana_cho = row[5]   # 町域名(半角カタカナ)
        ken = row[6]        # 都道府県名(漢字)
        shi = row[7]        # 市町村名(漢字)
        cho = row[8]        # 町域名(漢字)

        # 町域名が「以下に掲載がない場合」は、空の要素を入力
        if kana_cho == 'イカニケイサイガナイバアイ':
            kana_cho = ''
        if cho == '以下に掲載がない場合':
            cho = ''
        
        # 必要な項目をリストに追加
        zip_data.append([zcode, kana_ken, kana_shi, kana_cho, ken, shi, cho])

# 複数行に分割されている町域名を結合
# 郵便番号と「、()」で判別
# リストの件数回繰返し
for i in range(len(zip_data)) :
    if i < len(zip_data):
        # 一時的に1つ前の町域名を抽出
        tmp_kana_cho = zip_data[i-1][3]
        tmp_cho = zip_data[i-1][6]
        # 1つ前の郵便番号が同じ場合、「、()」で条件分岐
        # \はコードを途中で改行する記号
        while ((zip_data[i][0] == zip_data[i-1][0]) and ('(' in zip_data[i-1][6]) and ('、' in zip_data[i][6]) and ('(' not in zip_data[i][6]) and (')' not in zip_data[i][6])) \
            or ((zip_data[i][0] == zip_data[i-1][0]) and (')' in zip_data[i][6]) and ('(' not in zip_data[i][6])):
            # 1つ前の町域名(半角カタカナ)が違う場合、町域名(半角カタカナ)を追加
            if zip_data[i][3] != zip_data[i-1][3]:
                tmp_kana_cho += zip_data[i][3]
                zip_data[i-1][3] = tmp_kana_cho
            # 1つ前の町域名(漢字)に現在の町域名(漢字)を追加
            tmp_cho += zip_data[i][6]
            zip_data[i-1][6] = tmp_cho
            # 郵便番号が重複するので、現在のリストを削除
            del zip_data[i]

# CSV形式でファイル作成
# UTF-8がいらない場合は以下3行をコメントアウト
with open('20201130_zipcode_all_utf8.csv','wt', encoding='UTF-8', newline='') as f: 
    writer = csv.writer(f)
    writer.writerows(zip_data)

# Shift-JISの場合は、以下3行をアンコメント
# with open('20201130_zipcode_all_shiftjis.csv','wt', encoding='Shift-JIS', newline='') as f:
#     writer = csv.writer(f)
#     writer.writerows(zip_data)