取引先がインボイスの登録事業者になっているかAPIを使って調べてみた(Python編)
こんにちは。aliceです。
今日は、昨日の取引先がインボイスの登録事業者になっているかAPIを使って調べてみた(GAS編)のPython編です。
または、こちらの続き?だったりそうでなかったり…。
フォルダの構成はこんな感じです。
invoice.pyと同じフォルダに「.env」ファイルと「法人番号API.xlsx」ファイルを入れています。
まずはExcel(法人番号API.xlsx)から確認してみましょう。
取引先シート一覧というシートがあります。
そこには「ID、法人名、郵便番号、住所」が書かれています。
よくある取引先一覧ですね。
そのデータを使って「ID、法人番号、法人名、インボイスの登録番号、インボイスの登録年月日」が入ったシートを作成します。
今回はあらかじめ、出力用にヘッダー行のついたシートを作っておきました。
取引先一覧シートのデータを使ってAPIをがちゃがちゃ使ってみました。
本当はもっとうまくできるんだろうなぁ…。
ということで、書いたのがこちら。
GASと比べるとだいぶ自信のない感じですね🥲
(GASも全然自信ないけど)
import requests
from xml.etree import ElementTree
from urllib.parse import urlencode
from dotenv import load_dotenv
from pathlib import Path
import os
import copy
import openpyxl
load_dotenv()
APPLICATION_ID = os.getenv('APPLICATION_ID')
base_path = Path(__file__).parent
file_path = base_path / '法人番号API.xlsx'
workbook = openpyxl.load_workbook(file_path)
def get_excel_list(wb):
"""
法人番号API.xlsxにある取引先一覧シートのデータからdictを含んだlistを作成する
:param wb: 法人番号API.xlsx
:return: 法人情報のリスト
"""
ws = wb['取引先一覧']
result_list = []
for row in ws['A2:D' + str(ws.max_row)]:
result_dict = {
'ID': row[0].value,
'法人名': row[1].value,
'郵便番号': row[2].value,
'住所': row[3].value
}
result_list.append(result_dict)
return result_list
def get_pref_code_list(corporation_list):
"""
Excelから取得した法人情報のリストに都道府県コードを追加したリストを作成する
郵便番号API http://zipcloud.ibsnet.co.jp/doc/api
:param corporation_list: 法人情報リスト
:return: 法人情報に都道府県コードを追加したリスト
"""
base_url = 'https://zipcloud.ibsnet.co.jp/api/search?'
result_list = []
for corporation_dict in corporation_list:
result_dict = copy.deepcopy(corporation_dict)
param_dict = {
'zipcode': result_dict['郵便番号'],
}
response = requests.get(base_url, params=param_dict)
result_dict['都道府県コード'] = response.json()['results'][0]['prefcode']
result_dict['市町村名'] = response.json()['results'][0]['address2']
result_list.append(result_dict)
return result_list
def get_city_code_list(corporation_list):
"""
法人情報のリストに市区町村コードを追加したリストを作成する
都道府県内市区町村一覧取得API https://www.land.mlit.go.jp/webland/api.html
:param corporation_list:法人情報リスト
:return:法人情報に市区町村コードを追加したリスト
"""
base_url = 'https://www.land.mlit.go.jp/webland/api/CitySearch?'
result_list = []
for corporation_dict in corporation_list:
result_dict = copy.deepcopy(corporation_dict)
param_dict = {'area': result_dict['都道府県コード']}
response = requests.get(base_url, params=param_dict)
city_list = change_list(response.json()['data'])
for city_dict in city_list:
if city_dict['name'] == result_dict['市町村名']:
result_dict['市区町村コード'] = city_dict['id']
result_list.append(result_dict)
return result_list
def change_list(city_list):
"""
get_city_code_listで取得したリストを変更する
県内に複数の区があるときは、市区町村コードを正しく取得できないので、政令指定都市のときは区を含む名称に変更する
(例 大阪市北区) 北区 ⇒ 大阪市北区
:param city_list:市区町村APIで取得したリスト
:return:市区町村名を変更したリスト
"""
city = ''
for city_dict in city_list:
if ('市' in city_dict['name']):
city = city_dict['name']
if ('区' in city_dict['name']):
city_dict['name'] = city + city_dict['name']
return city_list
def get_corporation_info(corporation_list, application_id):
"""
法人情報リストをもとに法人名と法人番号等が入ったリストを取得する
法人番号システムAPI https://www.houjin-bangou.nta.go.jp/webapi/
:param corporation_list:法人情報リスト
:param application_id: アプリケーションID
:return: ID,法人番号,法人名,住所が入ったリスト
"""
result_list = []
base_url = 'https://api.houjin-bangou.nta.go.jp/4/name?'
for corporation_dict in corporation_list:
param_dict = {
'id': application_id,
'type': 12,
'history': 0,
'name': corporation_dict['法人名'],
'address': corporation_dict['市区町村コード'],
}
response = requests.get(base_url, params=urlencode(param_dict))
root = ElementTree.fromstring(response.text)
for corporation in root.findall('corporation'):
result_dict = create_corporation_dict(corporation)
result_dict['ID'] = corporation_dict['ID']
result_list.append(result_dict)
return result_list
def create_corporation_dict(corporation):
"""
法人番号システムAPIのレスポンスから法人情報のdictを作成する
:param corporation: apiのレスポンス(xml)
:return: result_dict: 法人情報のdict
"""
key_list = ['法人番号', '法人名', '住所']
corporate_number = corporation.find('corporateNumber').text
name = corporation.find('name').text
address = corporation.find('prefectureName').text + corporation.find('cityName').text + corporation.find(
'streetNumber').text
value_list = [corporate_number, name, address]
result_dict = dict(zip(key_list, value_list))
return result_dict
def get_invoice_list(corporation_list, application_id):
"""
法人情報リストにインボイスの登録番号と登録年月日を追加する
適格請求書発行事業者公表システムAPI https://www.invoice-kohyo.nta.go.jp/web-api/index.html
:param corporation_list:法人情報リスト
:param application_id:アプリケーションID
:return:インボイスの登録番号・登録年月日が入ったリスト
"""
result_list = []
base_url = 'https://web-api.invoice-kohyo.nta.go.jp/1/num?'
for corporation_dict in corporation_list:
result_dict = copy.deepcopy(corporation_dict)
param_dict = {
'id': application_id,
'number': 'T' + result_dict['法人番号'],
'type': 21,
}
response = requests.get(base_url, params=param_dict)
if (int(response.json()['count']) > 0):
result_dict['登録番号'] = response.json()['announcement'][0]['registratedNumber']
result_dict['登録年月日'] = response.json()['announcement'][0]['registrationDate']
else:
result_dict['登録番号'] = ''
result_dict['登録年月日'] = ''
result_list.append(result_dict)
return result_list
def output_invoice_data(wb, corporation_list):
"""
invoice_listをExcelに書き出す
:param wb: 法人番号API.xlsx
:param corporation_list:インボイスの登録番号・登録年月日が入ったリスト
:return:
"""
ws = wb['取引先一覧(インボイス)']
idx = 2
for corporation_dict in corporation_list:
ws['A' + str(idx)].value = corporation_dict['ID']
ws['B' + str(idx)].value = corporation_dict['法人番号']
ws['C' + str(idx)].value = corporation_dict['法人名']
ws['D' + str(idx)].value = corporation_dict['住所']
ws['E' + str(idx)].value = corporation_dict['登録番号']
ws['F' + str(idx)].value = corporation_dict['登録年月日']
idx += 1
excel_list = get_excel_list(workbook)
pref_code_list = get_pref_code_list(excel_list)
city_code_list = get_city_code_list(pref_code_list)
corporation_number_list = get_corporation_info(city_code_list, APPLICATION_ID)
invoice_list = get_invoice_list(corporation_number_list, APPLICATION_ID)
output_invoice_data(workbook, invoice_list)
workbook.save(file_path)
workbook.close()
print('完了しました')
1 ライブラリなど
使ったライブラリはこちらです。(いっぱいある😭)
法人番号APIのアプリケーションIDを.envファイルに入れてみました。
.envファイルを使うので今回はpython-dotenvをインストールしました。
そして、.envファイルの作り方がわからなかったのでテキストエディタから作成。
テキストエディタを開いて次のとおり入力します。
※アプリケーションIDのところに実際のIDを入れてください。
APPLICATION_ID :'アプリケーションID'
テキストエディタの名前を「.env」に変更してpyファイルと同じフォルダに入れます。
2 Excelからリストを作成
Excelの取引先一覧シートからリストを作成します。
こちらもpyファイルと同じフォルダにある法人番号API.xlsxからデータを取得します。
openpyxlライブラリを使ってデータを取得しました。
dictも慣れてくると便利ですね🙂
[{'ID': 1, '住所': '東京都港区台場2-3-3', '法人名': 'サントリー株式会社', '郵便番号': 1350091},
{'ID': 2, '住所': '大阪市北区堂島浜2-1-40', '法人名': 'サントリー株式会社', '郵便番号': 5300004},
{'ID': 3, '住所': '那覇市字安謝628', '法人名': '沖縄サントリー株式会社', '郵便番号': 9000003}]
3 郵便番号API
郵便番号APIを使って都道府県コード・市町村名を取得します。
requestモジュールを使ってリクエストを送ります。
郵便番号API
[{'ID': 1,
'住所': '東京都港区台場2-3-3',
'市町村名': '港区',
'法人名': 'サントリー株式会社',
'郵便番号': 1350091,
'都道府県コード': '13'},
{'ID': 2,
'住所': '大阪市北区堂島浜2-1-40',
'市町村名': '大阪市北区',
'法人名': 'サントリー株式会社',
'郵便番号': 5300004,
'都道府県コード': '27'},
{'ID': 3,
'住所': '那覇市字安謝628',
'市町村名': '那覇市',
'法人名': '沖縄サントリー株式会社',
'郵便番号': 9000003,
'都道府県コード': '47'}]
4 都道府県市区町村一覧取得API
郵便番号APIで取得した都道府県コード、市町村名をもとに都道府県市区町村一覧取得APIを使って市区町村コードを取得します。
https://www.land.mlit.go.jp/webland/api.html
[{'ID': 1,
'住所': '東京都港区台場2-3-3',
'市区町村コード': '13103',
'市町村名': '港区',
'法人名': 'サントリー株式会社',
'郵便番号': 1350091,
'都道府県コード': '13'},
{'ID': 2,
'住所': '大阪市北区堂島浜2-1-40',
'市区町村コード': '27127',
'市町村名': '大阪市北区',
'法人名': 'サントリー株式会社',
'郵便番号': 5300004,
'都道府県コード': '27'},
{'ID': 3,
'住所': '那覇市字安謝628',
'市区町村コード': '47201',
'市町村名': '那覇市',
'法人名': '沖縄サントリー株式会社',
'郵便番号': 9000003,
'都道府県コード': '47'}]
大阪府の場合だと、大阪市北区と堺市北区があってどちらの北区かわからないので、区名の前に市名を入れました。
【before🥲】
[{'id': '27100', 'name': '大阪市'}, {'id': '27102', 'name': '都島区'}, {'id': '27103', 'name': '福島区'}, {'id': '27104', 'name': '此花区'}, {'id': '27106', 'name': '西区'}, {'id': '27107', 'name': '港区'}, {'id': '27108', 'name': '大正区'}, {'id': '27109', 'name': '天王寺区'}, {'id': '27111', 'name': '浪速区'}, {'id': '27113', 'name': '西淀川区'}, {'id': '27114', 'name': '東淀川区'}, {'id': '27115', 'name': '東成区'}, {'id': '27116', 'name': '生野区'}, {'id': '27117', 'name': '旭区'}, {'id': '27118', 'name': '城東区'}, {'id': '27119', 'name': '阿倍野区'}, {'id': '27120', 'name': '住吉区'}, {'id': '27121', 'name': '東住吉区'}, {'id': '27122', 'name': '西成区'}, {'id': '27123', 'name': '淀川区'}, {'id': '27124', 'name': '鶴見区'}, {'id': '27125', 'name': '住之江区'}, {'id': '27126', 'name': '平野区'}, {'id': '27127', 'name': '北区'}, {'id': '27128', 'name': '中央区'}, {'id': '27140', 'name': '堺市'}, {'id': '27141', 'name': '堺区'}, {'id': '27142', 'name': '中区'}, {'id': '27143', 'name': '東区'}, {'id': '27144', 'name': '西区'}, {'id': '27145', 'name': '南区'}, {'id': '27146', 'name': '北区'}, {'id': '27147', 'name': '美原区'}, {'id': '27202', 'name': '岸和田市'}, {'id': '27203', 'name': '豊中市'}, {'id': '27204', 'name': '池田市'}, {'id': '27205', 'name': '吹田市'}, {'id': '27206', 'name': '泉大津市'}, {'id': '27207', 'name': '高槻市'}, {'id': '27208', 'name': '貝塚市'}, {'id': '27209', 'name': '守口市'}, {'id': '27210', 'name': '枚方市'}, {'id': '27211', 'name': '茨木市'}, {'id': '27212', 'name': '八尾市'}, {'id': '27213', 'name': '泉佐野市'}, {'id': '27214', 'name': '富田林市'}, {'id': '27215', 'name': '寝屋川市'}, {'id': '27216', 'name': '河内長野市'}, {'id': '27217', 'name': '松原市'}, {'id': '27218', 'name': '大東市'}, {'id': '27219', 'name': '和泉市'}, {'id': '27220', 'name': '箕面市'}, {'id': '27221', 'name': '柏原市'}, {'id': '27222', 'name': '羽曳野市'}, {'id': '27223', 'name': '門真市'}, {'id': '27224', 'name': '摂津市'}, {'id': '27225', 'name': '高石市'}, {'id': '27226', 'name': '藤井寺市'}, {'id': '27227', 'name': '東大阪市'}, {'id': '27228', 'name': '泉南市'}, {'id': '27229', 'name': '四條畷市'}, {'id': '27230', 'name': '交野市'}, {'id': '27231', 'name': '大阪狭山市'}, {'id': '27232', 'name': '阪南市'}, {'id': '27301', 'name': '島本町'}, {'id': '27321', 'name': '豊能町'}, {'id': '27322', 'name': '能勢町'}, {'id': '27341', 'name': '忠岡町'}, {'id': '27361', 'name': '熊取町'}, {'id': '27362', 'name': '田尻町'}, {'id': '27366', 'name': '岬町'}, {'id': '27381', 'name': '太子町'}, {'id': '27382', 'name': '河南町'}, {'id': '27383', 'name': '千早赤阪村'}]
【after🙂】
[{'id': '27100', 'name': '大阪市'}, {'id': '27102', 'name': '大阪市都島区'}, {'id': '27103', 'name': '大阪市福島区'}, {'id': '27104', 'name': '大阪市此花区'}, {'id': '27106', 'name': '大阪市西区'}, {'id': '27107', 'name': '大阪市港区'}, {'id': '27108', 'name': '大阪市大正区'}, {'id': '27109', 'name': '大阪市天王寺区'}, {'id': '27111', 'name': '大阪市浪速区'}, {'id': '27113', 'name': '大阪市西淀川区'}, {'id': '27114', 'name': '大阪市東淀川区'}, {'id': '27115', 'name': '大阪市東成区'}, {'id': '27116', 'name': '大阪市生野区'}, {'id': '27117', 'name': '大阪市旭区'}, {'id': '27118', 'name': '大阪市城東区'}, {'id': '27119', 'name': '大阪市阿倍野区'}, {'id': '27120', 'name': '大阪市住吉区'}, {'id': '27121', 'name': '大阪市東住吉区'}, {'id': '27122', 'name': '大阪市西成区'}, {'id': '27123', 'name': '大阪市淀川区'}, {'id': '27124', 'name': '大阪市鶴見区'}, {'id': '27125', 'name': '大阪市住之江区'}, {'id': '27126', 'name': '大阪市平野区'}, {'id': '27127', 'name': '大阪市北区'}, {'id': '27128', 'name': '大阪市中央区'}, {'id': '27140', 'name': '堺市'}, {'id': '27141', 'name': '堺市堺区'}, {'id': '27142', 'name': '堺市中区'}, {'id': '27143', 'name': '堺市東区'}, {'id': '27144', 'name': '堺市西区'}, {'id': '27145', 'name': '堺市南区'}, {'id': '27146', 'name': '堺市北区'}, {'id': '27147', 'name': '堺市美原区'}, {'id': '27202', 'name': '岸和田市'}, {'id': '27203', 'name': '豊中市'}, {'id': '27204', 'name': '池田市'}, {'id': '27205', 'name': '吹田市'}, {'id': '27206', 'name': '泉大津市'}, {'id': '27207', 'name': '高槻市'}, {'id': '27208', 'name': '貝塚市'}, {'id': '27209', 'name': '守口市'}, {'id': '27210', 'name': '枚方市'}, {'id': '27211', 'name': '茨木市'}, {'id': '27212', 'name': '八尾市'}, {'id': '27213', 'name': '泉佐野市'}, {'id': '27214', 'name': '富田林市'}, {'id': '27215', 'name': '寝屋川市'}, {'id': '27216', 'name': '河内長野市'}, {'id': '27217', 'name': '松原市'}, {'id': '27218', 'name': '大東市'}, {'id': '27219', 'name': '和泉市'}, {'id': '27220', 'name': '箕面市'}, {'id': '27221', 'name': '柏原市'}, {'id': '27222', 'name': '羽曳野市'}, {'id': '27223', 'name': '門真市'}, {'id': '27224', 'name': '摂津市'}, {'id': '27225', 'name': '高石市'}, {'id': '27226', 'name': '藤井寺市'}, {'id': '27227', 'name': '東大阪市'}, {'id': '27228', 'name': '泉南市'}, {'id': '27229', 'name': '四條畷市'}, {'id': '27230', 'name': '交野市'}, {'id': '27231', 'name': '大阪狭山市'}, {'id': '27232', 'name': '阪南市'}, {'id': '27301', 'name': '島本町'}, {'id': '27321', 'name': '豊能町'}, {'id': '27322', 'name': '能勢町'}, {'id': '27341', 'name': '忠岡町'}, {'id': '27361', 'name': '熊取町'}, {'id': '27362', 'name': '田尻町'}, {'id': '27366', 'name': '岬町'}, {'id': '27381', 'name': '太子町'}, {'id': '27382', 'name': '河南町'}, {'id': '27383', 'name': '千早赤阪村'}]
5 法人番号API
法人番号APIを使って法人番号、法人名、住所を取得します。
市区町村で絞って抽出しても、同じ法人名ってあるんですね。
レスポンスがJSONだったらなぁ…。
JSONの方が好きなんだよなぁ…。
JSONをごりごりするのが楽しい。
[{'ID': 1, '住所': '東京都港区台場2丁目3番3号', '法人名': 'サントリー株式会社', '法人番号': '4010401081444'},
{'ID': 1, '住所': '東京都港区芝浦3丁目1番1号', '法人名': 'サントリー株式会社', '法人番号': '9010001151337'},
{'ID': 2,'住所': '大阪府大阪市北区堂島浜2丁目1番40号','法人名': 'サントリー株式会社','法人番号': '1120001133100'},
{'ID': 3,'住所': '沖縄県那覇市字安謝628番地','法人名': '沖縄サントリー株式会社','法人番号': '9360001000343'}]
6 適格請求書発行事業者公表システムAPI
適格請求書発行事業者公表システムAPIを使って登録番号、登録年月日を取得します。
登録している法人だけ追記します。
登録していない法人は登録番号と登録年月日を空欄?で辞書を追加しないとExcelに書き出すときにエラーになりました。
CSVのときは大丈夫だったんだけどな。(コードの問題か)
[{'ID': 1,
'住所': '東京都港区台場2丁目3番3号',
'法人名': 'サントリー株式会社',
'法人番号': '4010401081444',
'登録年月日': '2023-10-01',
'登録番号': 'T4010401081444'},
{'ID': 1,
'住所': '東京都港区芝浦3丁目1番1号',
'法人名': 'サントリー株式会社',
'法人番号': '9010001151337',
'登録年月日': '',
'登録番号': ''},
{'ID': 2,
'住所': '大阪府大阪市北区堂島浜2丁目1番40号',
'法人名': 'サントリー株式会社',
'法人番号': '1120001133100',
'登録年月日': '',
'登録番号': ''},
{'ID': 3,
'住所': '沖縄県那覇市字安謝628番地',
'法人名': '沖縄サントリー株式会社',
'法人番号': '9360001000343',
'登録年月日': '2023-10-01',
'登録番号': 'T9360001000343'}]
7 Excelに書き出す
ここまでで取得したデータを取引先一覧(インボイス)シートに書き出します。
8 Excelを保存する
書き出しが完了したらExcelを保存して終了します。
お疲れさまでした。
次はリクエストのエラー処理をいれたいです。