2018/07/06

【Python】Yahoo!ローカルサーチAPIを使って施設名から各種情報を取得する

最近もっぱらGoogle Apps ScriptとSlackを連携して各種情報を垂れ流すようにしてみたり,ブックマークレットを書いてみたりとjavascript系のネタが多かったけど,久しぶりにPythonネタを.

Sponsored Link

店舗名をキーに所在地の緯度・経度が知りたい

……というのが最初の動機.

得た情報を使って何をしたかったのか,実際何を作ったのかはまた別の機会にするとして,まずとりあえず「店舗名」を投げて「緯度・経度」を知る仕組みづくりが必要だった.

この手のAPIといえばGoogleMapsAPI系なのだが,折しも無料枠がなくなるとかAPIが統合されるとか激動のさなかということであまり関わりたくなかった.あとで直すのも面倒だし.

そこで代替案を探した結果見つけたのがYahoo!ローカルサーチAPI.とりあえず24時間50000リクエストまでは使用可能ということで,趣味で使うには十分すぎる回数叩けるのが魅力.

出力形式をxmlにする際は名前空間に注意

出力はXMLまたはjsonで得ることができる.既定値はXML.

……というか,ちゃんと仕様を見ておらずjsonで出力できることに気づかずにXMLを使っていたのだが,その処理に割と苦労したのがこの記事を書く動機になっている.

というのも得られるXMLは名前空間付きのXMLなので,データを参照する際に名前空間を書かなければいけない,というお話.

XML版のコードはこんな感じで(アプリケーションIDは取得済みという前提である).

import requests
from xml.etree import ElementTree as ET
def getPosition(facility_name):
  url = 'https://map.yahooapis.jp/search/local/V1/localSearch?appid=[取得したID]&query='  + facility_name
  data = requests.get(url)
  root = ET.fromstring(data.content)
  #情報なしの時は'None'を返す
  if root.find('.//{http://olp.yahooapis.jp/ydf/1.0}Feature/{http://olp.yahooapis.jp/ydf/1.0}Geometry/{http://olp.yahooapis.jp/ydf/1.0}Coordinates') == None:
    return ['None']
  else:
    position_str = root.find('.//{http://olp.yahooapis.jp/ydf/1.0}Feature/{http://olp.yahooapis.jp/ydf/1.0}Geometry/{http://olp.yahooapis.jp/ydf/1.0}Coordinates').text
    #position_str = root[1][3][1].text でもよい
    position_str_list = position_str.split(',')
    position = [facility_name, float(position_str_list[1]), float(position_str_list[0])] #緯度経度の順番入れ替え
    return position #施設名,緯度,経度を返す

今後の使用を考えて「施設名,緯度,経度」をリストで返す関数になっている

ポイント1:XMLを扱うにはElementTree

この手のAPIはデフォルトでJSONを返すことが多いので最初は扱いに困った(この段階でJSONで出力させるオプションもあることに気づいていれば……).

そこで色々調べていたのだが,PythonでXMLを扱うならElementTreeを使うのが王道のようである.

ポイント2:名前空間を指定する

返ってくるXMLは名前空間付きのXMLである.その場合子要素を指定する時には名前空間も書いてやる必要があるらしい.ここ(Pythonで名前空間付きのXMLを操作する(ElementTree))を参考にしたのだが,XMLに疎すぎてそもそも「名前空間」という言葉を知らず適切な情報を得るのに苦労した.

ポイント3:子要素の取得

やり方は2つあって,find関数で指定してやる方法がまずひとつ.

その他に,もし返ってくるXMLの構造が確定しているのであればリストのようにindexで指定してやる必要もある.この場合は名前空間を無視できるので楽.

ポイント4:該当情報なしの切り分け

投げた施設名にヒットする情報がない場合,本来であれば緯度や経度が格納されている部分がNoneになっていることを利用してif文で切り分ける.

やっぱりJSONがナンバーワン

一方JSON版.

import requests
import json
def getPosition_json(facility_name):
  url =  'https://map.yahooapis.jp/search/local/V1/localSearch?appid=[取得したID]&output=json&query=' + facility_name
  r =  requests.get(url)
  data = r.json()
  #情報なしのときはNoneを返す
  if data['ResultInfo']['Count'] == 0:
    return ['None']
  else:
    position_str = data['Feature'][0]['Geometry']['Coordinates']
    position_str_list = position_str.split(',')
    position = [facility_name, float(position_str_list[1]), float(position_str_list[0])] #緯度経度の順番入れ替え
    return position

最初からJSONで書けばよかった.

以上.

ちなみに今回の例では一番目にヒットした情報を引き抜いている.複数ヒットした場合にどれが一番適切かは人間が判断するしかないかなあ……

0 件のコメント:

コメントを投稿