爬虫 — JsonPath 和 CSV 文件读写
目录
一、JsonPath
1、概念
JsonPath 是一种简单的方法来提取给定 JSON 文档的部分内容,JsonPath 有许多编程语言,如 Javascript,Python,PHP 和 JAVA。
JsonPath 提供的 JSON 解析非常强大,它提供了类似正则表达式的语法,基本上可以满足所有你想要获得的 JSON 内容。但 JsonPath 解析的数据只针对 JSON 格式的数据。
2、安装
JsonPath 是第三方库,需要安装
在终端运行:pip install jsonpath
3、用法
JsonPath 操作符
符号 | 说明 |
---|---|
$ | 查询的根节点对象,用于表示一个 JSON 数据,可以是数组或对象 |
@ | 过滤器断言(filter predicate)处理的当前节点对象,类似 JAVA 中的 this 字段 |
* | 通配符,可以表示一个名字或数字 |
. . | 可以理解为递归搜索 |
. | 表示一个子节点 |
[‘name’(,‘’)] | 表示一个或多个子节点 |
[(,)] | 表示一个或多个数组下标 |
[start:end] | 数组片段,区间为[start, end), 不包含end |
[?()] | 过滤器表达式,表达式结果必须是boolean |
# 数据
dic = {
"resultCode": "1",
"resultMsg": "success",
"reqId": "52f9f3e1-1d76-47b4-b2ae-226633b61476",
"systemTime": "1681991278593",
"videoInfo": { # 父亲
"playSta": "1",
"video_image": "https://image.pearvideo.com/cont/20170714/cont-1110173-10436784.png",
"srcUrl": "https://video.pearvideo.com/mp4/short/20170714/1681991278593-10632788-aaa.mp4",
"videos": { # 儿子
"hdUrl": "",
"hdflvUrl": "",
"sdUrl": "",
"sdflvUrl": "", # 孙子
"srcUrl": "https://video.pearvideo.com/mp4/short/20170714/1681991278593-10632788-hd.mp4"
}
}
}
3.1、. .
递归搜索,不需要考虑节点位置获取数据。
jsonpath.jsonpath(数据, ‘$. .key’)
print(jsonpath.jsonpath(dic, '$..srcUrl'))
# 返回值
# ['https://video.pearvideo.com/mp4/short/20170714/1681991278593-10632788-aaa.mp4', 'https://video.pearvideo.com/mp4/short/20170714/1681991278593-10632788-hd.mp4']
print(jsonpath.jsonpath(dic, '$..video_image'))
# 返回值
# ['https://image.pearvideo.com/cont/20170714/cont-1110173-10436784.png']
print(jsonpath.jsonpath(dic, '$..[playSta,video_image]'))
# 返回值
# ['1', 'https://image.pearvideo.com/cont/20170714/cont-1110173-10436784.png']
3.2、.
匹配下一级的标签(子集)。
print(jsonpath.jsonpath(dic, '$..srcUrl')[0])
# 返回值
# https://video.pearvideo.com/mp4/short/20170714/1681991278593-10632788-aaa.mp4
print(jsonpath.jsonpath(dic, '$..videoInfo.srcUrl'))
# 返回值
# ['https://video.pearvideo.com/mp4/short/20170714/1681991278593-10632788-aaa.mp4']
print(jsonpath.jsonpath(dic, '$..videos.srcUrl'))
# 返回值
# ['https://video.pearvideo.com/mp4/short/20170714/1681991278593-10632788-hd.mp4']
# 数据
dic = {
"resultCode": "1",
"resultMsg": "success",
"reqId": "52f9f3e1-1d76-47b4-b2ae-226633b61476",
"systemTime": "1681991278593",
"videoInfo": {
"playSta": "1",
"video_image": "https://image.pearvideo.com/cont/20170714/cont-1110173-10436784.png",
"videos": [
{
"hdUrl": "1",
"hdflvUrl": "",
"sdUrl": "",
"sdflvUrl": "bbbbbb",
},
{
"hdUrl": "",
"hdflvUrl": "",
"sdUrl": "",
"sdflvUrl": "cccccc",
"srcUrl": "https://video.pearvideo.com/mp4/short/20170714/1681991278593-10632788-hd.mp4"
},
{
"hdUrl": "",
"hdflvUrl": "",
"sdUrl": "",
"sdflvUrl": "dddddd",
"srcUrl": "https://video.pearvideo.com/mp4/short/20170714/1681991278593-10632788-hd.mp4"
},
]
}
}
3.3、@
正在处理的当前节点,?:过滤器。
# 只取 videos 列表里,包含 srcUrl 的对象
print(jsonpath.jsonpath(dic, '$..videos[?(@.srcUrl)]'))
# 返回值
# [{'hdUrl': '', 'hdflvUrl': '', 'sdUrl': '', 'sdflvUrl': 'cccccc', 'srcUrl': 'https://video.pearvideo.com/mp4/short/20170714/1681991278593-10632788-hd.mp4'}, {'hdUrl': '', 'hdflvUrl': '', 'sdUrl': '', 'sdflvUrl': 'dddddd', 'srcUrl': 'https://video.pearvideo.com/mp4/short/20170714/1681991278593-10632788-hd.mp4'}]
# 获取所有 key 为 srcUrl 的值
print(jsonpath.jsonpath(dic, '$..srcUrl'))
# 返回值
# ['https://video.pearvideo.com/mp4/short/20170714/1681991278593-10632788-hd.mp4', 'https://video.pearvideo.com/mp4/short/20170714/1681991278593-10632788-hd.mp4']
# 根据值筛选数据
print(jsonpath.jsonpath(dic, '$..videos[?(@.hdUrl=="1")]'))
# 返回值
# [{'hdUrl': '1', 'hdflvUrl': '', 'sdUrl': '', 'sdflvUrl': 'bbbbbb'}]
3.4、*
通配符,可以表示一个名字或数字。
# 多个子节点,通过下标,获取第一个对象 sdflvUrl 值
print(jsonpath.jsonpath(dic, '$..videos[0].sdflvUrl'))
# 返回值
# ['bbbbbb']
# 多个子节点,通过下标,获取第一和第二两个对象 sdflvUrl 值
print(jsonpath.jsonpath(dic, '$..videos[0,1].sdflvUrl'))
# 返回值
# ['bbbbbb', 'cccccc']
# 取所有节点
print(jsonpath.jsonpath(dic, '$..videos[*].sdflvUrl'))
# 返回值
# ['bbbbbb', 'cccccc', 'dddddd']
3.5、[start:end]
数组片段,区间为[start, end),不包含end。
# 列表下标,取两个数据 —— 切片,取1,2
print(jsonpath.jsonpath(dic, '$..videos[1:3].sdflvUrl'))
# 返回值
# ['cccccc', 'dddddd']
二、CSV 文件读写
1、概念
CSV(Comma Separated Values),是一种常用的文本格式,用以存储表格数据,包括数字或者字符。很多程序在处理数据时都会碰到 CSV 这种格式的文件。
Python 自带了 CSV 模块,专门用于处理 CSV 文件的读取。
2、写入 CSV 文件
2.1 方式一
# 导入模块
import csv
# 爬取下来的数据
data = [('肖申克的救赎', '9.7', '希望让人自由'), ('霸王别姬', '9.6', '风华绝代'), ('阿甘正传', '9.5', '一部美国近现代史')]
# 设置表头
head = ('电影名', '评分', '简介')
# 保存
# 'w':写入,encoding='utf-8-sig':编码格式,newline='':清除空行
with open('douban.csv','w',encoding='utf-8-sig',newline='') as f:
# 创建 csv 写入对象
writer = csv.writer(f)
# 写入表头
writer.writerow(head)
# 写入数据
writer.writerows(data)
2.2 方式二
# 导入模块
import csv
# 列表嵌套字典格式数据
data = [
{'标题': '肖申克的救赎', '评分': '9.7', '引言': '希望让人自由'},
{'标题': '霸王别姬', '评分': '9.6', '引言': '风华绝代'},
{'标题': '阿甘正传', '评分': '9.5', '引言': '一部美国近现代史'}
]
# 设置表头,key 必须要跟表头保持一致
head = ('标题', '评分', '引言')
# 创建文件对象
with open('douban1.csv', 'w', encoding='utf-8-sig', newline='') as f:
# 创建 csv 字典写入对象,fieldnames 固定参数,不可修改
writer = csv.DictWriter(f, fieldnames=head) # 设置表头
# 写入表头
writer.writeheader()
# 写入数据
writer.writerows(data)
'''
如果出现报错信息
ValueError: dict contains fields not in fieldnames: '标题', '引言'
检查表头是否与字典当中的 key 保持一致
'''
3、读取 CSV 文件
3.1 方式一
# 导入模块
import csv
# 读取数据
with open('douban.csv','r', encoding='utf-8-sig') as f:
# 创建 csv 读取对象
read = csv.reader(f)
# 不读取表头
next(read)
# list列表,直接下标取值
for i in read:
print(i)
3.2 方式二
# 导入模块
import csv
# 读取数据
with open('douban.csv','r', encoding='utf-8-sig') as f:
# 创建 csv 读取对象,返回数据为字典格式
read = csv.DictReader(f)
# dict 字典,通过 key 取值
for i in read:
print(i['电影名'])
w:写入数据(重新写入,覆盖)
r:读取数据
a:追加,文件原有的内容基础上添加新的内容
wb:保存视频,图片,音频
三、JsonPath + CSV 文件读写案例
目标网站:‘https://www.liepin.com/zhaopin/?inputFrom=www_index&workYearCode=0&key=python&scene=input&ckId=wodpawgkbhefdidqtq1hso4xqml8alw9&dq=’
翻页爬取前两页的数据(职位名称,公司地点,薪资)
# 导入模块
import requests
import csv
# 指定url
url = 'https://api-c.liepin.com/api/com.liepin.searchfront4c.pc-search-job'
# 设置请求头参数
head = {
'Accept': 'application/json, text/plain, */*',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'en,zh-CN;q=0.9,zh;q=0.8',
'Connection': 'keep-alive',
'Content-Length': '406',
'Content-Type': 'application/json;charset=UTF-8;',
'Cookie': 'XSRF-TOKEN=Bh2gi6GcTtqIvM4lrU3m4g; __gc_id=f204bf9d8cc34c659470e09660711986; _ga=GA1.1.1267984231.1683208911; __uuid=1683208911082.70; __tlog=1683208911084.44%7C00000000%7C00000000%7C00000000%7C00000000; Hm_lvt_a2647413544f5a04f00da7eee0d5e200=1683208911; __session_seq=13; __uv_seq=13; Hm_lpvt_a2647413544f5a04f00da7eee0d5e200=1683210035; _ga_54YTJKWN86=GS1.1.1683208911.1.1.1683211483.0.0.0',
'Host': 'api-c.liepin.com',
'Origin': 'https://www.liepin.com',
'Referer': 'https://www.liepin.com/',
'sec-ch-ua': '"Chromium";v="112", "Google Chrome";v="112", "Not:A-Brand";v="99"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-site',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36',
'X-Client-Type': 'web',
'X-Fscp-Bi-Stat': '{"location": "https://www.liepin.com/zhaopin/?inputFrom=www_index&workYearCode=0&key=python&scene=input&ckId=wodpawgkbhefdidqtq1hso4xqml8alw9&dq="}',
'X-Fscp-Fe-Version': '',
'X-Fscp-Std-Info': '{"client_id": "40108"}',
'X-Fscp-Trace-Id': '046a4cc3-72bc-4b95-8f7a-c39df3ee11d8',
'X-Fscp-Version': '1.1',
'X-Requested-With': 'XMLHttpRequest',
'X-XSRF-TOKEN': 'Bh2gi6GcTtqIvM4lrU3m4g',
}
# 表格数据
csvList = []
# 获取前两页数据
for i in range(0, 2):
# post请求参数
data = {"data":{"mainSearchPcConditionForm":{"city":"410","dq":"410","pubTime":"","currentPage":i,"pageSize":40,"key":"python","suggestTag":"","workYearCode":"0","compId":"","compName":"","compTag":"","industry":"","salary":"","jobKind":"","compScale":"","compKind":"","compStage":"","eduLevel":""},"passThroughForm":{"scene":"input","skId":"","fkId":"","ckId":"vmglvn6pyc3nv5scebhp02euhaexpkd8"}}}
# 发送请求
res = requests.post(url, headers=head, json=data)
# 获取响应内容
json_data = res.json() # 获取json格式的数据
result = json_data['data']['data']['jobCardList']
# print(result)
# 循环获取所需数据
for j in result:
# 职位名称
title = j['job']['title']
# 薪资
salary = j['job']['salary']
# 公司地点
compName = j['comp']['compName']
# 打印所需数据
print(title, salary, compName)
# 追加表格数据
csvList.append((title, salary, compName))
# print(csvList)
# 规定表头
tableHead = ('职位名称','薪资','公司地点')
# 创建文件对象
with open('liepin.csv', 'w', encoding='utf-8-sig', newline='') as f:
# 创建csv写入对象
writer = csv.writer(f)
# 写入表头
writer.writerow(tableHead)
# 写入数据
writer.writerows(csvList)
记录学习过程,欢迎讨论交流,尊重原创,转载请注明出处~