このガイドでは、Pythonのjsonモジュールを使用してJSONデータを解析し、それをPythonの辞書に変換する方法、およびその逆の変換方法を解説します。
JavaScript Object Notation(JSON)は、APIを介してサーバーとWebアプリケーション間でデータを送受信する際に一般的に使用される、軽量なデータ交換フォーマットです。JSONデータはキーと値のペアで構成され、各キーは文字列であり、各値は文字列、数値、真偽値、null、配列、またはオブジェクトになり得ます。
以下はJSONの例です。
{
"name": "Maria Smith",
"age": 32,
"isMarried": true,
"hobbies": ["reading", "jogging"],
"address": {
"street": "123 Main St",
"city": "San Francisco",
"state": "CA",
"zip": "12345"
},
"phoneNumbers": [
{
"type": "home",
"number": "555-555-1234"
},
{
"type": "work",
"number": "555-555-5678"
}
],
"notes": null
}Pythonは、Python Standard Libraryの一部であるjsonモジュールを通じて、ネイティブにJSONをサポートしています。つまり、PythonでJSONを扱うために追加のライブラリをインストールする必要はありません。jsonは次のようにインポートできます。
import json組み込みのPython jsonライブラリは、JSONを扱うための完全なAPIを提供します。特に重要な関数として、loadsとloadの2つがあります。loads関数は文字列からJSONデータを解析するためのもので、load関数はJSONデータをbytesへ解析するためのものです。
これら2つのメソッドを通じて、jsonはJSONデータをdictionariesやlistsなどの同等のPythonオブジェクトに変換でき、またその逆も可能です。さらにjsonモジュールは、特定のデータ型を扱うためのカスタムエンコーダーおよびデコーダーを作成できます。
文字列に保存されたJSONデータがあり、それをPython辞書に変換したいとします。JSONデータは次のとおりです。
{
"name": "iPear 23",
"colors": ["black", "white", "red", "blue"],
"price": 999.99,
"inStock": true
}そして、Pythonにおける文字列表現は次のとおりです。
smartphone_json = '{"name": "iPear 23", "colors": ["black", "white", "red", "blue"], "price": 999.99, "inStock": true}'Note
長い複数行のJSON文字列を保存する場合は、Pythonのトリプルクォートの慣習の使用をご検討ください。
smartphoneに有効なPython文字列が入っていることは、次の行で確認できます。
print(type(smartphone))出力は次のとおりです。
<class 'str'>
strは「string」を意味し、smartphone変数がテキストシーケンス型であることを示します。
smartphoneに含まれるJSON文字列を、json.loads()メソッドでPython辞書に解析するには次のようにします。
import json
# JSON string
smartphone_json = '{"name": "iPear 23", "colors": ["black", "white", "red", "blue"], "price": 999.99, "inStock": true}'
# from JSON string to Python dict
smartphone_dict = json.loads(smartphone_json)
# verify the type of the resulting variable
print(type(smartphone_dict)) # dictこのスニペットを実行すると、次の結果になります。
<class 'dict'>
これでsmartphone_dictには有効なPython辞書が含まれます。
次に、有効なJSON文字列をjson.loads()に渡して、JSON文字列をPython辞書に変換します。
これで、生成された辞書のフィールドには通常どおりアクセスできます。
product = smartphone_dict['name'] # smartphone
priced = smartphone['price'] # 999.99
colors = smartphone['colors'] # ['black', 'white', 'red', 'blue']json.loads()関数は常に辞書を返すとは限りません。具体的には、戻り値のデータ型は入力文字列に依存します。たとえば、JSON文字列が単一の値(フラットな値)を含む場合、それは同等のPythonプリミティブ値に変換されます。
import json
json_string = '15.5'
float_var = json.loads(json_string)
print(type(float_var)) # <class 'float'>同様に、配列リストを含むJSON文字列はPythonのリストになります。
import json
json_string = '[1, 2, 3]'
list_var = json.loads(json_string)
print(json_string) # <class 'list'>以下の変換表は、JSONの値がjsonによってどのようにPythonデータへ変換されるかを説明しています。
| JSON Value | Python Data |
| - | - | - |
| string | str |
| number (integer) | int |
| number (real) | float |
| true | True |
| false | False |
| null | None |
| array | list |
| object | dict |
APIを呼び出し、そのJSONレスポンスをPython辞書に変換する必要があるとします。以下の例では、{JSON} Placeholderプロジェクトの次のAPIエンドポイントを呼び出して、ダミーのJSONデータを取得します。
https://jsonplaceholder.typicode.com/todos/1
このRESTFul APIは次のJSONレスポンスを返します。
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}Standard LibraryのurllibモジュールでこのAPIを呼び出し、得られたJSONを次のようにPython辞書に変換できます。
import urllib.request
import json
url = "https://jsonplaceholder.typicode.com/todos/1"
with urllib.request.urlopen(url) as response:
body_json = response.read()
body_dict = json.loads(body_json)
user_id = body_dict['userId'] # 1urllib.request.urlopen()はAPI呼び出しを実行し、HTTPResponseオブジェクトを返します。続いて、そのread()メソッドを使用してレスポンスボディbody_jsonを取得します。これにはAPIレスポンスがJSON文字列として含まれています。最後に、先ほど説明したとおり、その文字列をjson.loads()でPython辞書に解析できます。
同様に、requests:を使って同じ結果を得ることもできます。
import requests
import json
url = "https://jsonplaceholder.typicode.com/todos/1"
response = requests.get(url)
body_dict = response.json()
user_id = body_dict['userId'] # 1Note
.json()メソッドは、JSONデータを含むレスポンスオブジェクトを対応するPythonデータ構造へ自動的に変換します。
次のようなJSONデータがsmartphone.jsonファイルに保存されているとします。
{
"name": "iPear 23",
"colors": ["black", "white", "red", "blue"],
"price": 999.99,
"inStock": true,
"dimensions": {
"width": 2.82,
"height": 5.78,
"depth": 0.30
},
"features": [
"5G",
"HD display",
"Dual camera"
]
}目的はJSONファイルを読み込み、それをPython辞書にロードすることです。次のスニペットで実現できます。
import json
with open('smartphone.json') as file:
smartphone_dict = json.load(file)
print(type(smartphone_dict)) # <class 'dict'>
features = smartphone_dict['features'] # ['5G', 'HD display', 'Dual camera']組み込みのopen()ライブラリを使用すると、ファイルをロードして対応するfile objectを取得できます。続いてjson.read()メソッドが、JSONドキュメントを含むtext fileまたはbinary fileを、同等のPythonオブジェクトへデシリアライズします。この場合、smartphone.jsonはPython辞書になります。
次に、JSONデータをカスタムPythonクラスへ解析してみましょう。カスタムSmartphone Pythonクラスは次のとおりです。
class Smartphone:
def __init__(self, name, colors, price, in_stock):
self.name = name
self.colors = colors
self.price = price
self.in_stock = in_stockここでの目的は、次のJSON文字列をSmartphoneインスタンスに変換することです。
{
"name": "iPear 23 Plus",
"colors": ["black", "white", "gold"],
"price": 1299.99,
"inStock": false
}このタスクを達成するために、カスタムデコーダーを作成します。そのためには、JSONDecoderクラスを拡張し、__init__メソッドでobject_hookパラメータを設定します。これには、カスタム解析ロジックを含むクラスメソッド名を割り当てます。その解析メソッド内では、json.read()が返す標準辞書に含まれる値を使ってSmartphoneオブジェクトをインスタンス化できます。
以下のようにカスタムSmartphoneDecoderを定義します。
import json
class SmartphoneDecoder(json.JSONDecoder):
def __init__(self, object_hook=None, *args, **kwargs):
# set the custom object_hook method
super().__init__(object_hook=self.object_hook, *args, **kwargs)
# class method containing the
# custom parsing logic
def object_hook(self, json_dict):
new_smartphone = Smartphone(
json_dict.get('name'),
json_dict.get('colors'),
json_dict.get('price'),
json_dict.get('inStock'),
)
return new_smartphoneカスタムobject_hook()メソッド内で辞書の値を読むにはget()メソッドを使用します。これにより、辞書にキーが存在しない場合でもKeyErrorが発生しないようにできます。代わりにNoneが返されます。
次に、json.loads()のclsパラメータにSmartphoneDecoderクラスを渡して、JSON文字列をSmartphoneオブジェクトに変換します。
import json
# class Smartphone:
# ...
# class SmartphoneDecoder(json.JSONDecoder):
# ...
smartphone_json = '{"name": "iPear 23 Plus", "colors": ["black", "white", "gold"], "price": 1299.99, "inStock": false}'
smartphone = json.loads(smartphone_json, cls=SmartphoneDecoder)
print(type(smartphone)) # <class '__main__.Smartphone'>
name = smartphone.name # iPear 23 Plus同様に、json.load()でもSmartphoneDecoderを使用できます。
smartphone = json.load(smartphone_json_file, cls=SmartphoneDecoder)
逆方向に、Pythonのデータ構造やプリミティブ型をJSONに変換することも可能です。これはjson.dump()およびjson.dumps()関数により可能で、以下の変換表に従います。
| Python Data | JSON Value |
| - | - | - |
| str | string |
| int | number (integer) |
| float | number (real) |
| True | true |
| False | false |
| None | null |
| list | array |
| dict | object |
| Null | None |
json.dump()を使用すると、次の例のようにJSON文字列をファイルへ書き込めます。
import json
user_dict = {
"name": "John",
"surname": "Williams",
"age": 48,
"city": "New York"
}
# serializing the sample dictionary to a JSON file
with open("user.json", "w") as json_file:
json.dump(user_dict, json_file)このスニペットは、Pythonのuser_dict変数をuser.jsonファイルにシリアライズします。
同様に、json.dumps()はPython変数を同等のJSON文字列に変換します。
import json
user_dict = {
"name": "John",
"surname": "Williams",
"age": 48,
"city": "New York"
}
user_json_string = json.dumps(user_dict)
print(user_json_string)このスニペットを実行すると、次の結果が得られます。
{"name": "John", "surname": "Williams", "age": 48, "city": "New York"}Note
カスタムエンコーダーの指定方法については、公式ドキュメントに従ってください。
JSONのデータ解析には、見過ごせない課題があります。
代表的な例は次の2つです。
- Pythonの
jsonモジュールは、無効、破損、または非標準のJSONの場合に不十分になることがあります。 - 信頼できないソースからのJSONデータの解析は危険です。悪意のあるJSON文字列により、パーサーが壊れたり、大量のリソースを消費したりする可能性があります。
これらの制限は回避できますが、Web Scraper APIのようにJSON解析を容易にする商用ツールを使用するのが最適です。
Pythonでjson標準モジュールを通じてJSONデータをネイティブに解析する場合、Webサイトによって課される制限を回避するために、信頼性の高いプロキシサーバーが必要になります。Bright Dataのデータおよびプロキシ製品のような、最先端でフル機能の商用ソリューションによるデータ解析をお試しください。
