专业的编程技术博客社区

网站首页 > 博客文章 正文

惊讶!竟然可以像xpath一样索引json里的值

baijin 2024-10-17 07:50:06 博客文章 3 ℃ 0 评论

今天下午在与某大神交流爬虫技术时,发现大神在提取数据时用到了一个我没见过的工具--JMESPath

我心想提取响应正文中有效的信息无非就是使用正则(RE)CSS选择器Xpath表达式还能玩出花来?

他问我是如何解析Web ApiJSON的数据的?

我回答:1. 先转成字典;2. 再使用json['a'][1]['c'][2]方式或者用字典get方法。

然后他一脸嫌弃地看着我,说让我看看JMESPath的用法。

JMESPath -- JSON的Xpath

JMESPath是一种用于在JSON数据中进行查询的查询语言,它可以让你以一种简洁的方式从复杂的JSON结构中提取所需的数据。

XML结构数据有XpathJSON结构数据有JMESPath

JMESPath的入门用法

取键"a"取对应值:

data = {"a": "foo", "b": "bar", "c": "baz"}

普通方式:

data = {"a": "foo", "b": "bar", "c": "baz"}

result = data.get("a")
print(result) # 结果:foo

JMESPath方式:

import jmespath

data = {"a": "foo", "b": "bar", "c": "baz"}

query = "a"
result = jmespath.search(query, data)
print(result) # 结果:foo

再复杂点!

取嵌套字典中"d"对应的值:

data = {"a": {"b": {"c": {"d": "value"}}}}

普通方式:

data = {"a": {"b": {"c": {"d": "value"}}}}

result = data.get("a").get("b").get("c").get("d")
print(result) # 结果:value

一串get相连。

JMESPath方式:

import jmespath
data = {"a": {"b": {"c": {"d": "value"}}}}

query = "a.b.c.d"
result = jmespath.search(query, data)
print(result) 

像JavaScript那样的方式取得第三层的"d"值。

再猛一点:

取嵌套字典中第一个"c"中第一个字典中的"d"中第二个列表元素的第一个值:

目标是取得[0, [1, 2]]中的1

data = {"a": {
  "b": {
    "c": [
      {"d": [0, [1, 2]]},
      {"d": [3, 4]}
    ]
  }
}}

普通方式:

data = {"a": {
  "b": {
    "c": [
      {"d": [0, [1, 2]]},
      {"d": [3, 4]}
    ]
  }
}}

result = data.get("a").get("b").get("c")[0].get("d")[1][0]
print(result) # 结果:1

JMESPath方式:

import jmespath

data = {"a": {
  "b": {
    "c": [
      {"d": [0, [1, 2]]},
      {"d": [3, 4]}
    ]
  }
}}

# result = data.get("a").get("b").get("c")[0].get("d")[1][0]
# print(result) # 结果:1

query = "a.b.c[0].d[1][0]"
result = jmespath.search(query, data)
print(result) # 结果:1

JMESPath的高阶用法

如果仅仅只能支持到索引的话,我觉得还挺一般的,毕竟xpath还支持条件定位目标元素。但JMESPath也是支持的!

获取JSON中所有的用户的名

data = {
  "people": [
    {"first": "James", "last": "d"},
    {"first": "Jacob", "last": "e"},
    {"first": "Jayden", "last": "f"},
    {"missing": "different"}
  ],
  "foo": {"bar": "baz"}
}

目标是获取:"James","Jacob","Jayden"。

见证奇迹的时候到了!

import jmespath

data = {
  "people": [
    {"first": "James", "last": "d"},
    {"first": "Jacob", "last": "e"},
    {"first": "Jayden", "last": "f"},
    {"missing": "different"}
  ],
  "foo": {"bar": "baz"}
}

query = "people[*].first"
result = jmespath.search(query, data)
print(result) # ['James', 'Jacob', 'Jayden']

解析下people[*].first的含义:

  1. people: 这是查询的起点,表示我们希望从 data 数据中的 people 键对应的值开始查询。
  2. [*]: 这是通配符操作符,它告诉 JMESPath 查询在 people 数组中的所有元素上执行后续操作。
  3. .first: 这是字段投影操作符,它用于从每个 people 数组中的元素中提取 first 字段的值。

再来个烧脑的!

获取当前状态是runing的机器名

data = {
  "machines": [
    {"name": "a", "state": "running"},
    {"name": "b", "state": "stopped"},
    {"name": "c", "state": "running"}
  ]
} 

肉眼迅速识别出是"a"和"c"。

普通方式:


data = {
  "machines": [
    {"name": "a", "state": "running"},
    {"name": "b", "state": "stopped"},
    {"name": "c", "state": "running"}
  ]
}

result = [machine["name"] for machine in data["machines"] if machine["state"] == "running"]
print(result) # 结果:['a', 'c']

这个三元表达式+列表推导式是不是有点绕?

JMESPath方式:

import jmespath

data = {
  "machines": [
    {"name": "a", "state": "running"},
    {"name": "b", "state": "stopped"},
    {"name": "c", "state": "running"}
  ]
}
query = "machines[?state=='running'].name"
result = jmespath.search(query, data)
print(result) # 结果:['a', 'c']

machines[?state == 'running'].name表达式的含义:

  1. machines: 这是查询的起点,表示我们希望从 data 数据中的 machines 键对应的值开始查询。
  2. [?state == 'running']: 这是一个过滤器操作符,它用于筛选满足条件的元素。在这里,我们使用过滤器来选择 state 字段值等于 "running" 的元素。

最后

我觉得这简直就是神器啊!尤其在处理一些复杂情况下的条件取值!

但是我觉得这个还是有一定的学习成本,去学习其一些表达式的用法。

如果你已经掌握了Xpath,相信再学习使用JMESPath也会很轻松!

来源:麦叔编程

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表