之前学习爬虫的时候,如果是 HTML 的数据,通过 xpath 或是 css 选择器,就能很快的获取我们想要的数据,如果是 json 有没有类似 xpath 这种,能够直接根据条件定位数据,而不需要自行 json 解析在遍历获取。答案是有的,也就是 JSONPath。
在线测试网址 JSONPath 在线验证
所选用的环境是 Node + JavaScript,用到 jsonpath 这个包 jsonpath - npm (npmjs.com)
基本语法
过滤器表达式
通常的表达式格式为:[?(@.age > 18)] 表示当前节点属性 age 大于 18
| 操作符 | 描述 | 
|---|---|
| == | 等于符号,但数字 1 不等于字符 1(note that 1 is not equal to ‘1’) | 
| != | 不等于符号 | 
| < | 小于符号 | 
| <= | 小于等于符号 | 
| > | 大于符号 | 
| >= | 大于等于符号 | 
| =~ | 判断是否符合正则表达式,例如[?(@.name =~ /foo.*?/i)] | 
| in | 所属符号,例如[?(@.size in [‘S’, ‘M’])] | 
| nin | 排除符号 | 
| size | size of left (array or string) should match right | 
| empty | 判空 Null 符号 | 
语法就这些,不过单单有语法,不实践肯定是不够的。下面就是一些官方简单例子操作,还有一个终极实战
代码演示
var jp = require('jsonpath')
var cities = [
  { name: 'London', population: 8615246 },
  { name: 'Berlin', population: 3517424 },
  { name: 'Madrid', population: 3165235 },
  { name: 'Rome', population: 2870528 },
]
var names = jp.query(cities, '$..name')
// [ "London", "Berlin", "Madrid", "Rome" ]
如果使用 js 来遍历的话,也简单
let names = cities.map(c => c.name)
这个数据可能还没那么复杂,在看看下面这个例子,代码来源于https://goessner.net/articles/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
    }
  }
}
| JsonPath | Result | 
|---|---|
| $.store.book[*].author | 所有 book 的 author 节点 | 
| $..author | 所有 author 节点 | 
| $.store.* | store 下的所有节点,book 数组和 bicycle 节点 | 
| $.store..price | store 下的所有 price 节点 | 
| $..book[2] | 匹配第 3 个 book 节点 | 
| $..book[(@.length-1)],或$..book[-1:] | 匹配倒数第 1 个 book 节点 | 
| $..book[0,1],或$..book[:2] | 匹配前两个 book 节点 | 
| $..book[?(@.isbn)] | 过滤含 isbn 字段的节点 | 
| $..book[?(@.price<10)] | 过滤 price<10的节点 | 
| $..* | 递归匹配所有子节点 | 
对应的语法可直接到在 JSONPath 在线验证网站上进行测试。要提一点的是,jsonpath 是支持使用 || 与 && 进行过滤的,比如上面要获取 category 为 fiction,price 大于 10 的语法为$..book[?(@.price>10 && @.category=="fiction")] 结果如下
[
  {
    "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
  }
]
终极实战
也许你会觉得上面的例子太过简单了,可能没达到你预期所想要的效果,甚至还不如使用 json 遍历呢,下面我列举一个是我实战中遇到的例子(实际上这样的例子特别多),我先把部分数据展示出来(删除部分没用到的参数,实际参数远比这多),然后通过 js 遍历,以及 jsonpath 来获取我想要的数据。
结构
