python之BeautifulSoup库

文章目录

    • 一 什么是BeautifulSoup
    • 二 使用BeautifulSoup
        • 1. 初始化网页源码
        • 2. 节点选择器
            • 2.1 通过html 标签匹配
            • 2.2 关联选择
        • 3. 方法选择器
            • 3.1 方法选择器:find_all()
        • 4. CSS选择器
            • 4.1 CSS选择器:select()
            • 4.2 获取文本:get_text()

一 什么是BeautifulSoup

    BeautifulSoup是python的一个HTML或者XML的解析库,可以通过它来实现对网页的解析,从而获得想要的数据。
     在用BeautifulSoup库进行网页解析时,其实还是要依赖解析器的,python有自己内置的html.parser解析器,Beautiful肯定是支持html.parse解析器的。不过除此之外,还有一些第三方解析器比如lxml,xml以及html5lib,其中lxml在这几个中是比较有优势的,主要体现在解析速度快,容错能力强。所以下面主要是围绕lxml解析器进行记录的。
     使用前请检查安装lxml库BeautifulSoup库。

二 使用BeautifulSoup

1. 初始化网页源码

大多数情况下我们获取到的HTML都是字符串,而且格式不一,这很不方便我们从中提取数据,这时就可以将HTML源码传给BeautifulSoup库初始化,进而将字符串的HTML转换成BeautifulSoup对象。如下

from bs4 import BeautifulSoup

html = '''



    
    BeautifulSoup学习


经典老歌

经典老歌列表

如上,BeautifulSoup()接受两个参数,一个是html字符串源码,第二个是要使用的解析器。我这里使用的是lxml解析器,经过初始化后的html字符串就转换成了树结构的BeautifulSoup对象了。BeautifulSoup在初始化的同时还会自动补全缺失的标签。如上html字符串结尾缺少了/div,/body标签,初始化后就会自动补上,sp.prettify()控制缩进,美化html。结果如下

>>>
<class 'bs4.BeautifulSoup'>

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>BeautifulSoup学习</title>
</head>
<body>
<div id="song list">
<h2 class="title">经典老歌</h2>
<p class="introduction">
经典老歌列表
</p>
<ul class="list-group" id="list">
<li data-view="2">一路上有你</li>
<li data-view="7">
<a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
</li>
<li class="active" data-view="4">
<a href="/3.mp3" singer="齐秦">往事随风</a>
</li>
<li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>
<li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本</a></li>
<li data-view="5">
<a href="/6.mp3" singer="邓丽君">但愿人长久</a>
</li>
</ul></div></body></html>

2. 节点选择器

节点选择器是通过一些html标签来提取对应的内容,如p,a,li等标签。如下

2.1 通过html 标签匹配
#初始化html
sp = BeautifulSoup(html, 'lxml')
sp.prettify()
# 通过html标签匹配
print(type(sp.title))
print(sp.title)
print(type(sp.h2))
print(sp.h2)

输出:

>>>
<class 'bs4.element.Tag'>
<title>BeautifulSoup学习</title>
<class 'bs4.element.Tag'>
<h2 class="title">经典老歌</h2>

如上,可以通过html标签元素来提取信息,也称节点选择。可以看到返回的结果类型都是bs4.element.Tag,凡是返回Tag类型的数据,它们都有如下属性

  • name: 返回节点的名字
  • string:获取节点内容,但仅限于节点里面没有其他节点的时候,返回类型NavigableString,可通过str()函数转为字符串。
  • attrs: 获取节点属性,字典形式返回
    如下
#初始化html
sp = BeautifulSoup(html, 'lxml')
sp.prettify()
# 通过html标签匹配
print("节点html:", sp.h2)
print("节点名字:", sp.h2.name)
print("节点内容:", sp.h2.string)
print("节点属性:", sp.h2.attrs)
>>>
节点html: <h2 class="title">经典老歌</h2>
节点名字: h2
节点内容: 经典老歌
节点属性: {'class': ['title']}

上面提取的都是只有单个节点的情况,而且节点里面没有其他节点。下面看看有多个同名节点,节点里面又嵌套其它节点的情况
#初始化html
sp = BeautifulSoup(html, 'lxml')
sp.prettify()
# 通过html标签匹配
print(sp.a)

输出结果

>>>
<a href="/2.mp3" singer="任贤齐">沧海一声笑</a>

python之BeautifulSoup库_第1张图片
可以看到输出结果只有第一个a节点,其他a节点并没有输出,所有通过这种方式只能提取出第一个符合的内容。


再看看节点里面有嵌套其它节点时,用string属性获取文本内容。如下

#初始化html
sp = BeautifulSoup(html, 'lxml')
sp.prettify()
# 通过html标签匹配
print("节点div的html: ", sp.div)
print(type(sp.div))
print("string获取内容:", sp.div.string)
>>>
节点div的html:  <div id="song list">
<h2 class="title">经典老歌</h2>
<p class="introduction">
经典老歌列表
</p>
<ul class="list-group" id="list">
<li data-view="2">一路上有你</li>
<li data-view="7">
<a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
</li>
<li class="active" data-view="4">
<a href="/3.mp3" singer="齐秦">往事随风</a>
</li>
<li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>
<li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本</a></li>
<li data-view="5">
<a href="/6.mp3" singer="邓丽君">但愿人长久</a>
</li>
</ul></div>

<class 'bs4.element.Tag'>
string获取内容: None

可以看到div节点里面还包含了其它节点,再用string获取内容时返回是None,即无法获取

2.2 关联选择

从上面可以看到,当a节点不止一个时,用节点选择的方法只能返回第一个匹配的值,这时候我们可以用一些关联选择的方法来进行更多的匹配,如下

  • 兄弟节点
    next_sibling: 获取节点的下一个兄弟节点
    next_siblings: 获取节点后面的兄弟节点,返回生成器类型
    previous_sibling: 获取节点的上一个兄弟节点
    previous_siblings: 获取节点前面的兄弟节点,返回生成器类型
#初始化html
sp = BeautifulSoup(html, 'lxml')
sp.prettify()
# 通过html标签匹配
print("获取第一个li节点:", sp.li)
print(type(sp.li.next_sibling))
print("获取第一个li节点的下一个兄弟节点:", sp.li.next_sibling)
print(type(sp.li.next_siblings))
print("获取第一个li节点后面的兄弟节点:", sp.li.next_siblings)
for i in sp.li.next_siblings:
    print(i)
获取第一个li节点: <li data-view="2">一路上有你</li>
<class 'bs4.element.Tag'>
获取第一个li节点的下一个兄弟节点: <li>这是测试</li>
<class 'generator'>
获取第一个li节点后面的兄弟节点: <generator object PageElement.next_siblings at 0x000001D4AD66E5C8>
<li>这是测试</li>


<li data-view="7">
<a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
</li>


<li class="active" data-view="4">
<a href="/3.mp3" singer="齐秦">往事随风</a>
</li>


<li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>


<li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本</a></li>


<li data-view="5">
<a href="/6.mp3" singer="邓丽君">但愿人长久</a>
</li>

注意next_sibling 返回的是一个Tag类型的对象,而next_siblings 返回的是一个生成器,可以通过遍历获取数据。(由于我是手敲的html,有手动换行,所以兄弟节点存在空的)


  • 子节点和子孙节点
    contents或者children: 获取直接子节点,centents返回列表,children返回生成器类型
    descendants: 获取所有子孙节点,返回生成器类型

获取ul节点的子节点:

#初始化html
sp = BeautifulSoup(html, 'lxml')
sp.prettify()
# 通过html标签匹配
for i in enumerate(sp.ul.contents):
    print(i)
>>>
(0, '\n')
(1, <li data-view="2">一路上有你</li>)
(2, <li>这是测试</li>)
(3, '\n')
(4, <li data-view="7">
<a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
</li>)
(5, '\n')
(6, <li class="active" data-view="4">
<a href="/3.mp3" singer="齐秦">往事随风</a>
</li>)
(7, '\n')
(8, <li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>)
(9, '\n')
(10, <li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本</a></li>)
(11, '\n')
(12, <li data-view="5">
<a href="/6.mp3" singer="邓丽君">但愿人长久</a>
</li>)
(13, '\n')

可以看到ul里面的a节点是没有被单独提取出来的,而是嵌套在li节点里面的,以为contents获取的是直接子节点。

获取ul节点的子孙节点:

#初始化html
sp = BeautifulSoup(html, 'lxml')
sp.prettify()
# 通过html标签匹配
print(type(sp.ul.descendants))
for i in enumerate(sp.ul.descendants):
    print(i)
<class 'generator'>
(0, '\n')
(1, <li data-view="2">一路上有你</li>)
(2, '一路上有你')
(3, <li>这是测试</li>)
(4, '这是测试')
(5, '\n')
(6, <li data-view="7">
<a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
</li>)
(7, '\n')
(8, <a href="/2.mp3" singer="任贤齐">沧海一声笑</a>)
(9, '沧海一声笑')
(10, '\n')
(11, '\n')
(12, <li class="active" data-view="4">
<a href="/3.mp3" singer="齐秦">往事随风</a>
</li>)
(13, '\n')
(14, <a href="/3.mp3" singer="齐秦">往事随风</a>)
(15, '往事随风')
(16, '\n')
(17, '\n')
(18, <li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>)
(19, <a href="/4.mp3" singer="beyond">光辉岁月</a>)
(20, '光辉岁月')
(21, '\n')
(22, <li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本</a></li>)
(23, <a href="/5.mp3" singer="陈慧琳">记事本</a>)
(24, '记事本')
(25, '\n')
(26, <li data-view="5">
<a href="/6.mp3" singer="邓丽君">但愿人长久</a>
</li>)
(27, '\n')
(28, <a href="/6.mp3" singer="邓丽君">但愿人长久</a>)
(29, '但愿人长久')
(30, '\n')
(31, '\n')

  • 父节点和祖先节点
    parent:返回节点的父节点,返回Tag类型
    parents: 返回节点的祖先节点,返回生成器类型

返回ul节点的父节点:

#初始化html
sp = BeautifulSoup(html, 'lxml')
sp.prettify()
# 通过html标签匹配
print(type(sp.ul.parent))
print(sp.ul.parent.name)
>>>
<class 'bs4.element.Tag'>
div

获取ul节点的祖先节点:

#初始化html
sp = BeautifulSoup(html, 'lxml')
sp.prettify()
# 通过html标签匹配
print(type(sp.ul.parents))
for i in sp.ul.parents:
    print(i.name)
<class 'generator'>
div
body
html
[document]

3. 方法选择器

上面讲的都是通过html元素标签来提取数据,对于一些结构简单的html使用是很直接,很方便的,但是在html比较复杂的时候,要想准确的提取出数据还是比较繁琐的,这时候就得用上方法选择器了,具体如下。

3.1 方法选择器:find_all()

find_all顾名思义就是查找所有满足条件的值,用法如下

  • find_all(name, attrs, recursive, text, **kargs ), 返回列表
    name: 根据节点名称来选择,传入形式name=value
    attrs: 根据属性来选择,attrs值为字典形式
    recursive: 限定直接子节点
    text: 根据文本来选择,传入形式是字符串,可以是正则表达式。

根据节点名称name选择

#初始化html
sp = BeautifulSoup(html, 'lxml')
sp.prettify()
# 通过html标签匹配
a = sp.find_all(name='a')#提取a节点信息
print(a)
>>>
[<a href="/2.mp3" singer="任贤齐">沧海一声笑</a>, <a href="/3.mp3" singer="齐秦">往事随风</a>, <a href="/4.mp3" singer="beyond">光辉岁月</a>, <a href="/5.mp3" singer="陈慧琳">记事本</a>, <a href="/6.mp3" singer="邓丽君">但愿人长久</a>]

可以看到,find_all是匹配出里所有的a节点信息,不像节点选择器则匹配第一个满足条件的值。

根据属性attrs选择

#初始化html
sp = BeautifulSoup(html, 'lxml')
sp.prettify()
# 通过html标签匹配
title = sp.find_all(attrs={'class': 'title'})
print(title)
>>>
[<h2 class="title">经典老歌</h2>]

根据文本text选择

#初始化html
sp = BeautifulSoup(html, 'lxml')
sp.prettify()
# 通过html标签匹配
h2 = sp.find_all(text=r'经典老歌')
print(h2)
>>>
['经典老歌']

4. CSS选择器

除了上面的节点选择器和方法选择器外,还有一个CSS选择器,如果对CSS比较熟悉的话也是可以此方法来进行数据提取的。CSS选择器主要是通过调用select()方法来实现,具体如下。

4.1 CSS选择器:select()
  • select(‘css选择器’) : 参数是CSS选择器,返回列表形式
#初始化html
sp = BeautifulSoup(html, 'lxml')
sp.prettify()
# 通过html标签匹配
a = sp.select('#list a')#CSS选择器提取歌单
for i, j in enumerate(a):
    print(i, j)
>>>
0 <a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
1 <a href="/3.mp3" singer="齐秦">往事随风</a>
2 <a href="/4.mp3" singer="beyond">光辉岁月</a>
3 <a href="/5.mp3" singer="陈慧琳">记事本</a>
4 <a href="/6.mp3" singer="邓丽君">但愿人长久</a>

上面选择器传入了两个值,一个是 #list 提取 id=list 的html,后面的 a 是在 id=list 的结果里面再提取a节点的内容。

4.2 获取文本:get_text()

上面说过,string只能在节点内没有其他节点的时候获取文本内容,而get_text()方法会获取所有文本内容,如下

#初始化html
sp = BeautifulSoup(html, 'lxml')
sp.prettify()
# 通过html标签匹配
li = sp.select('li')#CSS选择器提取歌单
for i in li:
    print("get_text获取文本: ", i.get_text().strip())
    print("string获取文本: ", i.string)
>>>
get_text获取文本:  一路上有你
string获取文本:  一路上有你
get_text获取文本:  这是测试
string获取文本:  这是测试
get_text获取文本:  沧海一声笑
string获取文本:  None
get_text获取文本:  往事随风
string获取文本:  None
get_text获取文本:  光辉岁月
string获取文本:  光辉岁月
get_text获取文本:  记事本
string获取文本:  记事本
get_text获取文本:  但愿人长久
string获取文本:  None

可以看到,当节点里面没有嵌套其他节点时,get_text()和string 都能获取文本内容,实现的效果是一样的,但当节点里面嵌套有其他节点是,string则无法获取,返回None,而get_text()则还能正常提取。

你可能感兴趣的:(python爬虫,python,beautifulsoup,html解析)