这篇文章总结下 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 | { |
可以看到,输出的 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 | { |
如果想要获取数组中所有元素怎们办呢?
输入:
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 | { |
需要注意的是,这种方式返回的数据是单独的 {}
组成的,是一条条独立的数据,并不是一个数组。
如果我们需要这些离散的数据组合成一个数组,可以这么做:
输入:
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 获取某些特定的字段值
有些应用场景下,我们可能只需要利用返回的 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 | { |
同样,如果想要返回的数据组装成一个数组,可以在最外层包裹一个 []
:
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 | { |
可以看到,返回的字段名已经变成我们自定义的名称了。