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。
适用于 Python 的流行 JSONPath 库
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 可用于多种编程语言:
- JavaScript的 – JSONPath Plus
- PHP – JSON路径
- Go – 格森
- PostgreSQL的 – JSON路径
- R – 路径
由于 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 文档中获取所需的数据。