BeautifulSoap基本使用

Beautiful Soup使用

简介

Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.

安装

  1. 在终端输入pip install beautifulsoup4

  2. 然后安装解析器:pip install lxml

Beautiful Soup也能支持其他解析器,这里使用lxml解析器。如果有更改解析器的需要,参看其[官方手册](Beautiful Soup 中文文档)。

快速开始

下面是一段html代码,后面会被多次用到,这段代码称其为文本1

html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
"""
# 将上述文本1的代码放在同一py文件下,运行。观察结果
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.prettify())
print(soup.title.string)
  • 对于文本1,观察可知,标签<html>没有闭合。但是在输出信息中,有</html闭合标签。所以初始化BeautifulSoup对象时,会自动进行修正。

  • prettify()方法是将html文本按标准缩进格式返回。

  • tag.string:获取tag标签的文本内容。如果标签tag的内容中含有其他标签,则返回None

以上仅是一个bs4使用的简单示例。我们从这个示例开始。

对象种类

Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种: Tag , NavigableString , BeautifulSoup , Comment .

  • Tag:是对html中标签(及其内容)和对应方法的封装。

  • NavigableString:字符串常被包含在tag内.Beautiful Soup用 NavigableString 类来包装tag中的字符串。这里注意理解:NavigableString就是对字符串及其方法的封装。

  • BeautifulSoup:对整个html文档内容及其方法的封装。

  • Comment:对注释内容及其方法的封装。Comment 对象是一个特殊类型的 NavigableString 对象。

所以,Beautiful Soup在解析html文档时,按照以上四种类型解析文档,并构成对应的树。
注意,BeautifulSoup在大部分情况下,可以当做Tag来使用。

节点的定位

解析html是为了获取标签中的信息。而要获得目标信息,首先需要定位到目标节点(标签)。

Bs4中,使用tag.标签名的方式,可以定位到tag对象的第一个指定标签名的Tag对象。

示例:分析文本1

from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.title)
print(type(soup.title))
print(soup.title.string)
print(type(soup.title.string))
print(soup.head)
print(soup.p)

运行结果:

<title>The Dormouse's story</title>
<class 'bs4.element.Tag'>
The Dormouse's story
<class 'bs4.element.NavigableString'>
<head><title>The Dormouse's story</title></head>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p></p>

注意节点的类型:

soup.titlebs4.element.Tag类型的对象,soup.title.stringbs4.element.NavigableString类型的对象,其打印结果其实就是一段字符串。

信息提取

上面我们简单介绍了一下,如何定位到某个Tag节点。下面来介绍,如果获取该Tag节点的信息。

由于Tag对象是对标签内容及其方法的封装,可以直接通过.的方式访问其信息。

  • 获取Tag对象的类型
tag.name #获取标签名称:返回当前tag对象的标签名称。如果tag是<p>标签的封装,则返回'p'

  • 获取Tag对象的属性
tag['id']
tag.attrs['id'] #获取Tag节点的属性:这两种方式等价。使用中括号加上属性名
  • 获取Tag对象的文本内容(更具体的说,可以理解为,获取Tag节点的NavigableString子节点)
tag.string # 返回一个NavigableString对象

这个.string需要理解一下,一个Tag对象实际上对应一个标签,但是标签中的内容可以有纯字符串和子标签。这里的纯字符串被解析为NavigableString节点,而子标签被解析为Tag节点。

如果tag只有一个 NavigableString 类型子节点,那么这个tag可以使用 .string 得到子节点,即NavigableString类型子节点。

如果一个tag仅有一个子节点,那么这个tag也可以使用 .string 方法,输出结果与当前唯一子节点的 .string 结果相同。

如果tag包含了多个子节点,tag就无法确定 .string 方法应该调用哪个子节点的内容, .string 的输出结果是 None

  • 获取Tag中的NavigableString对象以及子节点中的NavigableString对象。
#tag.strings   
#tag.stripped_strings #会忽略掉空格或空行。
#这两个方法都返回一个生成器 

for string in soup.strings:
    print(string)

关联选择

有时,不能一步定位到我们想要的节点。而需要先选中某一个节点,再以它为基准选择其子节点,父节点,兄弟节点等。

  • 子节点和子孙节点

tag的.contents属性可以将tag的子节点以列表的方式输出:

tag的.children属性可以将tag的子节点以生成器方式输出:

print(soup.p.contents)
#结果为一列表

for child in soup.p.children:
    print(child)
#结果为多个子节点的单独输出

.contents.children都是获取tag节点的直接子节点。如果要获取tag的所有子孙节点,那么使用.descendants属性。

.descendants 属性可以对所有tag的子孙节点进行先序遍历。


for child in soup.descendants:
    print(child)


  • 父节点和祖先节点

通过 .parent 属性来获取某个元素的父节点.

通过元素的 .parents 属性可以递归得到元素的所有父辈节点.

  • 兄弟节点

看一个简单例子:

sibling_soup = BeautifulSoup("<a><b>text1</b><c>text2</c></b></a>")
print(sibling_soup.prettify())
# <html>
#  <body>
#   <a>
#    <b>
#     text1
#    </b>
#    <c>
#     text2
#    </c>
#   </a>
#  </body>
# </html>

因为标签和标签是同一层:他们是同一个元素的子节点,所以和可以被称为兄弟节点.一段文档以标准格式输出时,兄弟节点有相同的缩进级别.在代码中也可以使用这种关系.

在文档树中,使用 .next_sibling.previous_sibling 属性来查询兄弟节点:

tag.next_sibling
tag.previous_sibling

在上述简单例子中,标签有 .next_sibling 属性,但是没有 .previous_sibling 属性,因为标签在同级节点中是第一个.同理,标签有 .previous_sibling 属性,却没有 .next_sibling 属性:

print(sibling_soup.b.previous_sibling)
# None
print(sibling_soup.c.next_sibling)
# None

例子中的字符串“text1”和“text2”不是兄弟节点,因为它们的父节点不同:

sibling_soup.b.string
# u'text1'

print(sibling_soup.b.string.next_sibling)
# None

值得注意:实际文档中的tag的 .next_sibling.previous_sibling 属性通常是字符串或空白.

下面给出一个html片段:

<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a>,
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>

如果以为第一个标签的 .next_sibling 结果是第二个标签,那就错了,真实结果是第一个标签和第二个标签之间的逗号和换行符:出错的原因是,没有深入理解BeautifulSoup的解析方式,节点应该有四种。

方法选择器(搜索文档树)

前面所讲的节点定位方法,都是基于html文档的结构,这种方式逻辑清晰,但是当文档结构复杂时,会很繁琐。所以接下来介绍更为强大的API。

下面将介绍两个API函数的使用。

find_all( name , attrs , recursive , string , **kwargs )
find( name , attrs , recursive , string , **kwargs )
  • find_all()

参数介绍:

  • name 参数可以查找所有名字为 name 的tag

  • attrs参数可以查找指定属性的tag,其中attrs为一字典,元素为属性名:属性值的键值对。

  • 调用tag的 find_all() 方法时,Beautiful Soup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数 recursive=False .

  • 通过 string 参数可以搜搜文档中的字符串内容.与 name 参数的可选值一样, string 参数接受 字符串 , 正则表达式 , 列表, True .

  • **kwargs参数:如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索,

各个参数的使用::下面的html代码记为文本2

name

html='''
<div class="panel">
    <div class="panel-heading">
        <h4>Hello</h4>
    </div>
    <div class="panel-body">
        <ul class="list" id="list-1">
            <li class="element">Foo</li>
            <li class="element">Bar</li>
            <li class="element">Jay</li>
        </ul>
        <ul class="list list-small" id="list-2">
            <li class="element">Foo</li>
            <li class="element">Bar</li>
        </ul>
    </div>
</div>
'''
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.find_all(name='ul'))
print(type(soup.find_all(name='ul')[0]))

for ul in soup.find_all(name='ul'):
    print(ul.find_all(name='li'))
    
for ul in soup.find_all(name='ul'):
    print(ul.find_all(name='li'))
    for li in ul.find_all(name='li'):
        print(li.string)

运行结果:

[<ul class="list" id="list-1">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jay</li>
</ul>, <ul class="list list-small" id="list-2">
<li class="element">Foo</li>
<li class="element">Bar</li>
</ul>]
<class 'bs4.element.Tag'>
[<li class="element">Foo</li>, <li class="element">Bar</li>, <li class="element">Jay</li>]
[<li class="element">Foo</li>, <li class="element">Bar</li>]
[<li class="element">Foo</li>, <li class="element">Bar</li>, <li class="element">Jay</li>]
Foo
Bar
Jay
[<li class="element">Foo</li>, <li class="element">Bar</li>]
Foo
Bar



  • 从上可知,find_all会将查询到的节点以列表形式返回。列表中的元素为bs4.element.Tag类型。

attrs

from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.find_all(attrs={'id': 'list-1'}))
print(soup.find_all(attrs={'name': 'elements'})) #还可以写成一下等价形式


print(soup.find_all(id='list-1'))
print(soup.find_all(class_='element')) #因为class为python关键字,所以使用
                                        #class_来加以区分。

string

from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.find_all(string=re.compile('link')))
print(soup.find_all(text=re.compile('link')))#这两条语句等价


这里的string=...text=...等价。使用text=...是,将需要匹配的信息作为要查找的属性。

  • find()方法与find_all()类似,不介绍。

CSS选择器

如果熟悉Web开发,也可以使用CSS选择器来定位节点。
使用select()方法。tag.select(),选择tag下的指定节点。

文本2中的html源码为示例:

html='''
<div class="panel">
    <div class="panel-heading">
        <h4>Hello</h4>
    </div>
    <div class="panel-body">
        <ul class="list" id="list-1">
            <li class="element">Foo</li>
            <li class="element">Bar</li>
            <li class="element">Jay</li>
        </ul>
        <ul class="list list-small" id="list-2">
            <li class="element">Foo</li>
            <li class="element">Bar</li>
        </ul>
    </
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.select('.panel .panel-heading'))
print(soup.select('ul li'))
print(soup.select('#list-2 .element'))
print(type(soup.select('ul')[0]))

运行结果:

[<div class="panel-heading">
<h4>Hello</h4>
</div>]
[<li class="element">Foo</li>, <li class="element">Bar</li>, <li class="element">Jay</li>, <li class="element">Foo</li>, <li class="element">Bar</li>]
[<li class="element">Foo</li>, <li class="element">Bar</li>]
<class 'bs4.element.Tag'>

select方法会将定位到的节点以列表形式返回,列表中的元素为bs4.element.Tag类型。

参考资料

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值