1. はじめに
書籍情報APIから返ってくるjsonデータが思った以上に扱いにくくて、個別にキーを指定して必要な情報を抽出するのは面倒だったので、キーを羅列したリストを用意して、for文で回せるようにしてみました。
2. ネストされた辞書の例
2.1. 書籍情報APIから返ってくるjsonデータ
例として以下のコードで返ってくるjsonデータを見てみますが、蔵書目録を作るという程度の目的であれば、必要なデータは一部で済むことになります。
ISBNを使ってAPIから書籍情報を抽出
import json
import urllib
import pprint
url = f'https://api.openbd.jp/v1/get?isbn=9784873117584'
urlopen = urllib.request.urlopen(url)
bookdata = json.loads(urlopen.read().decode('utf-8'))
pprint.pprint(bookdata)
openBDから返ってきたjsonデータ
[{'hanmoto': {'datecreated': '2016-08-31 16:02:14',
'datemodified': '2016-08-31 16:02:14'},
'onix': {'CollateralDetail': {'TextContent': [{'ContentAudience': '00',
'Text': 'ディープラーニングの本格的な入門書。外部のライブラリに頼らずPython '
'3によってゼロからディープラーニングを作成できる。',
'TextType': '02'},
{'ContentAudience': '00',
'Text': '実際にシステムを作りながらディープラーニングを学ぶ!\n'
'ディープラーニングの本格的な入門書。外部のライブラリに頼らずに、Python '
'3によってゼロからディープラーニングを作ることで、ディープラーニングの原理を楽しく学びます。ディープラーニングやニューラルネットワークの基礎だけでなく、誤差逆伝播法や畳み込みニューラルネットワークなども実装レベルで理解できます。ハイパーパラメータの決め方や重みの初期値といった実践的なテクニック、Batch '
'NormalizationやDropout、Adamといった最近のトレンド、自動運転や画像生成、強化学習などの応用例、さらには、なぜディープラーニングは優れているのか? '
'なぜ層を深くすると認識精度がよくなるのか? '
'といった“Why”に関する問題も取り上げます。',
'TextType': '03'}]},
'DescriptiveDetail': {'Audience': [{'AudienceCodeType': '22',
'AudienceCodeValue': '00'}],
'Contributor': [{'ContributorRole': ['A01'],
'PersonName': {'collationkey': 'サイトウ '
'コウキ',
'content': '斎藤 '
'康毅'},
'SequenceNumber': '1'}],
'Extent': [{'ExtentType': '11',
'ExtentUnit': '03',
'ExtentValue': '320'}],
'Language': [{'CountryCode': 'JP',
'LanguageCode': 'jpn',
'LanguageRole': '01'}],
'ProductComposition': '00',
'ProductForm': 'BA',
'ProductFormDetail': 'B108',
'Subject': [{'MainSubject': '',
'SubjectCode': '3055',
'SubjectSchemeIdentifier': '78'},
{'SubjectCode': '20',
'SubjectSchemeIdentifier': '79'}],
'TitleDetail': {'TitleElement': {'Subtitle': {'collationkey': 'パイソンデマナブディープラーニングノリロントジッソウ',
'content': 'Pythonで学ぶディープラーニングの理論と実装'},
'TitleElementLevel': '01',
'TitleText': {'collationkey': 'ゼロカラツクルディープラーニング',
'content': 'ゼロから作るDeep '
'Learning'}},
'TitleType': '01'}},
'NotificationType': '03',
'ProductIdentifier': {'IDValue': '9784873117584',
'ProductIDType': '15'},
'ProductSupply': {'SupplyDetail': {'Price': [{'CurrencyCode': 'JPY',
'PriceAmount': '3400',
'PriceType': '03'}],
'ProductAvailability': '99',
'ReturnsConditions': {'ReturnsCode': '03',
'ReturnsCodeType': '04'}}},
'PublishingDetail': {'Imprint': {'ImprintIdentifier': [{'IDValue': '87311',
'ImprintIDType': '19'},
{'IDValue': '0742',
'ImprintIDType': '24'}],
'ImprintName': 'オライリー・ジャパン'},
'Publisher': {'PublisherIdentifier': [{'IDValue': '274',
'PublisherIDType': '19'},
{'IDValue': '0742',
'PublisherIDType': '24'}],
'PublisherName': '株式会社オーム社',
'PublishingRole': '01'},
'PublishingDate': [{'Date': '20160924',
'PublishingDateRole': '01'}]},
'RecordReference': '9784873117584'},
'summary': {'author': '斎藤康毅/著',
'cover': '',
'isbn': '9784873117584',
'pubdate': '20160924',
'publisher': 'オライリー・ジャパン',
'series': '',
'title': 'ゼロから作るDeep Learning',
'volume': ''}}]
2.2. 書籍情報APIのjsonデータを扱う上での問題点
書籍情報APIのjsonデータの特徴として以下の点が挙げられます。
- ネストが深い
- dictとlistが混ざっている
- キーが常に存在するとは限らない
そして上記の理由から、以下のような問題が発生します。
- キーを事前に用意してfor文で回すことができない
- キーが存在しない場合に
try-except
で補足しないとKeyError
になる
そのため上記問題点を解消するために、辞書のキーと配列のインデックスをまとめたリストを用意しておいて、valueを取り出す関数を検討しました。
3. 作成した関数
def get_value_from_json(self, parent_data, key_list):
value = ''
for key in key_list:
if type(parent_data) is dict:
value = parent_data.get(key, '')
elif type(parent_data) is list and type(key) is int:
value = parent_data[key] if key < len(parent_data) else ''
else:
value = ''
if value == '':
break
parent_data = value
return value
出力例
get_value_from_json(bookdata, [])
#
get_value_from_json(bookdata, [0, 'onix', 'DescriptiveDetail', 'TitleDetail', 'TitleElement', 'TitleText', 'content'])
# 'ゼロから作るDeep Learning'
get_value_from_json(bookdata, [0, 'summary', 'author'])
# '斎藤康毅/著'
get_value_from_json(bookdata, ['null']) # ※存在しないキー
#
- 関数では最初に
result = None
として、第二引数のリストが空だった場合に''
を返します(Noneでもいいですが、処理によっては文字列のNone
が情報として入るので''
の方が使い勝手が良い気がしています)。 - for文でキーorインデックスのリストを1つずつ処理していきますが、
value
の型をdict
かlist
か判定して、キーかインデックスが有効な値であればvalue
をresult
に代入していき、最後に代入したvalue
を返します。 - キーorインデックスが有効でなければ
result
にはNone
を代入して、for文をbreak
して、None
を返します。
4. 活用例
この関数を用意しておくことで、以下のような形で書籍情報APIのjsonデータから情報を抽出できます。
keys = [[0, 'onix', 'DescriptiveDetail', 'TitleDetail', 'TitleElement', 'TitleText', 'content'],
[0, 'onix', 'DescriptiveDetail', 'TitleDetail', 'TitleElement', 'Subtitle', 'content'],
[0, 'summary', 'author'],
[0, 'summary', 'publisher'],
[0, 'onix', 'CollateralDetail', 'TextContent', 0, 'Text'],
[0, 'summary', 'pubdate'],
[0, 'summary', 'isbn']]
for key in keys:
print(get_value_from_json(bookdata, key))
出力
ゼロから作るDeep Learning
Pythonで学ぶディープラーニングの理論と実装
斎藤康毅/著
オライリー・ジャパン
ディープラーニングの本格的な入門書。外部のライブラリに頼らずPython 3によってゼロからディープラーニングを作成できる。
20160924
9784873117584