PythonでJSONPathを使用したデータ抽出の実践ガイド

概要

JSONPathは、JSONデータから特定の値を効率的に抽出するためのクエリ言語です。Pythonでは、主にjsonpath-ngjsonpath-rwという2つのライブラリが広く利用されています。

jsonpath-ngの特徴

  • ルートノード、子ノード、ワイルドカード、配列インデックスなどの基本構文をサポート
  • フィルタ内での正規表現やスクリプト式には非対応
  • 複雑な論理演算は制限あり

jsonpath-rwの特徴

  • 正規表現やスクリプト式を含む幅広いJSONPath構文をサポート
  • 複雑な論理演算やネストされたフィルタ条件に対応
  • 再帰下降演算子..をサポート

インストール方法

各ライブラリはpipでインストール可能です。

# jsonpath-ngの場合
pip install jsonpath-ng

# jsonpath-rwの場合
pip install jsonpath-rw

基本的な使用例

以下のサンプルデータを使用して、各種抽出パターンを解説します。

from jsonpath_ng import parse

# サンプルデータの定義
shop_data = {
    "shop": {
        "books": [
            {"genre": "novel", "name": "吾輩は猫である", "writer": "夏目漱石", "cost": 1200},
            {"genre": "essay", "name": "坊っちゃん", "writer": "夏目漱石", "cost": 980},
            {"genre": "novel", "name": "雪国", "writer": "川端康成", "cost": 1500}
        ],
        "bike": {"shade": "blue", "cost": 25000}
    }
}

全書籍のタイトルを取得

expr = parse('$.shop.books[*].name')
titles = [m.value for m in expr.find(shop_data)]
print(titles)
# 出力: ['吾輩は猫である', '坊っちゃん', '雪国']

全書籍の価格を取得

expr = parse('$.shop.books[*].cost')
prices = [m.value for m in expr.find(shop_data)]
print(prices)
# 出力: [1200, 980, 1500]

特定ジャンルの書籍をフィルタリング

expr = parse('$.shop.books[?(@.genre == "novel")]')
novels = [m.value for m in expr.find(shop_data)]
print(novels)

自転車の色を取得

expr = parse('$.shop.bike.shade')
bike_color = expr.find(shop_data)[0].value
print(bike_color)
# 出力: 'blue'

ネスト構造の処理

より深い階層を持つデータ構造に対するクエリ例です。

from jsonpath_ng import parse

catalog = {
    "vendors": [
        {
            "vendor_name": "東京書店",
            "products": [
                {"kind": "book", "item_name": "こころ", "cost": 850},
                {"kind": "dvd", "item_name": "映画セット", "cost": 3000}
            ]
        },
        {
            "vendor_name": "大阪ライブラリ",
            "products": [
                {"kind": "book", "item_name": "人間失格", "cost": 720},
                {"kind": "cd", "item_name": "クラシック集", "cost": 1800}
            ]
        }
    ]
}

# 全業者名を抽出
expr = parse('$.vendors[*].vendor_name')
vendors = [m.value for m in expr.find(catalog)]
print(vendors)
# 出力: ['東京書店', '大阪ライブラリ']

高度なクエリテクニック

条件付きフィルタリング

価格が1000円以上の書籍を抽出します。

expr = parse('$.shop.books[?(@.cost >= 1000)]')
expensive_books = [m.value for m in expr.find(shop_data)]
print(expensive_books)

配列インデックスによるアクセス

# 最初の業者の最初の商品を取得
expr = parse('$.vendors[0].products[0]')
first_product = expr.find(catalog)[0].value
print(first_product)
# 出力: {'kind': 'book', 'item_name': 'こころ', 'cost': 850}

複合条件クエリ

# ジャンルが小説で、価格が1000円以上の書籍のタイトルと価格を取得
expr = parse('$.shop.books[?(@.genre == "novel" && @.cost >= 1000)]')
filtered = [m.value for m in expr.find(shop_data)]
print(filtered)

例外処理の実装

クエリ実行時のエラーを適切にハンドリングします。

from jsonpath_ng import parse
from jsonpath_ng.exceptions import JsonPathParserError

def safe_query(data, path_str):
    """安全にJSONPathクエリを実行するヘルパー関数"""
    try:
        expression = parse(path_str)
        return [match.value for match in expression.find(data)]
    except JsonPathParserError as e:
        print(f"パースエラー: {e}")
        return []
    except Exception as e:
        print(f"予期しないエラー: {e}")
        return []

# 使用例
result = safe_query(shop_data, '$.shop.books[*].name')
print(result)

主なJSONPath構文一覧

構文説明
$ルートノード$
.子ノード$.shop.name
[n]配列インデックス$.items[0]
[*]全配列要素$.items[*]
..再帰的下降$..name
?()フィルタ式$[?(@.price > 100)]
*ワイルドカード$.store.*

タグ: Python jsonpath jsonpath-ng jsonpath-rw JSON

8月26日 23:17 投稿