こんにちは。投資エンジニアの三年坊主(@SannenBouzu)です。
今回は、PythonでJSONデータをパース・読み込みしたい人の疑問に答えます。
PythonでJSONデータをパースしたい。必要なデータをPythonの変数に読み込んで次の処理に使いたい。
JSONデータは、ファイルの時とAPIレスポンスの時で何か扱い方は違うの?
この記事では、エンジニアの実務・大学の研究・趣味で、合わせて5年以上Pythonを使ってきた経験を生かして、Pythonで標準モジュールjsonを使ってJSONデータをパース・読み込みする方法を紹介します。
- PythonでJSONデータをパース・読み込みしたい方
- 実際にjsonモジュールを使ってみて出てきた疑問を解消したい方
Python標準ライブラリjsonでJSONデータをパースする基本操作
標準ライブラリjsonでJSONデータをパースする基本操作を、実際のサンプルコードとあわせて紹介します。
jsonライブラリをインポートした前提で読み進めてください。基本的にはGoogle Colaboratory上で実行して確認しており、コマンド実行時の「!」は除いています。
1 | import json |
json.loads():JSON文字列を辞書に変換
引数として与えた str を、Pythonのオブジェクトに変換(デコード)します。
Python3.6からはbytes, bytearrayも引数に与えられるようになりました。
1 2 3 4 5 6 7 8 | s1 = '["foo", {"bar":[1.0, null, "baz", 2]}]' d1 = json.loads(s1) print(d1) print(type(d1)) # 出力結果 ['foo', {'bar': [1.0, None, 'baz', 2]}] <class 'list'> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | s2 = '"\\"foo\\bar"' d2 = json.loads(s2) d2 # print() does not output ASCII code print(type(d2)) # 出力結果 '"foo\x08ar' <class 'str'> # raw文字列でバックスラッシュを無効化する s2_2 = r'"\"foo\bar"' d2_2 = json.loads(s2_2) d2_2 print(s2 == s2_2) # 出力結果 '"foo\x08ar' True |
s2_2を見ると分かりやすいですが、「\”」の部分が「”」に、「\b」の部分が「\x08」に対応しています。
略語 | 由来語句 | 意味 | 8進数コード | エスケープコード |
BS | Back Space | 1文字後退 | \x08 | \b |
参考:ASCIIコードの秘密
以下のように、int型やbool型のPythonオブジェクトとしてデコードすることもできます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | s3 = '35' d3 = json.loads(s3) print(d3) print(type(d3)) s4 = 'true' d4 = json.loads(s4) print(d4) print(type(d4)) # 出力結果 35 <class 'int'> True <class 'bool'> |
json.load():JSONファイルを辞書として読み込む
引数にファイルオブジェクトを与えて、JSONファイルから辞書を作成できます。
1 2 | with open('test.json') as f: dict_f = json.load(f) |
手元にファイルがなく、簡単に試したい時には、StringIOを使ってメモリ上でファイルのように扱えるストリームを用意できます。
1 2 3 4 5 6 7 8 9 10 11 | from io import StringIO file_content = '["streaming API"]' with StringIO(file_content) as io: l = json.load(io) print(l) print(type(l)) # 出力結果 ['streaming API'] <class 'list'> |
StringIOオブジェクト活用の一例として、「Pythonの単体テストで簡単なテキストファイルが必要な時に、実際にファイルを用意することなくテストを実行できる」ことが挙げられます。手軽に使えて便利です。
Python標準ライブラリjsonに関するFAQ
標準ライブラリjsonを実際に使うにあたって、気になる疑問点をまとめました。
①:Web APIでJSONデータを扱うには?パース・読み込みに差はない
JSONデータ自体の扱い方は、ファイルであってもAPIのレスポンスであっても大きく違いはありません。
以下の例では、requestsライブラリを使ってWeb APIから簡単にデータを取得しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | import requests params = {'after': '2018-12-01T00:00:00'} URL = 'https://basicincome30.com/wp-json/wp/v2/posts' response = requests.get(URL, params=params) print(response.status_code) if response.status_code == 200: # jsonモジュールを使用する例 data = response.text json_data = json.loads(data) # 実はjsonモジュールを使わなくても、以下の1行でJSONデータを得ることができる #json_data = response.json() # 各エントリのタイトルを一覧で表示 for entry in json_data: print(entry['title']['rendered']) # 出力結果 200 エンジニアのキャリアを好きな仕事で広げるたった3つの方法【会社員の実体験】 ブログで収益が伸びない3つの罠【Blog Hacks10記事添削に学ぶ】 【保存版】エンジニア面接で聞くべき逆質問13選【リアルな回答例あり】 レバテックキャリアの面談・面接は4種類!気になる疑問を徹底解消【体験談あり】 オンラインサロン「ブログアカデミー」に1か月参加した感想とブログ継続3つの秘訣 ふるさと納税で上限ギリギリまで寄付する方法と注意点4つ【会社員向け】 |
WP REST APIはWordPress4.7以降に含まれている公式のAPIではありますが、上記の例のようにWordPressサイトに関する様々なデータをオンラインで取得することができます。
セキュリティ上不安な方は、ログインしていないユーザからのアクセスを制限することも検討してください(プラグインもあるようです)。
②:順序を保ったまま読み込むには?object_pairs_hookにOrderedDictを指定
入力JSON文字列中でのオブジェクト順序を保ったまま読み込むために、object_pairs_hook引数にOrderedDictを指定できます。
Python3.7以前では、通常のdict型では順序が保たれる保証がないので、必ずJSON文字列で指定した順番を保持したい場合には有効な方法です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | from collections import OrderedDict json_s = '{"c": 10, "b": 20, "a": 30}' # 指定なし od1 = json.loads(json_s) print(od1) # 指定あり od2 = json.loads(json_s, object_pairs_hook=OrderedDict) print(od2) # 出力結果 {'c': 10, 'b': 20, 'a': 30} OrderedDict([('c', 10), ('b', 20), ('a', 30)]) |
③:ワンライナーでJSONデータを整形するには?json.toolかjqが便利
「python -m json.tool」(Python2.6以降)または「jq」を使ってJSONデータを整形することができます。
1 2 3 4 5 6 7 8 9 10 | echo '{"meta": {"code": 400, "error_type": "OAuthParameterException", "error_message": "Missing client_id or access_token URL parameter."}}' | python -m json.tool # 出力結果 { "meta": { "code": 400, "error_type": "OAuthParameterException", "error_message": "Missing client_id or access_token URL parameter." } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # jqをインストール sudo apt -y install jq # jq echo '{"meta": {"code": 400, "error_type": "OAuthParameterException", "error_message": "Missing client_id or access_token URL parameter."}}' | jq . # 出力結果 { "meta": { "code": 400, "error_type": "OAuthParameterException", "error_message": "Missing client_id or access_token URL parameter." } } # 要素を指定 echo '{"meta": {"code": 400, "error_type": "OAuthParameterException", "error_message": "Missing client_id or access_token URL parameter."}}' | jq '.meta.code' # 出力結果 400 |
④:重複した名前のレコードは?デフォルトでは最後のレコード以外は無視される
JSONオブジェクト中の名前はユニークである必要があります。
ですが、万が一重複した名前を含むJSON文字列をloads()でデコードすると、デフォルトでは最後のレコード以外は無視されます。
1 2 3 4 5 | weird_json = '{"x": 1, "x": 2, "x": 3}' json.loads(weird_json) # 出力結果 {'x': 3} |
PythonのjsonモジュールでJSONデータのパース・読み込みをして、快適なエンジニア生活を送りましょう。