Xpath、bs4和jsonpath
正则、xpath、beautifulsoup、jsonpath都是在爬虫爬取数据时,定位数据的不同规则方法。为什么不写正则?因为正则太简单了,知道元字符怎么使用,记住match、search和find就可以了。为什么写jsonpath,因为某网站的数据都可以使用jsonpath来获取,所以整理一些知识点让大家了解一下子。
一、Xpath
安装:pip install lxml
使用:
1、需要导入相关模块 from lxml import etree;
2、然后加载解析文件 html = etree.HTML(“html_code”)
知识点1:根据标签中的属性去匹配数据,使用格式为 [@属性=”属性值”],一般用作寻找下一级或者匹配其他相同数据但不好匹配的内容;
知识点2:使用text() 获取文本,如果没有text,那么结果则打印出一个对象;
song = html.xpath('//li[@class="songlist__header_name"]/text()')
知识点3:使用@获取属性中的值,常见的会有获取a中的href链接和img中的src图片地址;当然也可以获取你想要的属性的值;
songurl = html.xpath('//div[@class="songlist__artist"]/a/@href')
知识点4:同时获取两个不同标签中的内容,比如招聘网站中的“职位介绍”和“职位要求”,需要同时获取到可以使用 ‘|’;
title2 = html.xpath('//i/@title | //div/@title')
知识点5:在xpath语句中获取相同标签下指定索引的数据时使用 [索引] ,xpath中的索引从1开始;
syz = html.xpath('//ul[@class="songlist__header"]//li[1]/text()')
知识点6:position() 此方法可以通过各种比较运算获取相关的值
pos = html.xpath('//ul[@class="songlist__list"]//li[position()<=2]//a[@class="js_song"]/text()')
完整代码示例:
# 导入相关模块
from lxml import etree
# 加载解析文件
html = etree.HTML(html_code)
# 获取 歌曲
# 知识点1:指定标签下的属性获取数据时,格式为 [@属性=“属性值”] 此时打印会得到一个对象
# 知识点2:/text() 获取文本song = html.xpath('//li[@class="songlist__header_name"]/text()')
# print(song)
# 获取歌曲详情url
# 知识点3:获取标签中属性的属性值使用@ ,如下:
songurl = html.xpath('//div[@class="songlist__artist"]/a/@href')
# print(songurl)
# 知识点4:同时获取数据,使用 | 如下获取标签i中的title值和div中的title值
title2 = html.xpath('//i/@title | //div/@title')
# print(title2)
# 知识点5:获取相同标签下指定索引的数据 使用[索引],xpath中的索引从1开始
syz = html.xpath('//ul[@class="songlist__header"]//li[1]/text()')
# print(syz)
# 知识点6:使用position对比查找指定索引 这里大于2,则结果为第3个pos = html.xpath('//ul[@class="songlist__list"]//li[position()<=2]//a[@class="js_song"]/text()')
print(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()方法返回列表,所以可以直接使用 [索引] 获取想要的数据;
texta2 = soup.select('div.text > a')[2].text
知识点3:使用.get()方法来获取属性值,比如a标签的href和img的src;
urla = soup.select('[class="text"] > a')[2].get('href')
知识点4:find()匹配一个,find_all()匹配所有,find_xxx还有很多方法,详细的内容可以查看代码中的注释或官方手册;
atags = soup.find_all('a')
知识点5:^ 显示开头;$ 限制结尾;*模糊查询;
kt = soup.select('span[class^="t"]')
完整代码示例
from bs4 import BeautifulSoup
soup = BeautifulSoup(html,'lxml')
# 获取标题(直接 .标签.text(或.string)获取指定数据的文本信息)
title = soup.title.string 46# print(title)
# 获取
#资信评估
# 超链接及文本
'''
selece() 返回值为列表,
如果获取一个,使用select_one,类似于xpath在scrapy中的.extrace_first();
如果获取每一个,请遍历;如果找其中的某个数据,请添加索引,此处用法同xpath
# 知识点1:获取文本使用.text
# 知识点2:获取任意属性的属性值,如href,scr 使用.get() 53# 知识点3:依次获取下一层标签使用 >
'''
texta1 = soup.select('div[class="text"] > a')[2].text texta2 = soup.select('div.text > a')[2].text
# print(texta)
# 也可以直接用属性,不加标签,建议添加 urla = soup.select('[class="text"] > a')[2].get('href')
# print(urla)
# 子孙级 去查找相关属性的标签
zisun = soup.select('p .text-blue')
# print(zisun)
# 子级 直接查找属性的标签
zi = soup.select('p > .text-blue')
# print(zi)
# ^ 查询所有以t开头的标签(类似正则的限制开头)
kt = soup.select('span[class^="t"]')
# print(kt)
# $ 查询所有以t结尾的标签(类似正则中限制结尾)
jw = soup.select('span[class$="t"]')
# print(jw)
# * 模糊查询
mh = soup.select('span[class*="u"]')
# print(mh)
# 获取span标签的单个属性(首个)
attr = soup.span['class']
# print(attr)
# 以字典的格式输出属性及属性值
attrs = soup.span.attrs
# print(attrs)
# 上面多用class选择器获取数据,格式: .classattr (.+class属性值)
# bs4中还有id选择器,格式: #idattr (#+id属性值)
idele = soup.select('div#sub-li')
# print(idele)
# 获取代码中所有a标签 find_all() 结果返回列表
atags = soup.find_all('a')
# for a in atags:
# 打印a标签 包含其中的内容
# print(a)
# # 打印标签名
# print(a.name)
# # 获取链接文本 下面两种都可以使用
# print(a.text)
# print(a.get_text())
# 只匹配第一个标签
aele = soup.find('a')
# print(aele)
# 在bs4中,True可以作为参数匹配所有标签,如下:
tags = soup.find_all(True)
# for tag in tags:
# print(tag.name)
# 获取所有含有 target 属性的内容 可以使用除class属性之外的所有属性,因为python中class是关键字
targets = soup.find_all(target=True)
# print(targets)
'''
其他补充:
# 1.string作为参数时,可以查找指定的数据,string中的参数都会被打印,可以是单个字符串也可以是列表
# 一定要区别 .string
strword = soup.find_all(string = ['风控','律师'])
print(strword)
# 2.limit 用来限制输出数量,如下,只输出两个a标签的内容
resnum = soup.find_all('a',limit=2)
print(resnum)
# 3.find_parents() 和 find_parent() 用来查找父节点,如下
# 寻找父级,需要反向查询,find_parents返回列表
fu = soup.find(string = '律师')
ua = fu.find_parent('a')
print(ua)
# 4. find_next() 解析下一个对象 find_previous() 解析上一个对象
'''
'''
# 扩展1138139diagnose()可以检测使用lxml 还是 xml进行解析,使用diagnose(),需要导入相关模块 from bs4.diagnose import diagnose
bs4默认将文档作为html进行解析,如果需要xml,请将BeautifulSoup()第二个参数改为xml,如soup = BeautifulSoup(html,'lxml')
'''
# from bs4.diagnose import diagnose
# # 直接使用该函数即可# diagnose(html)
'''
# 扩展2 了解即可
xml 解析xml格式如网站地图的sitemap.xml
lxml 解析html网页
html5lib 容错性较好,以浏览器的方式解析文档,生成h5格式的文档 并不常用
# 扩展3 157中文官方手册:https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/
'''
三、Jsonpath
Jsonpath可以理解为匹配解析json的xpath
安装:pip install jsonpath
使用:
1、导入模块 import jsonpath
2、使用jsonpath.jsonpath(jsoncode,’rules’)格式进行匹配
知识点1:$表示根元素,必须要有,和xpath起始处的/一样;
allele = jsonpath.jsonpath(jscode,'$..*')
知识点2:*表示所有
name = jsonpath.jsonpath(jscode,'$.data.comments[*]..name')
知识点3:[索引] jsonpath语法中的索引从0开始;
digg_count = jsonpath.jsonpath(jscode,'$.data.comments[0].digg_count')
知识点4:?() 表示指定数据,过滤出自己选择的内容 @表示指定的对象
elelist = jsonpath.jsonpath(jscode,'$..comments[?(@.zgl)]')
知识点5:jsonpath中比较运算的使用
reply_count = jsonpath.jsonpath(jscode,'$..comments[?(@.reply_count>3)]')
知识点6:获取最后一个数据,也可以使用切片的形式(见完整代码)
lone = jsonpath.jsonpath(jscode,'$..comments[(@.length-1)]')
完整代码示例:
# 输出所有数据内容
allele = jsonpath.jsonpath(jscode,'$..*')
# print(allele)
# 获取commonets中所有用户名 name name = jsonpath.jsonpath(jscode,'$.data.comments[*]..name') # print(name)
# 获取点赞数 digg_count = jsonpath.jsonpath(jscode,'$.data.comments[0].digg_count') # print(digg_count)
# 获取用户id 如果没有指定索引,则获取所有并返回列表
# 可以指定索引得出结果,如[2]获取第三个数据,也可以将所有user_id_list进行遍历 user_id_list = jsonpath.jsonpath(jscode,'$..user.user_id') print(user_id_list)
# [2:10:2] [start:end:step] [开始索引:结束索引:间隔步长] 如下,从索引2开始到10,间隔为2输出
# 类似python中的切片 user_id = jsonpath.jsonpath(jscode,'$..user.user_id')[2:10:2] # print(user_id)
# user中的所有数据 * 表示所有alluser = jsonpath.jsonpath(jscode,'$..user.*')
# print(alluser)
# 包含zgl的数据 ?() 表示指定数据,过滤出自己选择的内容 @表示指定的对象
elelist = jsonpath.jsonpath(jscode,'$..comments[?(@.zgl)]')
# for ele in elelist:
# print(ele['zgl'])
# 获取回复大于3的数据 < > = 等相关的用法 大家可以自己修改数据进行测试
reply_count = jsonpath.jsonpath(jscode,'$..comments[?(@.reply_count>3)]')
# print(reply_count)
# 最后一个数据
# lone = jsonpath.jsonpath(jscode,'$..comments[(@.length-1)]')
lone = jsonpath.jsonpath(jscode,'$..comments[-1:]')
print(lone)
'''
xpath中开头/表示根路径,jsonpath中$表示根路径;
xpath语句中/表示子级,jsonpath中.和[]表示子级;
xpath中..表示父级,使用//表示子孙级,jsonpath中..表示子孙级
xpath中()表示分组,jsonpath中()表示
*在xpath和jsonpath中一样,都表示所有
'''
# http://jsonpath.herokuapp.com/ 测试jsonpath的在线网站
无论是哪一种定位匹配数据的方法,都需要大量的练习,熟能生巧才能游刃有余。