Python爬虫(三)- BeautifulSoup 安装与使用教程

发布于:2025-02-11 ⋅ 阅读:(92) ⋅ 点赞:(0)


前言

本篇文章将详细介绍BeautifulSoup的基本概念、安装步骤、解析器的比较,以及如何使用该库解析HTML文档。我们将通过实际示例演示如何使用BeautifulSoup获取标签、属性和内容,帮助读者快速上手并应用于实际项目中。


一、简介及安装

1. 简介

BeautifulSoup 是一个用于解析 HTML 和 XML 文档的 Python 库,它帮助你从网页中提取数据。这个库非常灵活,并且可以与多种不同的解析器一起工作,比如 Python 内置的 html.parserlxml 或者 html5lib

使用版本:

python requests bs4 beautifulsoup4 soupsieve lxml
版本 3.8.5 2.31.0 0.0.2 4.12.3 2.6 4.9.3

2. 安装 BeautifulSoup 相关库

2.1 安装

执行如下命令安装 BeautifulSoup 相关库。

pip install bs4==0.0.2 -i https://mirrors.aliyun.com/pypi/simple/
pip install beautifulsoup4==4.12.3 -i https://mirrors.aliyun.com/pypi/simple/
pip install soupsieve==2.6 -i https://mirrors.aliyun.com/pypi/simple/
pip install lxml==4.9.3 -i https://mirrors.aliyun.com/pypi/simple/

2.2 检查安装是否成功

执行如下命令查看 BeautifulSoup 相关库是否安装成功。

pip show bs4
pip show beautifulsoup4
pip show soupsieve
pip show lxml

安装成功如下图所示。

在这里插入图片描述

3. 几种解析器的比较

解析器 使用方法 优势 劣势
Python 标准库 BeautifulSoup(markup, "html.parser") - Python 的内置标准库
- 执行速度较快
- 容错能力强
- 速度没有 lxml 快,容错性没有 html5lib 强
lxml HTML 解析器 BeautifulSoup(markup, "lxml") - 速度快
- 容错能力强
- 需要额外的 C 依赖
lxml XML 解析器 BeautifulSoup(markup, ["lxml-xml"])
BeautifulSoup(markup, "xml")
- 速度快
- 唯一支持 XML 的解析器
- 需要额外的 C 依赖
html5lib BeautifulSoup(markup, "html5lib") - 最好的容错性
- 以浏览器的方式解析文档
- 生成 HTML5 格式的文档
- 速度慢
- 需要额外的 Python 依赖

二、使用 BeautifulSoup 解析文档

1. 准备一个 HTML 文档

准备一个名称为 test.html的 HTML 文档,以便随后使用 BeautifulSoup 进行解析。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>测试 HTML 文档</title>
</head>
<body>

<h1 id="main-title">欢迎来到测试页面</h1>
<p class="intro">这是一个用于测试的 HTML 文档。</p>

<!-- 第一节 -->
<section id="section1">
    <h2>第一节标题</h2>
    <p id="para1" class="content">这里是第一节的内容。</p>
    <a href="https://example.com" id="link1" class="external-link">外部链接 1</a>
    <img src="image1.jpg" alt="图片描述 1" id="img1" class="image">
</section>

<!-- 第二节 -->
<section id="section2">
    <h2>第二节标题</h2>
    <p id="para2" class="content">这是第二节的内容。</p>
    <a href="https://example.org" id="link2" class="external-link">外部链接 2</a>
    <img src="image2.jpg" alt="图片描述 2" id="img2" class="image">
</section>

<!-- 包含重复元素的部分 -->
<div id="repeated-elements">
    <h3>重复元素部分</h3>

    <!-- 重复的段落 -->
    <p id="para-repeat-1" class="content repeated">这段文字会在文档中多次出现。</p>
    <p id="para-repeat-2" class="content repeated">这段文字也会在文档中多次出现。</p>

    <!-- 重复的链接 -->
    <a href="https://example.com" id="link-repeat-1" class="external-link repeated">重复的外部链接 1</a>
    <a href="https://example.org" id="link-repeat-2" class="external-link repeated">重复的外部链接 2</a>

    <!-- 重复的图像 -->
    <img src="image1.jpg" alt="重复的图片 1" id="img-repeat-1" class="image repeated">
    <img src="image2.jpg" alt="重复的图片 2" id="img-repeat-2" class="image repeated">

    <!-- 再次出现的重复元素 -->
    <p id="para-repeat-3" class="content repeated">这段文字会在文档中多次出现。</p>
    <a href="https://example.com" id="link-repeat-3" class="external-link repeated">重复的外部链接 1</a>
    <img src="image1.jpg" alt="重复的图片 1" id="img-repeat-3" class="image repeated">
</div>

</body>
</html>

2. 使用 BeautifulSoup 解析本地 HTML 文档

解析文档时,可以将HTML文档传入 BeautifulSoup 的构造方法,可以传递一段字符串或一个HTML文件作为输入。

解析过程:首先,BeautifulSoup 会自动将文档转换为 Unicode,并且 HTML 中的实体也会被转换成 Unicode 编码;然后,BeautifulSoup 会选择最合适的解析器来解析文档,如果指定了解析器那么 Beautiful Soup 会选择指定的解析器来解析文档。

2.1 将 HTML 文件作为输入

在 Python 中,with 语句用于封装执行某个操作前后的代码块,通常用于确保资源能够被正确地清理或释放。它最常用于文件操作,可以保证文件在使用完毕后自动关闭,即使期间发生了异常也不会忘记关闭文件。

from bs4 import BeautifulSoup

# 从文件中读取 HTML 文档并解析
with open(file="test.html", mode='r', encoding='utf-8') as fp:
    # 创建一个 BeautifulSoup 对象来解析 HTML 文档
    # markup 参数传入文件对象 fp,BeautifulSoup 将会读取该文件的内容进行解析。
    # features 参数指定使用的解析器,这里是 'html.parser',它是 Python 内置的标准库解析器。
    soup = BeautifulSoup(markup=fp, features='html.parser')

    # 打印 HTML 文档内容,且是经过美化的
    print(soup.prettify())

打印的部分结果为:

在这里插入图片描述

2.2 将字符串作为输入

from bs4 import BeautifulSoup

# 直接传递字符串进行解析
soup = BeautifulSoup(markup="<html>a web page</html>", features='html.parser')
print(soup.prettify())

打印的结果为:

<html>
 a web page
</html>

3. 对象种类

BeautifulSoup 将复杂的 HTML 文档转换成由 Python 对象构成的树形结构,主要包括以下四种类型的对象:Tag, NavigableString, BeautifulSoup, 和 Comment

  • Tag:表示标签。
  • NavigableString:表示标签之间的文本内容。
  • BeautifulSoup:表示整个解析后的文档。
  • Comment:一种特殊的 NavigableString,表示 HTML 中标签之间的注释。

3.1 Tag(获取标签)

Tag 对象与 XML 或 HTML 原生文档中的标签相同。

3.1.1 获取标签及内容
from bs4 import BeautifulSoup

with open(file="test.html", mode='r', encoding='utf-8') as fp:
    soup = BeautifulSoup(markup=fp, features='html.parser')
    # 查找并获取HTML文档中的第一个 <h2> 标签。
    h2_tag = soup.h2
    # 打印找到的 <h2> 标签及其内部的内容(如果有的话)。如果没有找到 <h2> 标签,则打印None。
    print(h2_tag)
    # 打印 <h2> 标签的数据类型。如果是有效的标签,它将是bs4.element.Tag 类型;如果没有找到标签,则是NoneType。
    print(type(h2_tag))

打印的结果为:

<h2>第一节标题</h2>
<class 'bs4.element.Tag'>
3.1.2 获取标签名

每个 Tag 都有一个名字和多个属性,名称可以通过 .name 访问或修改。

from bs4 import BeautifulSoup

with open(file="test.html", mode='r', encoding='utf-8') as fp:
    soup = BeautifulSoup(markup=fp, features='html.parser')
    # 查找并获取HTML文档中的第一个 <h2> 标签。
    h2_tag = soup.h2
    print(h2_tag)
    # 打印标签名
    print(h2_tag.name)
    # 修改标签名后打印,并不会修改原HTML文档中的标签名
    h2_tag.name = 'h3'
    print(h2_tag.name)

打印的结果为:

<h2>第一节标题</h2>
h2
h3
3.1.3 标签属性操作

一个 HTML 或 XML 标签可能有许多属性,可以像处理字典一样处理这些属性。

from bs4 import BeautifulSoup

with open(file="test.html", mode='r', encoding='utf-8') as fp:
    soup = BeautifulSoup(markup=fp, features='html.parser')
    # 查找并获取HTML文档中的第一个 img 标签。
    img_tag = soup.img
    # 打印标签内容
    print(img_tag)
    # 以字典方式获取所有属性的键和值
    print(img_tag.attrs)
    # 获取img标签中属性alt的值,如果属性不存在报KeyError错误
    print(img_tag['alt'])
    # 获取img标签中属性alt的值,如果属性不存在返回None
    print(img_tag.get('alt'))
    print(img_tag.attrs.get('alt'))
    img_tag['alt'] = '修改后的alt'
    # 打印修改后的alt的值
    print(img_tag['alt'])
    # 删除属性
    del img_tag['alt']
    # 打印标签内容
    print(img_tag)

打印的结果为:

<img alt="图片描述 1" class="image" id="img1" src="image1.jpg"/>
{'src': 'image1.jpg', 'alt': '图片描述 1', 'id': 'img1', 'class': ['image']}
图片描述 1
图片描述 1
图片描述 1
修改后的alt
<img class="image" id="img1" src="image1.jpg"/>

3.2 获取标签之间的内容

from bs4 import BeautifulSoup

with open(file="test.html", mode='r', encoding='utf-8') as fp:
    soup = BeautifulSoup(markup=fp, features='html.parser')
    # 查找并获取HTML文档中的第一个 h1 标签。
    h1_tag = soup.h1
    # 打印标签内容
    print(h1_tag)
    # 获取标签之间的文本内容,如果标签中还有标签,则返回None
    print(h1_tag.string)
    # 获取标签之间的文本内容,如果标签中还有标签,则获取所有二级标签的内容(以换行分隔)
    print(h1_tag.get_text())

打印的结果为:

<h1 id="main-title">欢迎来到测试页面</h1>
欢迎来到测试页面
欢迎来到测试页面

3.3 获取标签之间的注释内容

from bs4 import BeautifulSoup

markup = "<b><!-- Hey, buddy. Want to buy a used parser? --></b>"

soup = BeautifulSoup(markup=markup, features='html.parser')

# 获取 <b> 标签
b_tag = soup.b
# 打印 <b> 标签的内容
print(b_tag)
# 获取 <b> 标签中的字符串内容(在这里是一个注释)
comment = b_tag.string
# 打印 comment 的类型
print(type(comment))
# 打印 comment 的内容
print(comment)

打印的结果为:

<b><!-- Hey, buddy. Want to buy a used parser? --></b>
<class 'bs4.element.Comment'>
 Hey, buddy. Want to buy a used parser? 

三、获取标签

from bs4 import BeautifulSoup

with open(file="test.html", mode='r', encoding='utf-8') as fp:
    soup = BeautifulSoup(markup=fp, features='html.parser')
    print(下面的示例代码)

以下内容都基于此soup对象。

1. 获取HTML文档中的标签及内容

1.1 用法

soup.标签名

1.2 示例

获取HTML文档中的第一个head标签。

soup.head

在这里插入图片描述

获取HTML文档中的第一个head标签下的第一个title标签。

soup.head.title

在这里插入图片描述

获取HTML文档中的第一个img标签。

soup.img

在这里插入图片描述

2. 获取标签属性

2.1 用法

获取指定标签所有属性的键和值。

soup.标签名.attrs

获取指定标签的指定属性键的值。如果属性键不存在会报KeyError错误。

soup.标签名['属性键']

获取指定标签的指定属性键的值。如果属性键不存在返回None

soup.标签名.get('属性键')
soup.标签名.attrs.get('属性键')

2.2 示例

获取第一个img标签所有属性的键和值。

soup.img.attrs

在这里插入图片描述

获取第一个img标签的alt属性的值。

soup.img['alt']
soup.img.get('alt')
soup.img.attrs.get('alt')

在这里插入图片描述

3. 获取标签间的内容

3.1 用法

获取标签之间的文本内容,如果标签中还有标签,则返回None。

soup.标签名.string

获取标签之间的文本内容,返回可迭代列表。

soup.标签名.strings

获取标签之间的文本内容,返回可迭代列表,可以去除多余空白内容

soup.标签名.stripped_strings

获取标签之间的文本内容,如果标签中还有标签,则获取所有二级标签的内容(以换行分隔)。

soup.标签名.get_text()

3.2 示例

获取标签h1之间的文本内容,如果标签中还有标签,则返回None。

soup.h1.string

循环获取标签section之间的文本内容。

for string in soup.section.strings:
    print(string)

在这里插入图片描述

循环获取标签section之间的文本内容,去除多余空白内容

for string in soup.section.stripped_strings:
    print(string)

在这里插入图片描述

获取标签h1之间的文本内容,如果标签中还有标签,则获取所有二级标签的内容(以换行分隔)。

soup.h1.get_text()

4. 获取指定标签的子节点标签及内容

4.1 用法

获取指定标签的所有子节点标签及内容。

soup.标签名.contents

获取指定标签的所有子节点标签及内容,返回一个可迭代列表。

soup.标签名.children

递归获取指定标签的所有子节点标签及内容。

soup.标签名.descendants

获取指定标签的指定索引子节点标签及内容。

soup.标签名.contents[索引]

4.2 示例

获取head标签的子节点。

soup.head.contents
soup.head.contents[1]
soup.head.contents[1].name

在这里插入图片描述

获取head标签的子节点。

for tag in soup.head.children:
    print(tag)

在这里插入图片描述

递归获取head标签的子节点。

for tag in soup.head.descendants:
    print(tag)

在这里插入图片描述

5. 获取指定标签的父节点

5.1 用法

获取指定标签的父节点。

soup.标签名.parent

递归获取指定标签的父节点。

soup.标签名.parents

5.2 示例

获取title标签的父节点。

soup.title.parent

在这里插入图片描述

递归获取title标签的父节点。

for parent in soup.title.parents:
    print(parent)

在这里插入图片描述

6. 查找所有符合条件的标签

find_all() 方法是 Beautiful Soup 中最常用的搜索方法之一,它可以在当前标签的所有子节点中查找符合条件的标签。该方法的基本语法如下:

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

6.1 name 参数

  • 功能:传入一个值用于查找所有名称为 name 的标签。所有文本内容会被忽略,因为它们不匹配标签名称。
  • 用法示例
    soup.find_all("title")
    

6.2 attrs 参数

  • 功能:如果在动态参数中出现未识别的参数名,Beautiful Soup 会将该参数视为标签属性进行搜索。例如,若搜索参数中包含 id,则会搜索每个标签的 id 属性。
  • 用法示例
    soup.find_all(id='link2')
    

6.3 string 参数

  • 功能:通过 string 参数可以搜索文档中的字符串内容。该参数接受字符串、正则表达式、列表、函数或 True
  • 用法示例
    soup.find_all(string="Elsie")
    

6.4 limit 参数

  • 功能:限制返回结果的数量,类似于 SQL 中的 LIMIT 关键字。当搜索到的结果数量达到限制时,停止搜索并返回结果。
  • 用法示例
    soup.find_all("a", limit=2)
    

6.5 recursive 参数

  • 功能:控制是否检索所有子孙节点。如果设置为 False,则只搜索直接子节点。
  • 用法示例
    soup.html.find_all("title", recursive=False)
    

使用示例

以下是一些使用 find_all() 方法的示例:

from bs4 import BeautifulSoup

# 示例 HTML 文档
html_doc = """
<html>
 <head>
  <title>The Dormouse's story</title>
 </head>
 <body>
  <p class="title"><b>The Dormouse's story</b></p>
  <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
 </body>
</html>
"""

soup = BeautifulSoup(html_doc, 'html.parser')

# 查找所有 <title> 标签
titles = soup.find_all("title")
print(titles)

# 查找所有具有 class 为 "title" 的 <p> 标签
paragraphs = soup.find_all("p", "title")
print(paragraphs)

# 查找所有 <a> 标签
links = soup.find_all("a")
print(links)

# 查找特定 id 的标签
specific_link = soup.find_all(id="link2")
print(specific_link)

# 使用正则表达式查找字符串
import re
sisters = soup.find(string=re.compile("sisters"))
print(sisters)

7. 查找第一个符合条件的标签

在Beautiful Soup 4(bs4)中,find()方法是用于查找文档中第一个符合条件的标签。它非常强大且灵活,可以通过多种方式指定查找条件。以下是find()方法的详解:

7.1 查找标签

使用find()查找第一个匹配的标签:

title_tag = soup.find('title')
print(title_tag)

7.2 使用属性查找

可以通过标签的属性进行查找:

first_sister = soup.find('a', class_='sister')
print(first_sister)

7.3 使用多个属性查找

可以同时指定多个属性:

link = soup.find('a', {'class': 'sister', 'id': 'link2'})
print(link)

7.4 使用文本查找

可以通过标签的文本内容查找:

story_paragraph = soup.find('p', string="Once upon a time there were three little sisters; and their names were")
print(story_paragraph)

7.5 使用正则表达式

可以使用正则表达式进行更复杂的匹配:

import re

regex_link = soup.find('a', href=re.compile(r'example\.com'))
print(regex_link)

7.6 查找子标签

可以在特定标签内查找子标签:

body = soup.find('body')
first_paragraph = body.find('p')
print(first_paragraph)

8. CSS选择器查找标签

在Beautiful Soup 4(bs4)中,select()方法是用于根据CSS选择器查找标签的强大工具。

8.1 使用标签选择

可以直接使用标签名选择元素:

title_tags = soup.select('title')
print(title_tags)

8.2 使用类选择器

使用点(.)选择类:

sister_links = soup.select('.sister')
print(sister_links)

8.3 使用ID选择器

使用井号(#)选择ID:

link1 = soup.select('#link1')
print(link1)

8.4 组合选择器

可以组合选择器来更精确地选择元素:

first_story_paragraph = soup.select('p.story')
print(first_story_paragraph)

8.5 后代选择器

使用空格选择后代元素:

bold_text = soup.select('p b')
print(bold_text)

8.6 子元素选择器

使用大于号(>)选择直接子元素:

direct_children = soup.select('body > p')
print(direct_children)  # 输出: [<p class="title"><b>The Dormouse's story</b></p>, <p class="story">...</p>]

8.7 属性选择器

可以根据属性进行选择:

specific_link = soup.select('a[href="http://example.com/lacie"]')
print(specific_link)

8.8 伪类选择器

可以使用伪类选择器,例如选择第一个元素:

first_sister = soup.select('.sister:first-child')
print(first_sister)

网站公告

今日签到

点亮在社区的每一天
去签到