今回は郵便番号から住所を検索するツールを作ってみよう。そのために、郵便局のWebサイトで配布されているCSV形式のファイルを利用して独自のデータベースを作る。たくさんのフィールドを持つCSVファイルから必要な項目だけを抽出する方法や、データベースに保存する方法を紹介する。
Pythonで身近になったAIを実践する鍵はデータ処理にあり
ちょっと前まで専門家の領域だと思われていた機械学習、深層学習などAIによる処理がPythonのおかげでとても身近になった。Pythonのscikit-learnやTensorFlowなどのライブラリを使えばデータの自動分類ツールや予測ツールを自作できる。
ただし、そのためにはそれらAIのライブラリに読み込ませるために、しっかり整ったデータを用意する必要がある。大抵の場合、現実に提供されるデータをそのままライブラリに読み込ませることはできない。何かしらの変換処理をしたり、必要なデータだけを抽出する必要がある。
それは、現実のデータにあるデータに対して泥臭い整形処理をして、ライブラリに読み込ませることが必要であることを意味している。つまり、データ処理を制するものがAIを制すということなのだ。
CSV形式の郵便番号データをダウンロードしよう
そこで、今回はAI用のデータとしてよく使われるCSV形式のデータを例にして、簡単な抽出処理を紹介しよう。具体的には郵便局のWebサイトで配布されているCSV形式の郵便番号データを読み込んで必要なフィールドだけを抽出してみよう。
郵便番号データ自体は郵便局のWebサイトで無料で配布されている。こちらのページからダウンロードしよう。ここでは「全国一括」のCSV形式データを題材として利用する。
郵便番号データはZIP形式で提供されているので解凍しよう。すると、KEN_ALL.CSVというファイルがある。これは12MBもある巨大なCSVデータだ。
もしもマシンの性能が低い場合、このCSVファイルをうっかりExcelで読み込んでしまうとPCがフリーズしてしまうこともある。ファイルサイズが大きくちょっと扱いづらい。そのため、郵便局のページでは都道府県別のデータを配布しているのだと思われる。
しかし、ExcelでCSVファイルを開いてみると普段使わないフリガナやその他の情報がたくさん含まれていることに気付くだろう。そこで、Pythonのプログラムで不要なフィールドを覗いた必要最低限の情報だけをSQLiteのデータベースに格納し、後から手軽に活用できるようにしてみよう。
PythonでCSVファイルを読む
最初にPythonで郵便番号データのKEN_ALL.CSVを読み込んで必要なフィールドだけ取り出すプログラムを作ってみよう。PythonにはCSVファイルを読み込む専用のライブラリcsvモジュールがあるのでこれを利用してみる。
なお、本連載の11回目でも郵便番号データを利用してCSVファイルを読み込む方法を紹介している。ただし11回目では、csvモジュールを利用していない。モジュールを使う場合と使わない場合で比較してみると良いだろう。
import csv
# ファイルを開く --- (*1)
with open('KEN_ALL.CSV', 'rt', encoding='Shift_JIS') as fp:
# CSVを読み込む --- (*2)
reader = csv.reader(fp)
# 一行ずつ処理する --- (*3)
for row in reader:
# 必要なフィールドだけ取り出す --- (*4)
zipno = row[2] # 郵便番号
ken = row[6] # 都道府県
shi = row[7] # 市区
cho = row[8] # 市区以下
if cho == '以下に掲載がない場合': cho = ''
title = ken + shi + cho
print(zipno, title)
上記のプログラムを「readcsv.py」という名前で保存し、「KEN_ALL.CSV」を同じフォルダに配置しよう。そして、Pythonで実行してみよう。WindowsならPowerShell、macOSならターミナル.appで以下のコマンドを実行しよう。
# Windowsの場合
python readcsv.py
# macOS/Linuxの場合
python3 readcsv.py
すると、以下のようにコマンドラインに郵便番号と住所の一覧が表示される。
プログラムを確認してみよう。(*1)の部分では、ファイルを開いている。ファイル「KEN_ALL.CSV」は文字エンコーディングがShift_JISとなっているため、これを明示する必要がある。そして、(*2)の部分でcsvモジュールを用いてファイルの内容を解析するよう指定している。続いて、(*3)の部分でfor文を利用して一行ずつCSVファイルを解析して取り出している。このとき変数rowにはリスト形式でCSVの各フィールドの値が得られる。そのため、要素番号を指定して必要なフィールド値を取り出すことができる。
SQLiteデータベースに保存しよう
CSVの扱い方が分かったので次にデータをデータベースに格納してみよう。なぜ敢えてデータベースに入れるのかと言えば、データベースにデータを入れておけば、検索や抽出が容易になるからだ。餅は餅屋、データはデータベースに任せるのが一番なのだ。SQLiteは組み込みのデータベースでありPythonからも手軽に扱えるので優れている。しかもデータベース1つが1ファイルになるため可搬性も高い。
それでは、CSV形式の郵便番号データをSQLiteに格納するプログラムは以下のようになる。
import csv, sqlite3
# SQLite3のデータベースを開く --- (*1)
conn = sqlite3.connect('zip.sqlite3')
c = conn.cursor()
# テーブルを作る --- (*2)
c.execute('''CREATE TABLE zip (
zipno text, ken text, shi text, cho text)''')
c.execute('begin')
# CSVファイルを開く
with open('KEN_ALL.CSV', 'rt', encoding='Shift_JIS') as fp:
# CSVを読み込む
reader = csv.reader(fp)
# 一行ずつ処理する
for row in reader:
zipno = row[2] # 郵便番号
ken = row[6] # 都道府県
shi = row[7] # 市区
cho = row[8] # 市区以下
if cho == '以下に掲載がない場合': cho = ''
# SQLiteに追加 --- (*3)
c.execute('''INSERT INTO zip (zipno,ken,shi,cho)
VALUES(?,?,?,?)''', (zipno,ken,shi,cho))
# データベースを閉じる --- (*4)
c.execute('commit')
conn.close()
上記のプログラムを「csv2sqlite.py」という名前で保存しよう。そして同じようにCSVファイルと同じディレクトリに配置して、以下のコマンドを実行しよう。
# Windowsの場合
python csv2sqlite.py
# macOS/Linuxの場合
python3 csv2sqlite.py
実行してしばらくすると「zip.sqlite3」という名前のデータベースが作成される。
プログラムを確認してみよう。(*1)ではSQLite3のデータベースを開く。(*2)でデータベースのテーブルを作成する。SQLiteをはじめ多くのデータベースでは最初にどんなデータを格納するのか、フィールドを定義したテーブルを作成する必要がある。そして(*3)の部分でデータベースにデータを挿入し、(*4)の部分でデータベースを閉じる。なお、データベースに対してbeginとcommitというコマンドを実行しているが、これを指定することで大量のデータ挿入の操作が高速になる。
データベースからデータを取り出そう
続いて、作成したデータベースからデータを取り出してみよう。
import sqlite3
# データベースを開く --- (*1)
conn = sqlite3.connect('zip.sqlite3')
cur = conn.cursor()
# データベースから郵便番号を検索する関数 --- (*2)
def zip2addr(zipno):
cur.execute('SELECT * FROM zip WHERE zipno=?', [zipno])
r = cur.fetchone()
return r
# 郵便番号検索を実行 --- (*3)
if __name__ == '__main__':
print(zip2addr('1050011'))
print(zip2addr('6008213'))
上記のプログラムを「checkzip.py」という名前で保存しよう。そして、以下のコマンドを実行すると(*3)の部分で指定した郵便番号に対応した住所が以下のように表示される。
('1050011', '東京都', '港区', '芝公園')
('6008213', '京都府', '京都市下京区', '東塩小路向畑町')
プログラムを確認してみよう。(*1)ではデータベースを開く。(*2)ではデータベースから任意の郵便番号を検索して結果を一つ取り出す関数zip2addrを定義した。そして、(*3)では関数zip2addrを実行して郵便番号から住所を取り出して表示する。
GUIで使えるようにしてみよう
そして、せっかくならGUIのインターフェイスを作って、気軽に利用できるよう工夫してみよう。上記の「checkzip.py」と同じフォルダに以下のプログラムを配置しよう。
import tkinter as tk
import tkinter.messagebox as mb
import tkinter.simpledialog as sd
import checkzip
# tkinterの窓を表示しないように
tk.Tk().withdraw()
# 一行入力ボックスで郵便番号を尋ねる --- (*1)
zipno = sd.askstring(
'郵便番号検索', '郵便番号は?',
initialvalue='1050011')
# 郵便番号を表示 --- (*2)
r = checkzip.zip2addr(zipno)
mb.showinfo("結果:", "".join(r))
以下のようにコマンドラインから保存したプログラム「checkzip-gui.py」を実行すると、ダイアログで郵便番号の入力画面が表示される。郵便番号を入力すると住所が表示される。
# Windowsの場合
python checkzip-gui.py
# macOS/Linuxの場合
python3 checkzip-gui.py
以下実際に実行してみたところ。
まとめ
以上、今回はCSV形式の郵便番号データのうち、必要な項目だけを抽出してSQLiteのデータベースに挿入してみた。一度データベースに入れてしまえば、手軽にデータを検索することができる。CSVファイルから任意のフィールドを抽出しデータベースに入れることは、郵便番号データだけでなくいろいろな場面で利用できるので参考になるだろう。
自由型プログラマー。くじらはんどにて、プログラミングの楽しさを伝える活動をしている。代表作に、日本語プログラミング言語「なでしこ」 、テキスト音楽「サクラ」など。2001年オンラインソフト大賞入賞、2004年度未踏ユース スーパークリエータ認定、2010年 OSS貢献者章受賞。技術書も多く執筆している。