Pythonで複数の住所の座標を検索→CSVに出力するスクリプトを書いてみた
しまの時刻表に必要なデータを集める中で、各島の位置情報(座標)調べるのがなかなか面倒だったんだけど、Python使ったら結構楽に解決できたのでメモしておく。
元となるデータの収集・抽出
離島経済新聞のサイトに日本の有人離島の一覧が載っているページがあるので、そちらを利用させてもらった。
内容がコロコロ変わるような性質のものではないので、ソースをコピーしてローカルに保存→そこからデータを読み出すようにした。
from bs4 import BeautifulSoup soup = BeautifulSoup(open("rito.html"), "lxml") html = soup.find(class_='land-list').find_all('a')
スクレイピングはBeautifulSoup4でさくっと。
これでサイトの凡例通りの『島名(指定地域名|市町村名)』の部分が取り出せた。
for h in html: text = re.split('[(|)]', h.text) # ex. text = ['中島', '忽那諸島', '松山市', ''] name = text[0] area = text[2] address = area + name
経験上Mapで島を検索する際は「市町村名+島名」だと引っかかりやすいのでその形に。
変数textの箇所は正規表現使ってもっとキレイにしたかったんだけど、今回指定地域名は使わないのでカッコ等で区切った配列を作成→島名、市町村名の部分を抜き出して利用することにした。
Geocoding APIを叩いて位置情報を取得
住所→座標の取得はGoogle MapsのGeocoding APIから出来るっぽい。
最初はAPIキーの取得から。
Geocoding APIが含まれているのは右端の「プレイス」なのでそちらを選択。以前にサイト本体用のキーを取ったのと同じノリで「マップ」を選択して一瞬ん?ってなったのは内緒。
必要なモジュールをpipでインストールしたらAPIを呼び出せるようになる。
$ pip install googlemaps $ pip install pygeocoder
コード自体は非常にシンプルなので、コピペで簡単に使いまわしできそう。
# 関係ある部分だけ抜粋、最終的なソースは記事の後半で from pygeocoder import Geocoder import googlemaps body = [] apikey = '自分のAPIキー' gmaps = googlemaps.Client(key=apikey) for h in html: text = re.split('[(|)]', h.text) name = text[0] area = text[2] address = area + name result = gmaps.geocode(address) if(result != []): lat = result[0]["geometry"]["location"]["lat"] lng = result[0]["geometry"]["location"]["lng"] else: lat = "" lng = "" body.append([name, area, lat, lng]) print(name + 'の位置情報を取得')
住所が検索でヒットしなかった場合は空の配列で返ってくるので、そこ避けるためにif書いたくらい。
後でCSVに出力する用に配列を置いて、取ってきたデータをぽんぽん放り込んでいく。
全部で400件くらいデータあったんだけど、だいたい1回2,3分くらいで仕上げてくれた。
CSVへの書き込み部分
教科書通り、というかどっかからのコピペなので特に言うことはなし。
header = ['島名', '地域', '緯度', '経度'] #CSVへの出力処理 with open('data.csv', 'w') as f: writer = csv.writer(f) writer.writerow(header) writer.writerows(body) print('CSVへの出力完了')
最終的な全体のコードはこんな感じ。
#coding:utf-8 from bs4 import BeautifulSoup import re import csv # Geocoding API叩く用 import urllib # Google API モジュール from pygeocoder import Geocoder import googlemaps soup = BeautifulSoup(open("rito.html"), "lxml") html = soup.find(class_='land-list').find_all('a') header = ['島名', '地域', '緯度', '経度'] body = [] apikey = '自分のAPIキーの値' gmaps = googlemaps.Client(key=apikey) for h in html: text = re.split('[(|)]', h.text) name = text[0] area = text[2] address = area + name result = gmaps.geocode(address) if(result != []): lat = result[0]["geometry"]["location"]["lat"] lng = result[0]["geometry"]["location"]["lng"] else: lat = "" lng = "" body.append([name, area, lat, lng]) print(name + 'の位置情報を取得') #CSVへの出力処理 with open('data.csv', 'w') as f: writer = csv.writer(f) # writerオブジェクトを作成 writer.writerow(header) # ヘッダーを書き込む writer.writerows(body) # 内容を書き込む print('CSVへの出力完了')
スプレッドシートに上げておいたので、事あるごとに使うことにしよう。
感想とか
今までgeocoding APIの存在を知らなくて、本家Google Mapのサイトで得たい場所を検索→右クリックして座標をコピペをちまちまやっていたんだけど、それが一瞬で片付くようになったのは非常にありがたかった。
ただ位置情報関係でいうと、島ごとの港+そこと結ばれている本土の港の位置情報も集めなきゃいけないのがなかなかしんどいけど、今回書いたやつを一部修正して使い回すようにすればそこまで手間はかからないと思う。
これからもっと退屈なことはPythonにやらせよう。
2019/05/09追記
何の考えもなしに1回数百回API叩きまくってたら、いつの間にか請求額が結構な値段になってた。
GCPは最近登録したばっかりで、今回の請求もクレジットの範囲内でまかなえるっぽいけど、ちーと気をつけんといかんなと思った次第。