[原创] 调研 python json 提取工具

为便捷配置信息提取规则, 调研 json 格式的数据提取方案.

jsonselect

css selector 实现

$ pip install jsonselect 
>>> import jsonselect as j
>>> 
>>> data = {'name':'zz', 'books':[{'name':'x','price':1}, {'name':'y', 'price':2,'tag':'t'}]}

# 默认情况下和 css selector 一样, 根据标签进行递归的, 不区分是不是根节点的子节点
>>> j.select('.name', data)
['x', 'y', 'zz']
>>> j.select(':root>.name', data)
zz
>>> j.select('.books .tag', data)
't'
# 官方提供的示例 `[{}, {}]` 可以根据 value 过滤, 但没有实现从 `{key: [{}, {}]}` 结构中过滤成功(外层再加上:has 之后就没有数据了)
>>> j.select('.books .tag:val("t")', data)
't'
>>> j.select('object:last-child', data)
{'price': 2, 'tag': 't', 'name': 'y'}
# 虽然不能根据 value 进行过滤, 但是可以根据 key 是否存在进行过滤
>>> j.select(''.books>object:has(string.tag)', data)
{'price': 2, 'tag': 't', 'name': 'y'}
>>> j.select('.books object:has(.tag)', data)
{'price': 2, 'tag': 't', 'name': 'y'}

特别注意, 选择器 key 是 css 中的 class, 数据类型是 css 中的 tag.
{} -> object, [] -> array. {name: xyz}, -> .name || object

缺点: 不能根据 value 过滤. 实现[key=value]就基本完美.

jmespath

xpath 实现, 功能十分强大

$ pip install jmespath

>>> jmespath.search('name', data)
'zz'
>>> jmespath.search('books[].tag', data)
['t']
# 根据 value 过滤, 但 value 需要加 ``
>>> jmespath.search('books[?tag==`t`]', data)
[{'price': 2, 'tag': 't', 'name': 'y'}]
# ``中不区分类型
>>> jmespath.search('books[?price==`1`]', data)
[{'price': 1, 'name': 'x'}]
>>> jmespath.search('books[?price==`2`]', data)
[{'price': 2, 'tag': 't', 'name': 'y'}]
# 根据 key 是否存在进行过滤, 十分方便
>>> jmespath.search('books[?tag]', data)
[{'price': 2, 'tag': 't', 'name': 'y'}]
>>> jmespath.search('books[?name]', data)
[{'price': 1, 'name': 'x'}, {'price': 2, 'tag': 't', 'name': 'y'}]
# 管道操作, 对结果再进行一次匹配
# @匹配到的值. 可以不填. 如果填可以用函数括起来. 比如 sort(@)
>>> jmespath.search('books[?name] | (@)[?tag]', data)
>>> jmespath.search(''books[?name] | [?tag]', data) 
[{'price': 2, 'tag': 't', 'name': 'y'}]
# 使用 or 匹配
>>> jmespath.search('books[?no] || books[?tag]', data)
[{'price': 2, 'tag': 't', 'name': 'y'}]
>>> jmespath.search('books[?no] || books[?name]', data)
[{'price': 1, 'name': 'x'}, {'price': 2, 'tag': 't', 'name': 'y'}]
# 使用 and 匹配
>>> jmespath.search('books[?name] && books[?tag]', data)
[{'price': 2, 'tag': 't', 'name': 'y'}]
# 默认匹配到结果不区分字段, 也可以将匹配到的结果按字段区分. 可以直接存数据库.
>>> jmespath.search('{uname:name, like_books:books[?name] && books[?tag]}', data)
{'like_books': [{'price': 2, 'tag': 't', 'name': 'y'}], 'uname': 'zz'}
>>> jmespath.search('books[?tag].name', data)
['y']
>>> jmespath.search('{book_names:books[?tag].name}', data)
{'book_names': ['y']}

缺点: []匹配规则需要加?, value 需要加“

jsonpath

基于 xpath

pip install jsonpath
>>> import jsonpath as j
>>> 
>>> j.jsonpath(data, '$.books')
[[{'price': 1, 'name': 'x'}, {'price': 2, 'tag': 't', 'name': 'y'}]]
>>> j.jsonpath(data, '$.books[*]')
[{'price': 1, 'name': 'x'}, {'price': 2, 'tag': 't', 'name': 'y'}]
>>> j.jsonpath(d, '$.books[*].tag')
['t']
>>> j.jsonpath(data, '$.books[0]')
[{'price': 1, 'name': 'x'}]
>>> j.jsonpath(data, '$.books[1]')
[{'price': 2, 'tag': 't', 'name': 'y'}]
>>> j.jsonpath(data, '$.books.1')
[{'price': 2, 'tag': 't', 'name': 'y'}]
>>> j.jsonpath(data, '$.books.0')
[{'price': 1, 'name': 'x'}]
# 根据 key 进行过滤
>>> j.jsonpath(data, '$.books[?(@.tag)]')
[{'price': 2, 'tag': 't', 'name': 'y'}]
# 根据 value 进行过滤
>>> j.jsonpath(data, '$.books[?(@.tag=="t")]')
[{'price': 2, 'tag': 't', 'name': 'y'}]
>>> 

可以直接 .索引下标 取值, 用处不算太大

缺点: 过于啰嗦
优点: 用户群体多, 语法比较标准

objectpath

结合 xpath, python

$ pip install objectpath
>>> from objectpath import Tree
>>> 
>>> for i in Tree(data).execute('$.books[@.tag is "t"]'):print i
{'price': 2, 'tag': 't', 'name': 'y'}
>>> Tree(data).execute('$.books[-1]]')
{'price': 2, 'tag': 't', 'name': 'y'}
>>> Tree(data).execute('$.books[0]]')
{'price': 1, 'name': 'x'}

缺点: 语法相比 jsonpath 有些简化, 但仍有些啰嗦.
匹配 value 用 is, 而且必须从根节点$开始.
匹配 key 必须用@指定当前结果(子项)

jsonpath-ng

集成 jsonpath-rw jsonpath-rw-ext
xpath 语法

$ pip install --upgrade jsonpath-ng
>>> from jsonpath_ng import jsonpath, parse
>>> 
>>> [i.value for i in parse('$.books[?(tag="t")]').find(data)]
#!!! ERROR  !!!, 不支持 value 过滤
# 对 value 过滤
>>> from jsonpath_ng.ext import parse
[i.value for i in parse('$.books[?(tag="t")]').find(data)]
[{'price': 2, 'tag': 't', 'name': 'y'}]

缺点: 开始我以为是将jsonpath-rw jsonpath-rw-ext结合到一起, 没想到只是打成一个包而已.

jsonpath-rw-ext

jsonpath-rw 的补充版本
xpath 语法

$ pip install jsonpath-rw-ext --user
>>> import jsonpath_rw_ext
>>> 
# 根据 value 过滤
>>> [i.value for i in jsonpath_rw_ext.parse('$.books[?(tag="t")]').find(data)]
[{'price': 2, 'tag': 't', 'name': 'y'}]
>>> jsonpath_rw_ext.match('$.books[?(tag="t")]', data)
[{'price': 2, 'tag': 't', 'name': 'y'}]
>>> jsonpath_rw_ext.match('$.books[?(tag=t)]', data)
[{'price': 2, 'tag': 't', 'name': 'y'}]
# 根据 key 过滤
>>> jsonpath_rw_ext.match('books[?(tag)]', data)
[{'price': 2, 'tag': 't', 'name': 'y'}]
>>> jsonpath_rw_ext.match('name', data)
['zz']
>>> jsonpath_rw_ext.match('books', data)
[[{'price': 1, 'name': 'x'}, {'price': 2, 'tag': 't', 'name': 'y'}]]
>>> 

优点: 对 jsonpath 语法做了一些简化, 比如不需要$指定根节点
缺点: 根据 value 过滤, 还是有些啰嗦

ujsonpath

xpath 语法
不支持属性判断 !!!

pip install ujsonpath
from ujsonpath import parse
>>> [i.value for i in parse('name').find({'name':'zz', 'books':[{'name':'x','price':1}, {'name':'y', 'price':2,'tag':'t'}]})]
['zz']

>>> [i.value for i in parse('books[*].name').find({'name':'zz', 'books':[{'name':'x','price':1}, {'name':'y', 'price':2,'tag':'t'}]})]
['x', 'y']
>>> [i.value for i in parse('books.1').find({'name':'zz', 'books':[{'name':'x','price':1}, {'name':'y', 'price':2,'tag':'t'}]})]
[{'price': 2, 'tag': 't', 'name': 'y'}]
>>> [i.value for i in parse('books[*][name|tag]').find({'name':'zz', 'books':[{'name':'x','price':1}, {'name':'y', 'price':2,'tag':'t'}]})]
['x', 'y']
>>> [i.value for i in parse('books[*][tag|name]').find({'name':'zz', 'books':[{'name':'x','price':1}, {'name':'y', 'price':2,'tag':'t'}]})]
['x', 't']
>>> [i.value for i in parse('$store.book[?(@.price==2)]').find({'name':'zz', 'books':[{'name':'x','price':1}, {'name':'y', 'price':2,'tag':'t'}]})]
!!! NotImplementedError !!!

缺点: 不支持属性判断, 示例代码中定义 NotImplementedError.

jsonxs

pip install jsonxs
>>> import jsonxs as j
>>> j.jsonxs( data, 'name')
'zz'
>>> j.jsonxs(data, 'books[1].name')
'y'
>>> j.jsonxs(data, 'books[0]')
{'price': 1, 'name': 'x'}
>>> j.jsonxs(data, 'books[1]')
{'price': 2, 'tag': 't', 'name': 'y'}

优点: 代码实现非常简单.
缺点: 功能单一, 只能取 key, 取索引. 不支持过滤


综上, 只有jsonpath, jmespath, jsonpath-rw-ext 这三个是方便用户使用的.
jsonpath 用户群体大, 语法最啰嗦
jsonpath-rw-ext 语法有过简化, 但依旧啰嗦
jmespath 过滤语法标新立异, 功能强大. 支持组合查询, 非常容易.

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值