Xpathbs4和jsonpath
Posted 泥地里的乌龟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Xpathbs4和jsonpath相关的知识,希望对你有一定的参考价值。
正则、xpath、beautifulsoup、jsonpath都是在爬虫爬取数据时,定位数据的不同规则方法。但是鉴于大家在学习xpath和bs4之后经常搞混,所以有了这篇冒着熬夜猝死的风险整理出来的内容。为什么不写正则?因为正则太简单了,知道元字符怎么使用,记住match、search和find就可以了。为什么写jsonpath,因为某宝和渣浪的数据都可以使用jsonpath来获取,所以整理一些知识点让大家了解一下子。
说明1:以下所有知识点不保证以后绝对用到,只选择较为常见的内容来举例说明,如果求知欲超强,可以自行百度解脱自己。
说明2:完整代码在相关内容下方,下面大部分知识点多为文字描述不方便理解,所以建议大家运行代码并结合注释更快理解相关用法。
一、Xpath
安装:pip install lxml
使用:
1需要导入相关模块 from lxml import etree;
2然后加载解析文件 html = etree.HTML(“html_code”)
知识点1:根据标签中的属性去匹配数据,使用格式为 [@属性=”属性值”],一般用作寻找下一级或者匹配其他相同数据但不好匹配的内容;
知识点2:使用text() 获取文本,如果没有text,那么结果则打印出一个对象;
1song = html.xpath('//li[@class="songlist__header_name"]/text()')
1songurl = html.xpath('//div[@class="songlist__artist"]/a/@href')
知识点4:同时获取两个不同标签中的内容,比如招聘网站中的“职位介绍”和“职位要求”,需要同时获取到可以使用 ‘|’;
1title2 = html.xpath('//i/@title | //div/@title')
知识点5:在xpath语句中获取相同标签下指定索引的数据时使用 [索引] ,xpath中的索引从1开始;
1syz = html.xpath('//ul[@class="songlist__header"]//li[1]/text()')
知识点6:position() 此方法可以通过各种比较运算获取相关的值
1pos = html.xpath('//ul[@class="songlist__list"]//li[position()<=2]//a[@class="js_song"]/text()')
完整代码示例:
1html_code = '''
2<div class="mod_songlist">
3 <ul class="songlist__header">
4 <li class="songlist__header_name">歌曲</li>
5 <li class="songlist__header_author">歌手</li>
6 <li class="songlist__header_time">时长</li>
7 </ul>
8 <ul class="songlist__list">
9 <li mid="410314" ix="0">
10 <div class="songlist__songname">
11 <i class="songlist__icon" title="独家"></i>
12 <span class="songlist__songname_txt"><a href="https://y.qq.com/n/yqq/song/003O5GaP1SYe94.html" class="js_song" title="牛仔很忙">牛仔很忙</a></span>
13 <div class="mod_list_menu">
14 <a href="javascript:;" class="list_menu__item list_menu__play js_play" title="播放">
15 <span class="icon_txt">播放</span>
16 </a>
17 </div>
18 </div>
19 <div class="songlist__artist" title="周杰伦">
20 <a href="https://y.qq.com/n/yqq/singer/0025NhlN2yWrP4.html" data-singermid="0025NhlN2yWrP4" data-singerid="4558" title="周杰伦" class="singer_name">周杰伦</a>
21 </div>
22 </li>
23
24 <li mid="102340966" ix="1">
25 <div class="songlist__songname">
26 <i class="songlist__icon" title="独家"></i>
27 <span class="songlist__songname_txt"><a href="https://y.qq.com/n/yqq/song/004bRWFg3fej9y.html" class="js_song" title="彩虹 《命运呼叫转移》电影主题曲">彩虹<span class="songlist__song_txt">《命运呼叫转移》电影主题曲</span></a></span>
28 <div class="mod_list_menu">
29 <a href="javascript:;" class="list_menu__item list_menu__play js_play" title="播放">
30 <span class="icon_txt">播放</span>
31 </a>
32 </div>
33 </div>
34 <div class="songlist__artist" title="周杰伦">
35 <a href="https://y.qq.com/n/yqq/singer/0025NhlN2yWrP4.html" data-singermid="0025NhlN2yWrP4" data-singerid="4558" title="周杰伦" class="singer_name">周杰伦</a>
36 </div>
37 </li>
38
39
40 <li mid="410316" ix="2">
41 <div class="songlist__songname">
42 <i class="songlist__icon" title="独家"></i>
43 <span class="songlist__songname_txt"><a href="https://y.qq.com/n/yqq/song/002qU5aY3Qu24y.html" class="js_song" title="青花瓷 ">青花瓷</a></span>
44 <div class="mod_list_menu">
45 <a href="javascript:;" class="list_menu__item list_menu__play js_play" title="播放">
46 <span class="icon_txt">播放</span>
47 </a>
48 </div>
49 </div>
50 <div class="songlist__artist" title="周杰伦">
51 <a href="https://y.qq.com/n/yqq/singer/0025NhlN2yWrP4.html" data-singermid="0025NhlN2yWrP4" data-singerid="4558" title="周杰伦" class="singer_name">周杰伦</a>
52 </div>
53 </li>
54 </ul>
55</div>
56'''
57# 导入相关模块
58from lxml import etree
59# 加载解析文件
60html = etree.HTML(html_code)
61
62# 获取 歌曲
63# 知识点1:指定标签下的属性获取数据时,格式为 [@属性=“属性值”] 此时打印会得到一个对象
64# 知识点2:/text() 获取文本
65song = html.xpath('//li[@class="songlist__header_name"]/text()')
66# print(song)
67
68# 获取歌曲详情url
69# 知识点3:获取标签中属性的属性值使用@ ,如下:
70songurl = html.xpath('//div[@class="songlist__artist"]/a/@href')
71# print(songurl)
72
73
74# 知识点4:同时获取数据,使用 | 如下获取标签i中的title值和div中的title值
75title2 = html.xpath('//i/@title | //div/@title')
76# print(title2)
77
78# 知识点5:获取相同标签下指定索引的数据 使用[索引],xpath中的索引从1开始
79syz = html.xpath('//ul[@class="songlist__header"]//li[1]/text()')
80# print(syz)
81
82# 知识点6:使用position对比查找指定索引 这里大于2,则结果为第3个
83pos = html.xpath('//ul[@class="songlist__list"]//li[position()<=2]//a[@class="js_song"]/text()')
84print(pos)
二、Beautifulsoup
安装:pip install bs4
使用:
1导入该模块 from bs4 import Beautiful
2创建一个对象,指定解析类型soup = BeautifulSoup(html,'lxml')
知识点0:bs4中有id选择器div#id和class选择器div.class,请根据自己的需求和实际情况进行使用;
知识点1:使用.text 或者 .string 获取文本;
知识点2:select()方法返回列表,所以可以直接使用 [索引] 获取想要的数据;
1texta2 = soup.select('div.text > a')[2].text
知识点3:使用.get()方法来获取属性值,比如a标签的href和img的src;
1urla = soup.select('[class="text"] > a')[2].get('href')
知识点4:find()匹配一个,find_all()匹配所有,find_xxx还有很多方法,详细的内容可以查看代码中的注释或官方手册;
1atags = soup.find_all('a')
知识点5:^ 显示开头;$ 限制结尾;*模糊查询;
1kt = soup.select('span[class^="t"]')
完整代码示例
1#代码示例
2
3html = """
4<html>
5 <head>
6 <title>BOSS直聘网页部分源码</title>
7 </head>
8 <body>
9
10 <li>
11 <div id="sub-li">
12 <a href="https://www.zhipin.com/gongsi/1d2c60b2ada52a381XN62tW_.html" target="_blank" ka="index_rcmd_companylogo_20_custompage" class="company-info">
13
14 <img src="" src="/img?url=https://img.bosszhipin.com/beijin/mcs/chatphoto/20160530/b21e61e21c623660256fd6310fadb56424767dca77c834e649ea6b764734f3b7.jpg?x-oss-process=image/resize,w_100,limit_0" alt="">
15 <div class="conpany-text">
16 <h4>58集团</h4>
17 <p>已上市<span class="vline"></span>互联网</p>
18 </div>
19 </a>
20 <a href="https://www.zhipin.com/gongsir/1d2c60b2ada52a381XN62tW_.html" target="_blank" ka="index_rcmd_company_20_custompage" class="about-info">
21 <p>
22 <span class="pull-right"><span class="text-blue">26</span>位BOSS在线</span>
23 <span class="text-blue">42</span>个热招职位
24 </p>
25 </a>
26 </div>
27 </li>
28 <li>
29 <h4>风控</h4>
30 <div class="text">
31 <a href="https://www.zhipin.com/c101010100-p180201/">风控</a>
32 <a href="https://www.zhipin.com/c101010100-p180202/">律师</a>
33 <a href="https://www.zhipin.com/c101010100-p180203/">资信评估</a>
34 <a href="https://www.zhipin.com/c101010100-p180204/">合规稽查</a>
35 </div>
36 </li>
37 </body>
38</html>
39"""
40
41from bs4 import BeautifulSoup
42soup = BeautifulSoup(html,'lxml')
43
44# 获取标题(直接 .标签.text(或.string)获取指定数据的文本信息)
45title = soup.title.string
46# print(title)
47
48# 获取 #资信评估# 超链接及文本
49'''
50selece() 返回值为列表,如果获取一个,使用select_one,类似于xpath在scrapy中的.extrace_first();如果获取每一个,请遍历;如果找其中的某个数据,请添加索引,此处用法同xpath
51# 知识点1:获取文本使用.text
52# 知识点2:获取任意属性的属性值,如href,scr 使用.get()
53# 知识点3:依次获取下一层标签使用 >
54'''
55texta1 = soup.select('div[class="text"] > a')[2].text
56texta2 = soup.select('div.text > a')[2].text
57# print(texta)
58
59
60
61# 也可以直接用属性,不加标签,建议添加
62urla = soup.select('[class="text"] > a')[2].get('href')
63# print(urla)
64
65# 子孙级 去查找相关属性的标签
66zisun = soup.select('p .text-blue')
67# print(zisun)
68# 子级 直接查找属性的标签
69zi = soup.select('p > .text-blue')
70# print(zi)
71
72# ^ 查询所有以t开头的标签(类似正则的限制开头)
73kt = soup.select('span[class^="t"]')
74# print(kt)
75# $ 查询所有以t结尾的标签(类似正则中限制结尾)
76jw = soup.select('span[class$="t"]')
77# print(jw)
78# * 模糊查询
79mh = soup.select('span[class*="u"]')
80# print(mh)
81
82
83# 获取span标签的单个属性(首个)
84attr = soup.span['class']
85# print(attr)
86# 以字典的格式输出属性及属性值
87attrs = soup.span.attrs
88# print(attrs)
89
90# 上面多用class选择器获取数据,格式: .classattr (.+class属性值)
91# bs4中还有id选择器,格式: #idattr (#+id属性值)
92idele = soup.select('div#sub-li')
93# print(idele)
94
95
96# 获取代码中所有a标签 find_all() 结果返回列表
97atags = soup.find_all('a')
98# for a in atags:
99# # 打印a标签 包含其中的内容
100# print(a)
101# # 打印标签名
102# print(a.name)
103# # 获取链接文本 下面两种都可以使用
104# print(a.text)
105# print(a.get_text())
106
107# 只匹配第一个标签
108aele = soup.find('a')
109# print(aele)
110# 在bs4中,True可以作为参数匹配所有标签,如下:
111tags = soup.find_all(True)
112# for tag in tags:
113# print(tag.name)
114# 获取所有含有 target 属性的内容 可以使用除class属性之外的所有属性,因为python中class是关键字
115targets = soup.find_all(target=True)
116# print(targets)
117
118'''
119其他补充:
120# 1.string作为参数时,可以查找指定的数据,string中的参数都会被打印,可以是单个字符串也可以是列表
121# 一定要区别 .string
122strword = soup.find_all(string = ['风控','律师'])
123print(strword)
124# 2.limit 用来限制输出数量,如下,只输出两个a标签的内容
125resnum = soup.find_all('a',limit=2)
126print(resnum)
127# 3.find_parents() 和 find_parent() 用来查找父节点,如下
128# 寻找父级,需要反向查询,find_parents返回列表
129fu = soup.find(string = '律师')
130ua = fu.find_parent('a')
131print(ua)
132# 4. find_next() 解析下一个对象 find_previous() 解析上一个对象
133
134'''
135
136'''
137# 扩展1
138
139diagnose()可以检测使用lxml 还是 xml进行解析,使用diagnose(),需要导入相关模块 from bs4.diagnose import diagnose
140bs4默认将文档作为html进行解析,如果需要xml,请将BeautifulSoup()第二个参数改为xml,如soup = BeautifulSoup(html,'lxml')
141
142
143'''
144# from bs4.diagnose import diagnose
145# # 直接使用该函数即可
146# diagnose(html)
147
148'''
149# 扩展2 了解即可
150xml 解析xml格式如网站地图的sitemap.xml
151lxml 解析html网页
152html5lib 容错性较好,以浏览器的方式解析文档,生成h5格式的文档 并不常用
153
154
155
156# 扩展3
157中文官方手册:https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/
158'''
三、Jsonpath
Jsonpath可以理解为匹配解析json的xpath
安装:pip install jsonpath
使用:
1导入模块 import jsonpath
2使用jsonpath.jsonpath(jsoncode,’rules’)格式进行匹配
知识点1:$表示根元素,必须要有,和xpath起始处的/一样;
1allele = jsonpath.jsonpath(jscode,'$..*')
知识点2:*表示所有
1name = jsonpath.jsonpath(jscode,'$.data.comments[*]..name')
知识点3:[索引] jsonpath语法中的索引从0开始;
1digg_count = jsonpath.jsonpath(jscode,'$.data.comments[0].digg_count')
知识点4:?() 表示指定数据,过滤出自己选择的内容 @表示指定的对象
1elelist = jsonpath.jsonpath(jscode,'$..comments[?(@.zgl)]')
知识点5:jsonpath中比较运算的使用
1reply_count = jsonpath.jsonpath(jscode,'$..comments[?(@.reply_count>3)]')
知识点6:获取最后一个数据,也可以使用切片的形式(见完整代码)
1 lone = jsonpath.jsonpath(jscode,'$..comments[(@.length-1)]')
完整代码示例:
1import jsonpath
2
3jscode = {
4 'message': 'success',
5 'data':
6 {'comments': [
7 {
8 'reply_data':{'reply_list': []},
9 'dongtai_id': 1609915612034062,
10 'user_digg': 0,
11 'reply_count': 6,
12 'id': 1609915612034062,
13 'text': '黄作兴真爷们!全民好叔叔啊',
14 'create_time': 1535335170,
15 'digg_count': 252,
16 'user':
17 {
18 'avatar_url': 'https://p1.pstatp.com/thumb/216e0010a57df74d57f7',
19 'user_id': 5888752041,
20 'name': '月头'
21 }
22 },
23 {
24 'reply_data': {'reply_list': []},
25 'dongtai_id': 1609923776820238,
26 'user_digg': 0,
27 'reply_count': 8,
28 'id': 1609923776820238,
29 'text': '我姐夫贷款5个亿,准备东山再起',
30 'create_time': 1535342957,
31 'digg_count': 68,
32 'user':
33 {
34 'avatar_url': 'https://p1.pstatp.com/thumb/54e700120e0553b91751',
35 'user_id': 50055032975,
36 'name': '浙江江南皮革厂黄鹤小姨子'
37 }
38 },
39 {
40 'reply_data': {'reply_list': []},
41 'dongtai_id': 1609916359282695,
42 'user_digg': 0,
43 'reply_count': 5,
44 'id': 1609916359282695,
45 'text': '不要问我那么多, 厂长是我表哥。那又是什么回事?',
46 'create_time': 1535335883,
47 'digg_count': 57,
48 'user':
49 {
50 'avatar_url': 'https://p0.pstatp.com/origin/3797/2889309425',
51 'user_id': 57029503634,
52 'name': '手机用户57029503634'
53 }
54 },
55 {
56 'reply_data': {'reply_list': []},
57 'dongtai_id': 1609912651052035,
58 'user_digg': 0,
59 'zgl':'我从未见过如此厚颜无耻之人',
60 'reply_count': 2,
61 'id': 1609912651052035,
62 'text': '现在江南阀门还开着,厂房老大了,',
63 'create_time': 1535332347,
64 'digg_count': 25,
65 'user':
66 {
67 'avatar_url': 'https://p1.pstatp.com/thumb/2c6a001a4629082ba945',
68 'user_id': 6732133131,
69 'name': '酸枣很枣'
70 }
71 }
72 ],
73 'total': 142,
74 'has_more': True,
75 }
76 }
77
78# 输出所有数据内容
79allele = jsonpath.jsonpath(jscode,'$..*')
80# print(allele)
81
82# 获取commonets中所有用户名 name
83name = jsonpath.jsonpath(jscode,'$.data.comments[*]..name')
84# print(name)
85
86
87# 获取点赞数
88digg_count = jsonpath.jsonpath(jscode,'$.data.comments[0].digg_count')
89# print(digg_count)
90
91# 获取用户id 如果没有指定索引,则获取所有并返回列表
92# 可以指定索引得出结果,如[2]获取第三个数据,也可以将所有user_id_list进行遍历
93user_id_list = jsonpath.jsonpath(jscode,'$..user.user_id')
94print(user_id_list)
95
96# [2:10:2] [start:end:step] [开始索引:结束索引:间隔步长] 如下,从索引2开始到10,间隔为2输出
97# 类似python中的切片
98user_id = jsonpath.jsonpath(jscode,'$..user.user_id')[2:10:2]
99# print(user_id)
100
101# user中的所有数据 * 表示所有
102alluser = jsonpath.jsonpath(jscode,'$..user.*')
103# print(alluser)
104
105# 包含zgl的数据 ?() 表示指定数据,过滤出自己选择的内容 @表示指定的对象
106elelist = jsonpath.jsonpath(jscode,'$..comments[?(@.zgl)]')
107# for ele in elelist:
108# print(ele['zgl'])
109
110# 获取回复大于3的数据 < > = 等相关的用法 大家可以自己修改数据进行测试
111reply_count = jsonpath.jsonpath(jscode,'$..comments[?(@.reply_count>3)]')
112# print(reply_count)
113
114# 最后一个数据
115# lone = jsonpath.jsonpath(jscode,'$..comments[(@.length-1)]')
116lone = jsonpath.jsonpath(jscode,'$..comments[-1:]')
117print(lone)
118
119
120
121
122'''
123xpath中开头/表示根路径,jsonpath中$表示根路径;
124xpath语句中/表示子级,jsonpath中.和[]表示子级;
125xpath中..表示父级,使用//表示子孙级,jsonpath中..表示子孙级
126xpath中()表示分组,jsonpath中()表示
127*在xpath和jsonpath中一样,都表示所有
128
129'''
130
131# http://jsonpath.herokuapp.com/ 测试jsonpath的在线网站
无论是哪一种定位匹配数据的方法,都需要大量的练习,熟能生巧才能游刃有余,毕竟写好匹配规则也可以找到一份工作,况且我们还要以一敌五拿到三人份工资呢。
以上是关于Xpathbs4和jsonpath的主要内容,如果未能解决你的问题,请参考以下文章
带有JSONPath的JSON.NET SelectToken