今天下午在与某大神交流爬虫技术时,发现大神在提取数据时用到了一个我没见过的工具--JMESPath。
我心想提取响应正文中有效的信息无非就是使用正则(RE)、CSS选择器、Xpath表达式还能玩出花来?
他问我是如何解析Web Api的JSON的数据的?
我回答:1. 先转成字典;2. 再使用json['a'][1]['c'][2]方式或者用字典get方法。
然后他一脸嫌弃地看着我,说让我看看JMESPath的用法。
JMESPath -- JSON的Xpath
JMESPath是一种用于在JSON数据中进行查询的查询语言,它可以让你以一种简洁的方式从复杂的JSON结构中提取所需的数据。
XML结构数据有Xpath,JSON结构数据有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的含义:
- people: 这是查询的起点,表示我们希望从 data 数据中的 people 键对应的值开始查询。
- [*]: 这是通配符操作符,它告诉 JMESPath 查询在 people 数组中的所有元素上执行后续操作。
- .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表达式的含义:
- machines: 这是查询的起点,表示我们希望从 data 数据中的 machines 键对应的值开始查询。
- [?state == 'running']: 这是一个过滤器操作符,它用于筛选满足条件的元素。在这里,我们使用过滤器来选择 state 字段值等于 "running" 的元素。
最后
我觉得这简直就是神器啊!尤其在处理一些复杂情况下的条件取值!
但是我觉得这个还是有一定的学习成本,去学习其一些表达式的用法。
如果你已经掌握了Xpath,相信再学习使用JMESPath也会很轻松!
来源:麦叔编程