先日、国土地理院APIで住所から緯度・経度を取得する方法をご紹介しました。
elsammit-beginnerblg.hatenablog.com
こちらの記事の最後にcsvファイルからまとめた住所データから緯度・経度を出力するコードを載せたのですが、
今回はこちらのコードについてブログにてご紹介していきたいと思います。
■環境
前回の記事と同様に今回も使用するのはpythonです。
pythonのバージョンはPython 3.7.3です。
また今回、csvファイルの読み込み・書き込みを行う関係でcsvライブラリを用います。
未インストールの方はこちらでインストールを行ってください。
pip3 install python-csv
ライブラリは他に前回と同様にurllib、requestsを用いますので、
pip3 install urllib3 pip3 install requests
でインストールしておいてください。
■条件
今回想定するcsvはこちらのように、
1行目をヘッダー、2行目以降にデータが格納された構成にします。
要素は住所が存在すればOKです。
id,店名,住所 1,札幌駅,北海道札幌市北区北6条西4丁目 2,東京駅,東京都千代田区丸の内1丁目 3,蒲郡駅,愛知県蒲郡市元町1
■コード紹介
ではcsvファイルから住所情報を読み出し、緯度・経度をcsv出力するコードを紹介していきます。
全体のコードはこちらです。
# -*- coding: utf-8 -*- import csv import sys import urllib.parse # pip3 install urllib3 import json # https://note.com/masato1230/n/nba86746179ca import requests # pip3 install requests import time import os def main(): args = sys.argv # 実行時の引数取得 if len(args) < 2: # 実行時にcsvファイルを指定していなかった場合はエラーとする print("[Error] csvファイルを引数に与えてください") return if os.path.exists(args[1]) == False: # 指定したファイルが存在しない場合にはエラーとする print("[Error] 指定したファイルがみつかりませんでした") return fileName = os.path.splitext(args[1]) # 指定したファイルがcsvファイルでなければエラーとする if fileName[1] != ".csv": print("拡張子はcsvファイルにしてください") return i = 0 # ヘッダーとbody切り替え用 AddressNum = -1 # 住所 or Addressが書かれた番号 MapLists = [] # csvから読み出しかつ緯度・経度を追加する用変数 # 国土地理院URL # APIの使用方法等は下記を参考のこと # https://libraries.io/github/gsi-cyberjapan/internal-search makeUrl = "https://msearch.gsi.go.jp/address-search/AddressSearch?q=" with open(args[1],encoding='utf-8') as f: # 引数に与えたcsvファイル読み込み reader = csv.reader(f) for line in reader: # 各行読み取り buf = [] if i == 0: # ヘッダーの場合 j = 0 # 列番号カウント用変数 for item in line: # 列読み取り if item == "住所" or item == "Address" or item == "address": AddressNum = j # 住所を見つけたらその時の番号を変数に格納 j+=1 # 列番号カウンタをインクリメント buf.append(item) # 取得した列をbufに格納 if AddressNum < 0: # 住所やアドレスがなかったらエラーとして修了 print("[Error] 住所もしくはAddressが項目にありません") return buf.append("latitude") # ヘッダーの列にlatitudeを追加 buf.append("longitude") # ヘッダーの列にlongitudeを追加 MapLists.append(buf) # ヘッダー行をMapListsに格納 else: # body側の各行情報 for item in line: # 各行ごとのカラムを取得 buf.append(item) # 取得したカラムを変数に格納 print(line[AddressNum]) s_quote = urllib.parse.quote(line[AddressNum]) # 住所の文字列をURLエンコード response = response = requests.get(makeUrl + s_quote) # エンコードした文字列を国土地理院APIの引数として与えてget request if response.json() == []: # レスポンスされたjsonデータの中身を確認し空だったら print("[Error] 住所がよくわかりませんでした") # 判定できなかった旨を出力し緯度・経度は空文字を格納 buf.append("") buf.append("") elif len(response.json()) >1: # 候補が複数あった場合、判定出来ないためスキップ print("[Error] 住所の絞り込みが出来ず複数候補が出ました \n 住所の絞り込みをおこなってください ") buf.append("") buf.append("") else: # レスポンスされたjsonデータが空でなかった場合 print(response.json()[0]["geometry"]["coordinates"]) buf.append(response.json()[0]["geometry"]["coordinates"][0]) # 緯度情報をbufに格納 buf.append(response.json()[0]["geometry"]["coordinates"][1]) # 経度情報をbufに格納 MapLists.append(buf) # 各行ごとの情報をMapListsに格納 time.sleep(1) # 国土地理院APIに負荷を掛けないように i+=1 with open("output.csv", mode='w',newline='',encoding='shift_jis') as f: # データをcsvファイルへ格納 writer = csv.writer(f) for maplist in MapLists: # 各行の情報を格納した変数を読み出す writer.writerow(maplist) # 読み出したデータをcsvへ書き込み print("Write Finish !!") if __name__ == "__main__": main()
まず、csvファイルからデータを読み出し、reader変数に格納します。
with open(args[1],encoding='utf-8') as f: # 引数に与えたcsvファイル読み込み reader = csv.reader(f)
次に、
for line in reader: # 各行読み取り ~~~ for item in line: # 列読み取り
にて読み出したデータを1行ごとに分割さらに各列でパースしていきます。
最初はヘッダーになるので、ヘッダー名に"住所"要素がある列番号をセットします。
for item in line: # 列読み取り if item == "住所" or item == "Address" or item == "address": AddressNum = j # 住所を見つけたらその時の番号を変数に格納 j+=1 # 列番号カウンタをインクリメント buf.append(item) # 取得した列をbufに格納
後は前回と同様に住所データを国土地理院のAPIに渡して緯度・経度を取得すればOKです。
for item in line: # 各行ごとのカラムを取得 buf.append(item) # 取得したカラムを変数に格納 s_quote = urllib.parse.quote(line[AddressNum]) # 住所の文字列をURLエンコード response = response = requests.get(makeUrl + s_quote) # エンコードした文字列を国土地理院APIの引数として与えてget request buf.append(response.json()[0]["geometry"]["coordinates"][0]) # 緯度情報をbufに格納 buf.append(response.json()[0]["geometry"]["coordinates"][1]) # 経度情報をbufに格納 MapLists.append(buf) # 各行ごとの情報をMapListsに格納 time.sleep(1) # 国土地理院APIに負荷を掛けないように
AddressNumにはヘッダーで検索した住所列番号が格納されているので、line[AddressNum]には各住所情報が格納されております。
ですので、line[AddressNum]をurllib.parse.quoteによりURLエンコードしてAPIに渡して緯度・経度を取得しています。
そして、MapListsにデータを格納。
MapListsは最後にcsv出力するためのデータ群が格納されます。
データを読み出して緯度・経度を取得⇒MapListsに格納まで出来たので最後にcsvファイル出力です。
コードはこちら。
with open("output.csv", mode='w',newline='',encoding='shift_jis') as f: # データをcsvファイルへ格納 writer = csv.writer(f) for maplist in MapLists: # 各行の情報を格納した変数を読み出す writer.writerow(maplist) # 読み出したデータをcsvへ書き込み
MapListsを1行ごとに分割しwriter.writerowで1行ずつcsvファイルに書き込み。
■コードの使い方
本コードは下記の通り実行すれば引数として渡したcsvファイルから読み取り、output.csvが出力されます。
python3 ファイル名.py csvファイル名.csv
APIに負担を掛けないように1秒ごとにwaitを入れ込んでいます。
この関係で複数住所データが格納されていると少し時間がかかってしまいます。
コーヒーを飲んでゆっくり待ってください。
■最後に
今回はcsvファイルにまとめた住所情報から緯度・経度を算出しcsv出力するコードをご紹介しました。
こちらにcsvファイルにまとめた住所データから緯度・経度を算出するコードを格納しておりますので、
よろしければご覧ください。
https://github.com/Elsammit/SearchAddressToMapInfo
■参考
https://pypi.org/project/python-csv/
https://qiita.com/motoki1990/items/0274d8bcf1a97fe4a869