一、简介
JsonPath
是一种信息抽取类库,是从JSON文档中抽取指定信息的工具,提供多种语言实现版本,包括:Javascript
,Python
,PHP
和Java
。JsonPath
对于JSON
来说,相当于XPATH
对于XML
,参考文章:
Json
结构清晰,可读性高,复杂度低,非常容易匹配,下表中对应了XPath
的用法(n/a
代表不支持):
XPath | JSONPath | 描述 |
/ |
$ |
根节点 |
. |
@ |
现行节点 |
/ |
. or [] |
取子节点 |
.. |
n/a |
取父节点,JsonPath 未支持 |
// |
.. |
就是不管位置,选择所有符合条件的条件 |
* |
* |
匹配所有元素节点 |
@ |
n/a |
根据属性访问,Json 不支持,因为 Json 是个 Key-value 递归结构,不需要 |
[] |
[] |
迭代器标示(可以在里边做简单的迭代操作,如数组下标,根据内容选值等) |
| |
[,] |
支持迭代器中做多选 |
[] |
?() |
支持过滤操作 |
n/a |
() |
支持表达式计算 |
() |
n/a |
分组,JsonPath 不支持 |
二、使用案例
- 安装
$ pip install jsonpat
- 本地案例测试数据,从 goessner - JsonPath 中拷贝的
{ "store": { "book": [ { "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 }, { "category": "fiction", "author": "Herman Melville", "title": "Moby Dick", "isbn": "0-553-21311-3", "price": 8.99 }, { "category": "fiction", "author": "J. R. R. Tolkien", "title": "The Lord of the Rings", "isbn": "0-395-19395-8", "price": 22.99 } ], "bicycle": { "color": "red", "price": 19.95 } } }
- 本地案例
# 导入 json import json # 导入 jsonpath import jsonpath # 1、json 读取本地文件 obj = json.load(open('test.json', 'r', encoding='utf-8')) # 2、jsonpath 进行分析查找 # 获取所有书籍的作者名称 list = jsonpath.jsonpath(obj, '$.store.book[*].author') # ['Nigel Rees', 'Evelyn Waugh', 'Herman Melville', 'J. R. R. Tolkien'] list = jsonpath.jsonpath(obj, '$..author') # ['Nigel Rees', 'Evelyn Waugh', 'Herman Melville', 'J. R. R. Tolkien'] # 获取 store 下面的所有元素 list = jsonpath.jsonpath(obj, '$.store.*') # [[{'category': 'reference', 'author': 'Nigel Rees', 'title': 'Sayings of the Century', 'price': 8.95}, ...], {'color': 'red', 'price': 19.95}] # 获取第 3 本书 list = jsonpath.jsonpath(obj, '$..book[2]') # [{'category': 'fiction', 'author': 'Herman Melville', 'title': 'Moby Dick', 'isbn': '0-553-21311-3', 'price': 8.99}] # 获取最后一本书 list = jsonpath.jsonpath(obj, '$..book[(@.length - 1)]') # [{'category': 'fiction', 'author': 'J. R. R. Tolkien', 'title': 'The Lord of the Rings', 'isbn': '0-395-19395-8', 'price': 22.99}] # 获取前 2 本书 list = jsonpath.jsonpath(obj, '$..book[0, 1]') # 错误写法,[0, 1]不能有空格,不然只会生效第一个值,后面的值不生效 list = jsonpath.jsonpath(obj, '$..book[0,1]') # [{'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}] list = jsonpath.jsonpath(obj, '$..book[:2]') # 切片写法,结果一样 # 条件过滤需要再 () 前面添加一个 ? # 过滤出包含 isbn 的书 list = jsonpath.jsonpath(obj, '$..book[?(@.isbn)]') # [{'category': 'fiction', 'author': 'Herman Melville', 'title': 'Moby Dick', 'isbn': '0-553-21311-3', 'price': 8.99}, {'category': 'fiction', 'author': 'J. R. R. Tolkien', 'title': 'The Lord of the Rings', 'isbn': '0-395-19395-8', 'price': 22.99}] # 查询哪本书超过 10 块钱 list = jsonpath.jsonpath(obj, '$..book[?(@.price > 10)]') # [{'category': 'fiction', 'author': 'Evelyn Waugh', 'title': 'Sword of Honour', 'price': 12.99}, {'category': 'fiction', 'author': 'J. R. R. Tolkien', 'title': 'The Lord of the Rings', 'isbn': '0-395-19395-8', 'price': 22.99}] # 输出 print(list)
- 远程案例
# 测试某站的城市地址接口 # 使用 urllib import urllib.request # 使用 json import json import jsonpath # 获取地址 url = 'https://dianying.taobao.com/cityAction.json?activityId&_ksTS=1668502740745_108&jsoncallback=jsonp109&action=cityAction&n_s=new&event_submit_doGetAllRegion=true' # 请求头 headers = { # 带冒号的请求头都需要注释掉,报错:ValueError: Invalid header name b':Authority' # ':authority': 'dianying.taobao.com', # ':method': 'GET', # ':path': '/cityAction.json?activityId&_ksTS=1668502740745_108&jsoncallback=jsonp109&action=cityAction&n_s=new&event_submit_doGetAllRegion=true', # ':scheme': 'https', # 'accept': 'text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01', # 报错:UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8b in position 1: invalid start byte # 'accept-encoding': 'gzip, deflate, br', 'accept-language': 'zh-CN,zh;q=0.9', 'bx-v': '2.2.3', 'cookie': 't=1e424829769814085473c593565f4f5e; cookie2=17da1c30f22c22dd8d81dd3a871ab15b; v=0; _tb_token_=e817e63793373; cna=YQP5G0dsBVICAXToSX0EYCa1; xlly_s=1; isg=BPX1ozOY-se3Ah57r0dv0iYMBHevcqmEGFxmE3ca42y7ThVAP8MsVEZAmBL4DsE8; l=eBTcoCz7T1tElPbMBOfZnurza779LIRAguPzaNbMiOCP97CH57nFW6zmqRTMCnGVhsF9R3kzvXKpBeYBclYsjqj4axom4zHmn; tfstk=cJ1lBkVp-nisATNdcgO71aCmdIyOZYcej65fgrjQfnFphI1ViZkqQ2V7o3q_4v1..', 'referer': 'https://dianying.taobao.com/', 'sec-ch-ua': '"Google Chrome";v="107", "Chromium";v="107", "Not=A?Brand";v="24"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"macOS"', 'sec-fetch-dest': 'empty', 'sec-fetch-mode': 'cors', 'sec-fetch-site': 'same-origin', 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36', 'x-requested-with': 'XMLHttpRequest' } # 请求对象 request = urllib.request.Request(url=url, headers=headers) # 模拟浏览器向服务器发起请求 response = urllib.request.urlopen(request) # 响应数据转成字符串 content = response.read().decode('utf-8') # 会发现拿回来的数据是一个 jsonp109({xx: xx}); 包裹的字符串,放到 https://www.json.cn/ 解析也失败 # 解决办法:删除 'jsonp109(' 与后面的 ');' 即可 # 代码切割 content = content.split('(')[1].split(')')[0] # 字符串转 json obj = json.loads(content) # 将内容写入文件(使用 json.loads() 则不需要存储) with open('test.json', 'w', encoding='utf-8') as f: f.write(content) # 方式一:读取文件(需要存储,在读出来) obj = json.load(open('test.json', 'r', encoding='utf-8')) # 方式二:字符串转 json(不需要存储,直接使用) # obj = json.loads(content) # 导出所有 regionName 名称 regionNames = jsonpath.jsonpath(obj, '$..regionName') # 输出 print(regionNames)