XML で結果返す、郵便番号検索API http://zip.cgis.biz/ は、古くからあるAPIであるが、
Python の xml 解析 xml.etree.ElementTree を学ぶのに丁度よい題材である。
API サービス、今や JSON の方が主流で、今更、XMLでレスポンスするものなど。。。
http://zipcloud.ibsnet.co.jp/doc/api
https://postcode-jp.com/ (有料)
これらを使うところが多いのではないだろうか。
とはいえ、xml.etree.ElementTree を学習する題材に、http://zip.cgis.biz/ に利用できます。
xml.etree.ElementTree を使う前に、どんなXMLを返すか?
# -*- coding: UTF-8 -*- import urllib.request import urllib.parse param = { 'zn': '1600023' } request = urllib.request.Request('http://zip.cgis.biz/xml/zip.php' + '?' + urllib.parse.urlencode(param)) with urllib.request.urlopen(request) as response: xml_string = response.read() print(xml_string.decode())
返ってくるXML
<?xml version="1.0" encoding="utf-8" ?> <ZIP_result> <result name="ZipSearchXML" /> <result version="1.01" /> <result request_url="http%3A%2F%2Fzip.cgis.biz%2Fxml%2Fzip.php%3Fzn%3D1600023" /> <result request_zip_num="1600023" /> <result request_zip_version="none" /> <result result_code="1" /> <result result_zip_num="1600023" /> <result result_zip_version="0" /> <result result_values_count="1" /> <ADDRESS_value> <value state_kana="トウキョウト" /> <value city_kana="シンジュクク" /> <value address_kana="ニシシンジュク(ツギノビルヲノゾク)" /> <value company_kana="none" /> <value state="東京都" /> <value city="新宿区" /> <value address="西新宿(次のビルを除く)" /> <value company="none" /> </ADDRESS_value> </ZIP_result>
xml.etree import ElementTree を使用して、
クラスとして、まとめます。
zipsearch.py
# -*- coding: UTF-8 -*- import urllib.request import urllib.parse from xml.etree import ElementTree as ET class ZipSearch: def __init__(self): self.url = 'http://zip.cgis.biz/xml/zip.php' self.rkeys = [ 'state', 'city', 'address', 'company' ] def search(self, zipcode=None): if zipcode==None: raise AttributeError('zipcode is None') result = {} param = { "zn": zipcode } request = urllib.request.Request(self.url + '?' + urllib.parse.urlencode(param)) with urllib.request.urlopen(request) as response: xml_string = response.read() root = ET.fromstring(xml_string) # 結果にエラー有れば、エラーコード、エラー内容を返す errorcodes = [ t.attrib['error_code'] for t in filter(lambda x: 'error_code' in x.attrib, root.iter('result'))] if any(errorcodes): errors = [t.attrib['error_note'] for t in filter(lambda x: 'error_note' in x.attrib, root.iter('result'))] result['error_code'] = errorcodes[0] result['error'] = errors[0] return result # resdict[0] → 検索結果数 resdict = [int(t.attrib['result_values_count']) for t in filter(lambda x: 'result_values_count' in x.attrib, root.iter('result'))] result['count'] = resdict[0] if result['count'] > 0: address = [] for addrvalue in root.iter('ADDRESS_value'): values = {} for item in addrvalue.iter('value'): for k, v in item.attrib.items(): values[k] = v addrString = "".join(list(filter(lambda x: x != 'none', [values[a] for a in self.rkeys]))) kanaString = " ".join(list(filter(lambda x: x != 'none', [values[a + "_kana"] for a in self.rkeys]))) address.append({ 'name': addrString, 'name_kana': kanaString }) result['address'] = address return result
呼出し実行
from zipsearch import ZipSearch zips = ZipSearch() res = zips.search('1600023') print(res) res = zips.search('4130302') print(res) res = zips.search('1111111') print(res) res = zips.search('160-0023') print(res)
結果
{'count': 1, 'address': [{'name': '東京都新宿区西新宿(次のビルを除く)', 'name_kana': 'トウキョウト シンジュクク ニシシンジュク(ツギノビルヲノゾク)'}]} {'count': 2, 'address': [{'name': '静岡県賀茂郡東伊豆町奈良本', 'name_kana': 'シズオカケン カモグンヒガシイズチョウ ナラモト'}, {'name': '静岡県賀茂郡東伊豆町北川', 'name_kana': 'シズオカケン カモグンヒガシイズチョウ ホツカワ'}]} {'count': 0} {'error_code': '3', 'error': '郵便番号パラメータ(zn)形式誤り'}