Pythonで複数の住所の座標を検索→CSVに出力するスクリプトを書いてみた

しまの時刻表に必要なデータを集める中で、各島の位置情報(座標)調べるのがなかなか面倒だったんだけど、Python使ったら結構楽に解決できたのでメモしておく。

元となるデータの収集・抽出

離島経済新聞のサイトに日本の有人離島の一覧が載っているページがあるので、そちらを利用させてもらった。

ritokei.com

内容がコロコロ変わるような性質のものではないので、ソースをコピーしてローカルに保存→そこからデータを読み出すようにした。

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から出来るっぽい。

developers.google.com

最初はAPIキーの取得から。

cloud.google.com

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への出力完了')

スプレッドシートに上げておいたので、事あるごとに使うことにしよう。 f:id:kamonohashiy:20190508192112p:plain

感想とか

今までgeocoding APIの存在を知らなくて、本家Google Mapのサイトで得たい場所を検索→右クリックして座標をコピペをちまちまやっていたんだけど、それが一瞬で片付くようになったのは非常にありがたかった。

ただ位置情報関係でいうと、島ごとの港+そこと結ばれている本土の港の位置情報も集めなきゃいけないのがなかなかしんどいけど、今回書いたやつを一部修正して使い回すようにすればそこまで手間はかからないと思う。

これからもっと退屈なことはPythonにやらせよう

2019/05/09追記

何の考えもなしに1回数百回API叩きまくってたら、いつの間にか請求額が結構な値段になってた。 f:id:kamonohashiy:20190509201514p:plain

GCPは最近登録したばっかりで、今回の請求もクレジットの範囲内でまかなえるっぽいけど、ちーと気をつけんといかんなと思った次第。