网页元素定位

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的在线网站

无论是哪一种定位匹配数据的方法,都需要大量的练习,熟能生巧才能游刃有余。

你可能感兴趣的