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或JMESPath过滤

jsonpath的使用

快速入门 Python 爬虫常用解析库(xpathbs4)

带有JSONPath的JSON.NET SelectToken

Python爬虫 JsonPath -- JsonPath的安装和基本使用

JsonPath使用说明