Python JsonPath 详细使用

165 阅读3分钟

一、简介

  • JsonPath 是一种信息抽取类库,是从JSON文档中抽取指定信息的工具,提供多种语言实现版本,包括:Javascript, PythonPHPJava

  • JsonPath 对于 JSON 来说,相当于 XPATH 对于 XML,参考文章:

  • Json 结构清晰,可读性高,复杂度低,非常容易匹配,下表中对应了 XPath 的用法(n/a 代表不支持):

    XPathJSONPath描述
    /$根节点
    .@现行节点
    /. or []取子节点
    ..n/a取父节点,JsonPath 未支持
    //..就是不管位置,选择所有符合条件的条件
    **匹配所有元素节点
    @n/a根据属性访问,Json 不支持,因为 Json 是个 Key-value 递归结构,不需要
    [][]迭代器标示(可以在里边做简单的迭代操作,如数组下标,根据内容选值等)
    |[,]支持迭代器中做多选
    []?()支持过滤操作
    n/a()支持表达式计算
    ()n/a分组,JsonPath 不支持

二、使用案例

  • 安装

    $ pip install jsonpath
    
  • 本地案例测试数据,从 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)