跳到内容

在 Python 中使用 JSONPath 解析 JSON 的快速介绍

JSON 已成为网络数据交换事实上的标准。 API、Web 服务和现代网站广泛使用 JSON 在服务器和客户端之间发送数据。

例如,根据BuiltWith的数据,排名前70的网站中有超过10,000%使用JSON API。 JSON 格式很容易用任何编程语言生成和解析。

然而,从大型 JSON 文档中有效提取有意义的信息仍然具有挑战性。这就是 JSONPath 的用武之地——一种专门的查询语言,用于简化定位和转换 JSON 数据的方式。

解析 JSON 的问题

传统上,JSON 数据在应用程序中的处理方式是将其完全解析为 Python 字典等本机数据结构。即使您只需要一小部分数据,您也可以解析整个 JSON 响应。

这种方法有一些缺点:

  • 性能慢 – 将大型 JSON 文件解析为对象的计算成本很高
  • 高内存使用 – 整个JSON结构需要保存在内存中
  • 详细代码 – 你经常需要编写大量循环/遍历代码来深入分析对象

另一种方法是使用正则表达式直接提取匹配的 JSON 片段。然而,正则表达式由于复杂的嵌套结构而变得混乱。它还与动态键名称或任意嵌套深度作斗争。

与原始解析或正则表达式匹配相比,JSONPath 提供了一种更清晰、更简洁的 JSON 查询方式。

JSONPath 简介

JSONPath 表达式描述如何访问 JSON 文档的各个部分。它在概念上类似于 XPath,允许查询 XML 中的元素和属性:

//node/child::*  - XPath for all child nodes
$.node.child     - Equivalent JSONPath 

JSONPath 方法的一些优点:

  • 可读性 – 查询表达式易于理解
  • 简短 – 不需要冗长的遍历代码
  • 高度灵活 – 支持查找、过滤、通配符匹配
  • 性能 – 非常优化的匹配算法
  • 可扩展性 – 甚至可以快速处理巨大的 JSON 文档

JSONPath 提供了一种简单、可扩展的替代方案,用于从 JSON 中提取数据。接下来我们来看看它是如何工作的。

使用 JSONPath 查询 JSON

JSONPath 表达式是一个字符串,描述如何在 JSON 结构中查找值。例如:

data = {
  "store": {
    "books": [
      { "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95
      },
      { "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99
      }
    ]
  }
}

# All book titles
books = jsonpath(data, ‘$.store.books[*].title‘) 

# Filter fiction books 
fiction_books = jsonpath(data, ‘$.store.books[?(@.category=="fiction")].title‘)

JSONPath 使用如下运算符:

  • . – 子操作员
  • [] – 用于数组访问的下标运算符
  • * – 所有匹配元素的通配符
  • ?() – 过滤谓词

将它们链接在一起可以有效地查询复杂的 JSON:

# Get all authors of books over 10 dollars 
authors = jsonpath(data, ‘$.store.books[?(@.price > 10)].author‘)

不再需要深层嵌套循环代码! JSONPath 直接匹配对象,无需完全解析整个 JSON 树。

可用的 JSONPath 运算符

以下是 JSONPath 中可用运算符的摘要:

路径运算符

  • $ – 根对象
  • @ – 当前对象
  • . or [] – 子操作员
  • .. – 递归搜索后代

过滤器运算符

  • [?(<expression>)]– 过滤对象
  • [(<condition>)]– 根据条件过滤

数组运算符

  • * – 通配符索引所有元素
  • [<index>] – 指数位置
  • [start:end] – 数组切片
  • [?(<condition>)] - 筛选

投影运算符

  • [] – 投影 – 提取列出的属性
  • [@] – 索引投影 – 展平数组

其他运营商

  • | – 联盟运营商
  • () – 优先运营商
  • , – 分隔多个结果

这些为您提供了广泛的灵活性,可以使用简单的路径字符串查询、过滤和转换 JSON 数据。

XML 的 JSONPath 与 XPath

由于 JSONPath 与 XPath 有许多相似之处,因此值得对两者进行比较:

XPath的

  • XML 的查询语言
  • 允许遍历 XML 树
  • 支持高级轴,例如 //, /*, //@
  • 用于提取 XML 节点

JSON路径

  • JSON 的等效查询语言
  • 受 XPath 启发的语法
  • 语法更简单,因为 JSON 在表示上比 XML 更简单
  • 快速实施,因为不需要 XML 解析

两者都允许在分层数据结构中选择节点。 JSONPath 可以被认为是 XPath 的简化版本,专门用于 JSON 而不是 XML。

JSONPath 已针对多种编程语言实现。一些流行的 Python 库是:

自学资料库课程描述
jsonpath-ng推荐的库,速度快,具有高级功能
jsonpath-rw合规参考实施
路径实现简单但功能有限

对于大多数用途, jsonpath-ng 提供合规性、功能和性能的最佳组合。

让我们更详细地了解如何使用它。

使用 jsonpath-ng 查询和过滤

首先,安装 jsonpath-ng:

pip install jsonpath-ng

导入:

from jsonpath_ng import jsonpath, parse

一些例子:

data = { "name": "John",
          "age": 30,
          "cars": [
            { "model": "BMW", "year": 2019 },
            { "model": "Tesla", "year": 2020 } 
          ]
        }

# Extract name
name = jsonpath(data, ‘$.name‘)

# Get first car 
first_car = jsonpath(data, ‘$.cars[0]‘)

# Filter Tesla cars
teslas = jsonpath(data, ‘$.cars[?(@.model=="Tesla")]‘) 

# Get all car years 
years = jsonpath(data, ‘$..cars[*].year‘)

您也可以使用 parse() 编译路径以获得更好性能的方法:

parser = parse(‘$.cars[*].year‘)

for obj in json_data:
   years = parser.find(obj)
   print(years)

当将相同路径应用于多个 JSON 文档时,这会更快。

过滤 JSON 数据

JSONPath 最强大的功能之一是它的过滤语法。

过滤器允许选择符合特定条件的对象。例如:

RecentCars = jsonpath(data, ‘$.cars[?(@.year > 2015)]‘)

这使得汽车比 2015 年更新。

您可以使用比较进行过滤,例如:

  • 数学: =, !=, >, <=等等。
  • 逻辑: and, or, not
  • 常用表达: =~, !=~
  • 存在: exists(), ?()

过滤器也可以组合:

ElectricCars = jsonpath(data, 
   ‘$.cars[?(@.year > 2010 && @.model =~ "Tesla|Volt")]`
)

这使得 2010 年之后生产电动汽车。

转换 JSON 数据

除了提取数据之外,JSONPath 还可以使用以下运算符转换 JSON 对象:

  • [] – 投影重塑物体
  • [@] – 数组索引扁平化

例如,将汽车数据扁平化为一个简单的列表:

all_models = jsonpath(data, ‘$..cars[*].model‘)
all_years = jsonpath(data, ‘$..cars[*].@year‘) 

@ 进行基于索引的投影。

链接过滤器、投影和切片允许以编程方式重构 JSON。

jsonpath-ng 的高级功能

jsonpath-ng 提供的一些附加高级功能:

自定义功能

您可以注册自定义函数来扩展 JSONPath:

def format_price(x):
  return f‘${x:,.2f}‘

jsonpath.register_custom_function(format_price, ‘format‘)

prices = jsonpath(data, ‘$.prices[*].format(@)‘) 

这允许直接在 JSONPath 表达式中实现复杂的数据转换。

缓存和优化

jsonpath-ng 编译和优化查询以提高性能。它还支持:

  • 缓存以提高速度
  • 惰性匹配以避免不必要的扫描
  • 产出良率优化

因此,即使针对大量 JSON 文档,它也能表现良好。

额外的操作员

其他一些有用的运算符:

  • ?() – 存在性检查
  • =~, !=~ – 正则表达式匹配
  • in – 包含支票
  • all – 通用量词

JSONPath 方法

辅助方法如:

  • find() – 返回匹配项
  • parse() – 编译路径

为常见查询提供更简单的 API。

使用 JSONPath 进行网页抓取

JSONPath 最有用的应用之一是在网页抓取时提取数据。

现代网站严重依赖 JSON 来传输数据:

  • APIs – JSON 是 REST API 的标准格式
  • 异步数据 – JSON 与 JavaScript 一起用于动态页面更新
  • 页面元数据 – 站点数据通常以 JSON 形式存储在脚本中

手动解析所有这些 JSON 会很麻烦。 JSONPath 允许轻松查询您需要的片段。

例如,以下是如何从电子商务页面提取产品数据:

import requests
from jsonpath_ng import jsonpath, parse

# Fetch product page
url = "http://www.example.com/product/123"  
response = requests.get(url)

# Extract JSON data 
data = response.json()

# Parse out product details 
name = jsonpath(data, ‘$.product.name‘)[0]
price = jsonpath(data, ‘$.product.price‘)[0] 
image = jsonpath(data, ‘$.product.images[0]‘)

print(name, price, image)

关键是使用 JSONPath 直接抓取需要的字段,而不是手动处理。

以下是一些常见用例:

  • API抓取 – 从 REST API 响应中提取数据
  • JavaScript 网站 – 前端使用的查询对象
  • 移动应用 – 从应用程序流量中解析 JSON 数据
  • 动态内容 – 从客户端 JavaScript 构建数据集

JSONPath 允许使用简单的路径字符串可扩展地抓取数千个 JSON 文档。

解析大型 JSON 文件

虽然 JSONPath 可以很好地扩展,但解析巨大的 JSON 文档仍然会带来挑战:

  • 内存使用情况 – 将完整的 JSON 加载到内存中
  • CPU负载 – 解析复杂的文档是处理器密集型的
  • 网络传输 – 大文档意味着更多带宽

处理大型 JSON 数据时的一些提示:

  • 使用流解析器避免完全加载 JSON
  • 编译路径为 parse() 而不是重新解析
  • 仅提取实际需要的字段而不是完整对象
  • 使用 laziness 以避免不必要的对象扫描
  • 处理 TB+ 规模数据时在强大的云服务器上运行
  • 跨集群分布式解析以进行并行处理

在大多数情况下,经过适当优化,JSONPath 甚至可以从包含数十万条记录的巨大 JSON 文件中高效地提取数据。

为什么我喜欢使用 JSONPath

作为一名广泛使用 JSON 数据、经验丰富的代理工程师,这就是我喜欢使用 JSONPath 的原因:

  • 简洁的语法 – 与传统解析代码相比,路径表达式非常简洁
  • 提高生产力 – 由于直观的语法,您可以像查询数据库一样轻松地查询 JSON
  • 强大的过滤 – 谓词过滤器使选择匹配数据变得轻而易举
  • 极快的性能 – jsonpath-ng 在底层使用极其优化的算法,即使在大型数据集上也能实现闪电般快速的数据提取
  • 内存高效 – 由于它选择性地解析 JSON,因此与完全解析本机对象相比,内存占用较低
  • 网页抓取能力 – 从 API 和 JavaScript 响应中轻松提取数据是 JSONPath 的亮点

虽然 jq 和 grep 等工具很棒,但我发现 JSONPath 对于我的大多数 JSON 解析需求来说更简单、更优雅。 Python 生态系统对 jsonpath-ng 等库的支持使其成为我对 JSON 数据进行切片和切块的首选。

其他语言的 JSONPath 支持

虽然我们专注于 Python,但 JSONPath 可用于多种编程语言:

由于 JSON 是一种通用数据格式,因此能够从任何语言有效地查询它是很有用的。值得庆幸的是 JSONPath 得到了广泛的支持。

为什么 JSONPath 很重要

JSON 已迅速成为 Web API、微服务和前端应用程序的必需品。 JSONPath 将类似 XPath 的查询功能带入 JSON 数据世界。

使用标准化路径语言来轻松提取嵌套 JSON 值有很多好处:

  • 简化跨平台和语言的 JSON 数据提取
  • 为丑陋的正则表达式解析提供可读的替代方案
  • 无需解析整个响应即可实现可扩展的网络抓取
  • 允许使用投影和过滤器进行复杂的转换
  • 解锁高效查询大型 JSON 数据集的能力
  • 与 jq 等其他 JSON 工具一起自然地融入管道中

随着 JSON 作为事实上的数据交换格式继续占据主导地位,拥有一组通用的 JSONPath 运算符将有助于降低导航大型 JSON 文档的复杂性。

结论

JSONPath 提供了一种通过简洁的路径表达式从 JSON 数据中提取和转换值的优雅方法。

图书馆喜欢 jsonpath-ng 使将 JSONPath 集成到您的 Python 项目中变得简单。

关键外包:

  • JSONPath 允许使用“.”轻松查询 JSON 结构和“[]”运算符
  • 使用谓词按属性值进行过滤可以很好地扩展
  • 可以使用投影和数组扩展来应用转换
  • JSONPath 避免了抓取时解析整个 JSON 对象的需要
  • 语法模仿用于查询 XML 的 XPath 表达式
  • 支持多种编程语言

对于使用基于 JSON 的 Web 服务,JSONPath 是任何开发人员工具包中不可或缺的工具。 JSONPath 可让您提出简单的问题并从复杂的 JSON 文档中获取所需的数据。

标签:

加入谈话

您的电邮地址不会被公开。 必填带 *