什么?你学爬虫还不会用 jsonpath?

177 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第22天,点击查看活动详情

JsonPath

JSONPath是一种信息抽取类库,是从JSON文档中抽取指定信息的工具,提供多种语言实现版本,包括:JavascriptPythonPHPJavaJSONPath对于JSON来说,相当于XPath对于XML

安装

pip install jsonpath

image.png

基本使用

import jsonpath
jsonObj = json.load(open('xxx.json', 'r', encoding='utf-8'))
result = jsonpath.jsonpath(jsonObj, 'jsonpath语法')

语法

JSON结构清晰,可读性高,复杂度低,非常容易匹配。JSONPath的语法与Xpath类似,下表展示了JSONPathXPath的语法对比:

XPathJSONPathDescription
/$表示根元素
.@ 当前元素
/. or []子元素
..n/a父元素
//..递归下降,JSONPath是从E4X借鉴的。
**通配符,表示所有的元素
@n/a 属性访问字符
[][]子元素操作符
|[,]连接操作符在XPath 结果合并其它结点集合。JSONP允许name或者数组索引。
n/a[start:end:step]数组分割操作从ES4借鉴。
[]?()应用过滤表示式
n/a()脚本表达式,使用在脚本引擎下面。
()n/aXpath分组

语法案例演示

首先,我们到沸点评论区找一条沸点,打开评论区,拿到服务器响应回来的json数据,并将其保存到我们项目文件中。不想找的小伙伴也可以直接复制下面的json内容。

image.png

image.png

{
  "err_no": 0,
  "err_msg": "success",
  "data": [
    {
      "comment_id": "7177213960243381053",
      "comment_info": {
        "comment_id": "7177213960243381053",
        "user_id": "2189882895379623",
        "item_id": "7176819484424929337",
        "item_type": 4,
        "comment_content": "必须N+1,闹到底",
        "comment_pics": [],
        "comment_status": 1,
        "ctime": 1671075466,
        "comment_replys": [],
        "digg_count": 0,
        "bury_count": 0,
        "reply_count": 0,
        "is_digg": false,
        "is_bury": false,
        "level": 0
      },
      "user_info": {
        "user_id": "2189882895379623",
        "user_name": "知识搅拌机",
        "company": "",
        "job_title": "项目经理",
        "avatar_large": "https://p26-passport.byteacctimg.com/img/user-avatar/6a74bbc65a694653ed30a1b76c16ccbc~300x300.image",
        "level": 1,
        "description": "为什么可乐一定要加冰"
      }
    },
    {
      "comment_id": "7177204289558496059",
      "comment_info": {
        "comment_id": "7177204289558496059",
        "user_id": "2506542243125469",
        "item_id": "7176819484424929337",
        "item_type": 4,
        "comment_content": "回家休息个把月,再回去仲裁它",
        "comment_pics": [],
        "comment_status": 1,
        "ctime": 1671073224,
        "comment_replys": [
          {
            "user_id": "1415826709428494",
            "reply_id": "7177209524732953381",
            "reply_comment_id": "7177204289558496059",
            "reply_to_reply_id": "0",
            "reply_to_user_id": "0",
            "item_id": "7176819484424929337",
            "item_type": 4,
            "reply_content": "在收集证据了",
            "reply_pics": [],
            "reply_status": 1,
            "ctime": 1671074413,
            "digg_count": 0,
            "burry_count": 0
          }
        ],
        "digg_count": 0,
        "bury_count": 0,
        "reply_count": 1,
        "is_digg": false,
        "is_bury": false,
        "level": 0
      },
      "user_info": {
        "user_id": "2506542243125469",
        "user_name": "花染醉墨",
        "company": "",
        "job_title": "",
        "avatar_large": "https://p6-passport.byteacctimg.com/img/user-avatar/0b32cad711929b8817c1365cab56420d~300x300.image",
        "level": 5,
        "description": ""
      },
      "cursor": "5",
      "count": 84,
      "has_more": true
    }
  ]
}

案例1

找到沸点回复评论中的所有掘友,语法如下:

  • $:根目录
  • data[*]data属性是一个列表,用*代表全部
  • .comment_info:表示data对象的comment_info属性
  • .user_id:同上
import json
import jsonpath

jsonObj = json.load(open('沸点评论.json', 'r', encoding='utf-8'))
result = jsonpath.jsonpath(jsonObj, '$.data[*].comment_info.user_id')
print(result)

可以看到筛选出来了评论中的掘友,如果只要找其中的一条,则将data[*]修改为对应索引值即可。

image.png

image.png

案例2

上面的案例找的是沸点评论中的用户ID,但是只有评论的,评论的回复用户ID并没有一起查询出来,如果需要需要加上回复的用户ID,则需要使用..

image.png

可以看到只要是在json中出现的user_id属性都查出来了,但是这里有个小问题,就是该功能并不会帮我们去重。

案例3

查询某个字段下所有的信息,如user_info字段:

import json
import jsonpath

jsonObj = json.load(open('沸点评论.json', 'r', encoding='utf-8'))

result = jsonpath.jsonpath(jsonObj, '$.data[0].user_info.*')
print(result)

image.png

案例4

找到某个字段下的所有指定字段,如data属性下的所有user_id字段,包括子孙节点:

import json
import jsonpath

jsonObj = json.load(open('沸点评论.json', 'r', encoding='utf-8'))

result = jsonpath.jsonpath(jsonObj, '$.data[*]..user_id')
print(result)

image.png

案例5

找到列表中的后N条数据:

import json
import jsonpath

jsonObj = json.load(open('沸点评论.json', 'r', encoding='utf-8'))

result = jsonpath.jsonpath(jsonObj, '$.data[(@.length-1)]')
print(result)

image.png

案例6

找到列表的前N条数据:

import json
import jsonpath

jsonObj = json.load(open('沸点评论.json', 'r', encoding='utf-8'))

result = jsonpath.jsonpath(jsonObj, '$.data[0,]')
print(result)
import json
import jsonpath

jsonObj = json.load(open('沸点评论.json', 'r', encoding='utf-8'))

result = jsonpath.jsonpath(jsonObj, '$.data[:1]')
print(result)

image.png

案例7

在小括号前面加?, 过滤出包含指定属性的数据:

import json
import jsonpath

jsonObj = json.load(open('沸点评论.json', 'r', encoding='utf-8'))

result = jsonpath.jsonpath(jsonObj, '$.data[*]..[?(@.reply_comment_id)]')
print(result)

image.png

案例8

过滤出等级大于2的用户:

result = jsonpath.jsonpath(jsonObj, '$.data[*][?(@.level > 2)]')
print(result)

image.png

相信通过上面的几个案例,对于jsonpath的使用都有一定的了解了,跟xpath用法是差不多的,多练习几次很快就能熟练使用了。