Linux 学习记录:jq 处理 json

这篇文章总结下 Linux 下如何在 shell 中利用 jq 处理 json 数据。

1. 背景

最近项目上需要利用政府数据做些分析,但是深圳市政府提供的 API,请求的数据都是分页的,通过 curl 每次只能请求一部分数据,但是我们的应用需要利用所有的数据,所以得将每次请求的 分片数据 整合起来。

又因为 API 返回的数据是 json 格式的,所以得需要在 shell 中处理 json。

于是想起了之前在 Github 上遇到的项目:the art of command line,里面介绍到了一个工具 jq,专门用来在 shell 里面处理 json 数据。于是专门学习了下其使用方法,在这篇文章中作个总结。

2. jq 的使用方法

2.1 准备工作

这里我用深圳市政府提供的 个体工商户基本信息 来做演示。

请求这个 API 需要三个参数:

  • page : 返回的数据在第几个分页;
  • rows : 当前这个分页下面包含多少条数据记录
  • appKey : 你自己的 appKey ,这个需要注册用户后才能获取到。

我们首先看直接在 shell 中用 GET 方式请求数据会得到什么。

输入:

1
ubuntu@VM-0-14-ubuntu:~$ curl https://opendata.sz.gov.cn/api/29200_01303570/1/service.xhtml?page=1\&rows=3\&appKey=<填用户自己的 appKey>

输出:

1
{"total":2464687,"data":[{"OPLOC":"深圳市宝安区福永街道兴围社区兴业一路27号第一层101号铺","ENTTYPE":"9510","REGORG":"440306","REGSTATE_CN":"吊销,未注销","NAME":"汤福祥","REGORG_CN":"宝安局","OPSCOPE":"公话(在授权范围内经营公用电话业务)。","REGSTATE":"2","COMPFORM_CN":"个人经营","REGNO":"440306803535369","APPRDATE":"2007-11-26","COMPFORM":"1","ESTDATE":"2007-11-26","TRANAME":"深圳市宝安区福永福家公话亭"},{"OPLOC":"深圳市福田区南园街道南园路埔尾新村28号103","ENTTYPE":"9510","REGORG":"440304","REGSTATE_CN":"存续(在营、开业、在册)","NAME":"周玉红","REGORG_CN":"福田局","OPSCOPE":"化妆品、日用品的批发与零售^","REGSTATE":"1","COMPFORM_CN":"个人经营","REGNO":"440304816709149","APPRDATE":"2016-09-20","COMPFORM":"1","ESTDATE":"2016-09-20","TRANAME":"深圳市福田区安颜化妆品商行"},{"OPLOC":"深圳市坪山新区坑梓老坑社区西坑新大道12号101","ENTTYPE":"9510","REGORG":"440310","REGSTATE_CN":"吊销,未注销","NAME":"伍群红","REGORG_CN":"坪山局","OPSCOPE":"美容(不含医学美容)(《卫生许可证》有效期至2015年12月25日)。","REGSTATE":"2","COMPFORM_CN":"个人经营","REGNO":"440309807694281","APPRDATE":"2011-12-30","COMPFORM":"1","ESTDATE":"2011-12-30","TRANAME":"深圳市坪山新区爱妍坊美容中心"}],"page":1,"rows":3}

可以看到服务器上返回的 json 数据是很杂乱的,没有我们熟悉的 json 格式。这时候 jq 就可以派上用场了。

2.2 . 用法

jq 最简单的用法使用 . 来美化输出。

还是之前的例子,我们在命令尾部通过管道 |GET 获取到的 json 数据传递给 jq

输入:

1
ubuntu@VM-0-14-ubuntu:~$ curl https://opendata.sz.gov.cn/api/29200_01303570/1/service.xhtml?page=1\&rows=3\&appKey=<填用户自己的 appKey> | jq '.'     # 注意 jq 后面跟的表达式一定要用引号包裹住

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"total": 2464687,
"data": [
{
"OPLOC": "深圳市宝安区福永街道兴围社区兴业一路27号第一层101号铺",
"ENTTYPE": "9510",
"REGORG": "440306",
"REGSTATE_CN": "吊销,未注销",
"NAME": "汤福祥",
"REGORG_CN": "宝安局",
"OPSCOPE": "公话(在授权范围内经营公用电话业务)。",
"REGSTATE": "2",
"COMPFORM_CN": "个人经营",
"REGNO": "440306803535369",
"APPRDATE": "2007-11-26",
"COMPFORM": "1",
"ESTDATE": "2007-11-26",
"TRANAME": "深圳市宝安区福永福家公话亭"
},
# 省略......
],
"page": 1,
"rows": 3
}

可以看到,输出的 json 已经是 well-formatted 的了,这是我们想要看到的。

2.2 获取数组中的元素

上述返回的 json 数据中,"data" 字段是一个数组。

如果我们需要获取 "data" 字段第一个元素的值,可以这么做:

1
ubuntu@VM-0-14-ubuntu:~$ curl https://opendata.sz.gov.cn/api/29200_01303570/1/service.xhtml?page=1\&rows=3\&appKey=<填用户自己的 appKey> | jq '.data[0]'

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"OPLOC": "深圳市宝安区福永街道兴围社区兴业一路27号第一层101号铺",
"ENTTYPE": "9510",
"REGORG": "440306",
"REGSTATE_CN": "吊销,未注销",
"NAME": "汤福祥",
"REGORG_CN": "宝安局",
"OPSCOPE": "公话(在授权范围内经营公用电话业务)。",
"REGSTATE": "2",
"COMPFORM_CN": "个人经营",
"REGNO": "440306803535369",
"APPRDATE": "2007-11-26",
"COMPFORM": "1",
"ESTDATE": "2007-11-26",
"TRANAME": "深圳市宝安区福永福家公话亭"
}

如果想要获取数组中所有元素怎们办呢?

输入:

1
ubuntu@VM-0-14-ubuntu:~$ curl https://opendata.sz.gov.cn/api/29200_01303570/1/service.xhtml?page=1\&rows=3\&appKey=<填用户自己的 appKey> | jq '.data[]'

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"OPLOC": "深圳市宝安区福永街道兴围社区兴业一路27号第一层101号铺",
"ENTTYPE": "9510",
"REGORG": "440306",
"REGSTATE_CN": "吊销,未注销",
"NAME": "汤福祥",
"REGORG_CN": "宝安局",
"OPSCOPE": "公话(在授权范围内经营公用电话业务)。",
"REGSTATE": "2",
"COMPFORM_CN": "个人经营",
"REGNO": "440306803535369",
"APPRDATE": "2007-11-26",
"COMPFORM": "1",
"ESTDATE": "2007-11-26",
"TRANAME": "深圳市宝安区福永福家公话亭"
}
{
"OPLOC": "深圳市福田区南园街道南园路埔尾新村28号103",
"ENTTYPE": "9510",
"REGORG": "440304",
"REGSTATE_CN": "存续(在营、开业、在册)",
"NAME": "周玉红",
# 省略......

需要注意的是,这种方式返回的数据是单独的 {} 组成的,是一条条独立的数据,并不是一个数组。

如果我们需要这些离散的数据组合成一个数组,可以这么做:

输入:

1
ubuntu@VM-0-14-ubuntu:~$ curl https://opendata.sz.gov.cn/api/29200_01303570/1/service.xhtml?page=1\&rows=3\&appKey=<填用户自己的 appKey> | jq '[.data[]]'

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[
{
"OPLOC": "深圳市宝安区福永街道兴围社区兴业一路27号第一层101号铺",
"ENTTYPE": "9510",
"REGORG": "440306",
"REGSTATE_CN": "吊销,未注销",
"NAME": "汤福祥",
"REGORG_CN": "宝安局",
"OPSCOPE": "公话(在授权范围内经营公用电话业务)。",
"REGSTATE": "2",
"COMPFORM_CN": "个人经营",
"REGNO": "440306803535369",
"APPRDATE": "2007-11-26",
"COMPFORM": "1",
"ESTDATE": "2007-11-26",
"TRANAME": "深圳市宝安区福永福家公话亭"
},
# 省略....
]

可以看到 {} 最外面已经包裹了一层 [],说明现在是一个数组。

2.3 获取某些特定的字段值

有些应用场景下,我们可能只需要利用返回的 json 中某个或某几个特定的 key。

拿上面返回的 json 数据为例,我们只需要 "data" 属性下面的 "NAME""REGNO" 两个属性,我们可以这么做:

输入:

1
ubuntu@VM-0-14-ubuntu:~$ curl https://opendata.sz.gov.cn/api/29200_01303570/1/service.xhtml?page=1\&rows=3\&appKey=<填用户自己的 appKey> | jq '.data[] | {NAME: .NAME, REGNO: .REGNO}'

输出:

1
2
3
4
5
6
7
8
9
10
11
12
{
"NAME": "汤福祥",
"REGNO": "440306803535369"
}
{
"NAME": "周玉红",
"REGNO": "440304816709149"
}
{
"NAME": "伍群红",
"REGNO": "440309807694281"
}

同样,如果想要返回的数据组装成一个数组,可以在最外层包裹一个 [] :

1
ubuntu@VM-0-14-ubuntu:~$ curl https://opendata.sz.gov.cn/api/29200_01303570/1/service.xhtml?page=1\&rows=3\&appKey=<填用户自己的 appKey> | jq '[.data[] | {NAME: .NAME, REGNO: .REGNO}]'

同样,如果只需要 "data" 属性的第一个元素的 "NAME""REGNO" 属性,只需要将 data[] 换成 data[0] 即可:

1
ubuntu@VM-0-14-ubuntu:~$ curl https://opendata.sz.gov.cn/api/29200_01303570/1/service.xhtml?page=1\&rows=3\&appKey=<填用户自己的 appKey> | jq '.data[0] | {NAME: .NAME, REGNO: .REGNO}'

2.4 返回的字段重命名

我们也可以将自己想要获取的字段的名字进行重命名:

还是用上面的例子做示范:我们想要将原字段名 "NAME" 改成 "my_name" ,将 "REGNO" 改成 "my_regno",可以这么做:

1
ubuntu@VM-0-14-ubuntu:~$ curl https://opendata.sz.gov.cn/api/29200_01303570/1/service.xhtml?page=1\&rows=3\&appKey=<填用户自己的 appKey> | jq '.data[] | {my_name: .NAME, myregno: .REGNO}'

输出:

1
2
3
4
5
6
7
8
9
10
11
12
{
"my_name": "汤福祥",
"my_regno": "440306803535369"
}
{
"my_name": "周玉红",
"my_regno": "440304816709149"
}
{
"my_name": "伍群红",
"my_regno": "440309807694281"
}

可以看到,返回的字段名已经变成我们自定义的名称了。

3. References

Donate comment here