【Python模块】9.beautifulsoup4

部署运行你感兴趣的模型镜像

BeautifulSoup4 系统学习指南

BeautifulSoup4(简称BS4)是Python中用于解析HTML/XML文档的库,能将复杂的HTML结构转换为树形结构,方便遍历、查找和修改节点。本文将从基础安装核心初始化节点查找节点遍历节点操作辅助方法六个维度系统梳理,并结合案例和表格详解所有核心方法。

一、基础准备

1. 安装

# 安装核心库
pip install beautifulsoup4
# 安装解析器(推荐lxml,速度快、兼容性好)
pip install lxml
# 备选解析器:html.parser(Python内置)、html5lib(兼容性最强)

2. 解析器对比

解析器优点缺点使用方式
html.parserPython内置、无需额外安装对残缺HTML支持一般BeautifulSoup(doc, 'html.parser')
lxml速度最快、解析效率高、容错性好需要额外安装BeautifulSoup(doc, 'lxml')
html5lib兼容所有残缺HTML、符合W3C标准速度最慢BeautifulSoup(doc, 'html5lib')

3. 初始化示例

先定义一个测试HTML文档(后续所有案例均基于此):

from bs4 import BeautifulSoup

# 测试HTML文档
html_doc = """
<html>
<head><title>测试页面</title></head>
<body>
<p class="title"><b>测试标题</b></p>
<p class="content">测试内容1</p>
<p class="content">测试内容2</p>
<a href="https://example1.com" class="link">链接1</a>
<a href="https://example2.com" class="link">链接2</a>
<div id="container">
    <p>容器内的段落</p>
</div>
</body>
</html>
"""

# 初始化BeautifulSoup对象
soup = BeautifulSoup(html_doc, 'lxml')
# 格式化输出(便于查看结构)
print(soup.prettify())

二、核心:节点查找方法

BS4的核心能力是查找节点,主要分为内置查找方法CSS选择器两大类,覆盖所有查找场景。

1. find_all():查找所有匹配节点

语法格式
soup.find_all(name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs)
参数说明
参数类型说明
name字符串/列表/正则/函数匹配节点名称(如p['p','a']re.compile('^p')
attrs字典匹配节点属性(如{'class':'content'}
recursive布尔值是否递归查找子节点(默认True,False仅查找直接子节点)
text字符串/正则/列表/函数匹配节点的文本内容
limit整数限制返回结果的数量(默认返回所有)
**kwargs-直接传入属性名(如class_='content',注意class加下划线避免关键字冲突)
案例
import re

# 案例1:查找所有p标签
all_p = soup.find_all('p')
print("所有p标签:", [p.text for p in all_p])  # ['测试标题', '测试内容1', '测试内容2', '容器内的段落']

# 案例2:查找class为content的p标签(两种方式)
# 方式1:kwargs
content_p1 = soup.find_all('p', class_='content')
# 方式2:attrs字典
content_p2 = soup.find_all('p', attrs={'class': 'content'})
print("class=content的p标签:", [p.text for p in content_p1])  # ['测试内容1', '测试内容2']

# 案例3:查找href属性包含example的a标签(正则)
a_reg = soup.find_all('a', href=re.compile('example'))
print("href含example的a标签:", [a['href'] for a in a_reg])  # ['https://example1.com', 'https://example2.com']

# 案例4:查找文本包含"测试"的节点(不限标签)
text_node = soup.find_all(text=re.compile('测试'))
print("文本含测试的节点:", text_node)  # ['测试页面', '测试标题', '测试内容1', '测试内容2', '容器内的段落']

# 案例5:限制返回前2个p标签
limit_p = soup.find_all('p', limit=2)
print("前2个p标签:", [p.text for p in limit_p])  # ['测试标题', '测试内容1']

2. find():查找第一个匹配节点

语法格式
soup.find(name=None, attrs={}, recursive=True, text=None, **kwargs)

(参数与find_all()完全一致,区别是仅返回第一个匹配节点,无limit参数)

案例
# 查找第一个class为title的p标签
first_title_p = soup.find('p', class_='title')
print("第一个title的p标签:", first_title_p.text)  # 测试标题

# 查找第一个a标签的href属性
first_a_href = soup.find('a')['href']
print("第一个a标签的href:", first_a_href)  # https://example1.com

3. 快捷查找方法(find_xxx系列)

BS4提供了一系列快捷方法,本质是find/find_all的封装,覆盖父子、兄弟节点查找场景:

方法名作用等价写法
find_parent()查找直接父节点node.find_parents(limit=1)
find_parents()查找所有祖先节点-
find_next_sibling()查找下一个兄弟节点node.find_next_siblings(limit=1)
find_previous_sibling()查找上一个兄弟节点node.find_previous_siblings(limit=1)
find_next_siblings()查找后续所有兄弟节点-
find_previous_siblings()查找前面所有兄弟节点-
find_next()查找后续第一个匹配节点(不限层级)node.find_all_next(limit=1)
find_all_next()查找后续所有匹配节点-
find_previous()查找前面第一个匹配节点node.find_all_previous(limit=1)
find_all_previous()查找前面所有匹配节点-
find_child()查找直接子节点(BS4.7+)node.find_all(recursive=False, limit=1)
find_children()查找所有直接子节点(BS4.7+)node.find_all(recursive=False)
案例
# 定位第一个content的p标签
content_p = soup.find('p', class_='content')

# 案例1:查找该节点的父节点(body)
parent_node = content_p.find_parent()
print("父节点名称:", parent_node.name)  # body

# 案例2:查找该节点的上一个兄弟节点(class=title的p标签)
prev_sibling = content_p.find_previous_sibling()
print("上一个兄弟节点文本:", prev_sibling.text)  # 测试标题

# 案例3:查找div#container的直接子节点(p标签)
container = soup.find('div', id='container')
children = container.find_children()
print("div的直接子节点文本:", [c.text for c in children])  # ['容器内的段落']

4. CSS选择器:select()/select_one()

通过CSS选择器语法查找节点,更贴近前端开发习惯,灵活性更高。

语法格式
# 返回所有匹配节点(列表)
soup.select(selector, limit=None, **kwargs)
# 返回第一个匹配节点
soup.select_one(selector, **kwargs)
常用CSS选择器规则
选择器类型语法示例说明
标签选择器p匹配所有p标签
类选择器.content匹配class=content的所有标签
ID选择器#container匹配id=container的标签
后代选择器div p匹配div下所有后代p标签
子选择器div > p匹配div的直接子节点p标签
属性选择器a[href]匹配有href属性的a标签
属性值匹配a[href*="example"]匹配href含example的a标签
多条件选择器p.title, p.content匹配class=title或content的p标签
案例
# 案例1:ID选择器(查找#container)
container = soup.select_one('#container')
print("ID=container的节点文本:", container.text.strip())  # 容器内的段落

# 案例2:类选择器(查找.all.content)
content_nodes = soup.select('.content')
print("class=content的节点文本:", [n.text for n in content_nodes])  # ['测试内容1', '测试内容2']

# 案例3:后代选择器(查找div下的p标签)
div_p = soup.select('div p')
print("div下的p标签文本:", [p.text for p in div_p])  # ['容器内的段落']

# 案例4:属性选择器(查找href以https开头的a标签)
https_a = soup.select('a[href^="https"]')
print("href以https开头的a标签:", [a['href'] for a in https_a])  # ['https://example1.com', 'https://example2.com']

# 案例5:限制返回前1个a标签
limit_a = soup.select('a', limit=1)
print("前1个a标签文本:", limit_a[0].text)  # 链接1

三、节点遍历方法

BS4将HTML解析为树形结构,可通过以下方法遍历节点的子节点父节点兄弟节点

1. 遍历子节点

方法/属性类型说明
contents列表返回所有直接子节点(含文本/换行符)
children迭代器返回所有直接子节点(仅迭代,节省内存)
descendants迭代器返回所有子孙节点(递归遍历)
案例
# 定位div#container节点
container = soup.find('div', id='container')

# 案例1:contents(直接子节点,含换行符)
print("contents:", container.contents)  # ['\n', <p>容器内的段落</p>, '\n']

# 案例2:children(迭代直接子节点)
print("children遍历:")
for child in container.children:
    if child.name:  # 过滤换行符(无name属性)
        print(child.text)  # 容器内的段落

# 案例3:descendants(所有子孙节点)
print("descendants遍历:")
for desc in container.descendants:
    if desc.string:  # 有文本的节点
        print(desc.string.strip())  # 容器内的段落

2. 遍历父节点

方法/属性类型说明
parent节点对象返回直接父节点
parents迭代器返回所有祖先节点(直到根节点html)
案例
# 定位第一个a标签
a_node = soup.find('a')

# 案例1:parent(直接父节点body)
print("直接父节点:", a_node.parent.name)  # body

# 案例2:parents(所有祖先节点)
print("所有祖先节点:")
for parent in a_node.parents:
    if parent.name:
        print(parent.name)  # body → html → [document]

3. 遍历兄弟节点

方法/属性类型说明
next_sibling节点/None下一个兄弟节点(含换行符)
previous_sibling节点/None上一个兄弟节点(含换行符)
next_siblings迭代器后续所有兄弟节点
previous_siblings迭代器前面所有兄弟节点
案例
# 定位第一个content的p标签
content_p1 = soup.find('p', class_='content')

# 案例1:next_sibling(下一个兄弟节点,换行符)
print("下一个兄弟节点:", content_p1.next_sibling)  # '\n'

# 案例2:next_siblings(后续所有兄弟节点,过滤换行符)
print("后续兄弟节点:")
for sib in content_p1.next_siblings:
    if sib.name:
        print(sib.text)  # 测试内容2 → 链接1 → 链接2 → 容器内的段落

# 案例3:previous_sibling(上一个兄弟节点,换行符)
print("上一个兄弟节点:", content_p1.previous_sibling)  # '\n'

四、节点内容与属性操作

1. 获取/修改节点文本

方法/属性说明
string获取单个标签的文本(有子标签则返回None)
text/get_text()获取标签下所有文本(含子标签,推荐)
stripped_strings迭代器,返回所有文本并去除首尾空格
案例
# 定位title的p标签(含b子标签)
title_p = soup.find('p', class_='title')

# 案例1:string(有子标签,返回None)
print("string:", title_p.string)  # None
print("b标签的string:", title_p.b.string)  # 测试标题

# 案例2:text/get_text()(获取所有文本)
print("text:", title_p.text)  # 测试标题
print("get_text():", title_p.get_text())  # 测试标题

# 案例3:stripped_strings(去空格迭代)
print("stripped_strings:")
for s in title_p.stripped_strings:
    print(s)  # 测试标题

# 案例4:修改文本
title_p.b.string = "新测试标题"
print("修改后文本:", title_p.text)  # 新测试标题

2. 获取/修改节点属性

方法/属性说明
attrs返回属性字典(如{'class': ['title']}
tag['attr']获取指定属性(无则抛KeyError)
tag.get('attr')获取指定属性(无则返回None,推荐)
has_attr('attr')判断是否有指定属性
案例
# 定位第一个a标签
a_node = soup.find('a')

# 案例1:获取属性(三种方式)
print("attrs字典:", a_node.attrs)  # {'href': 'https://example1.com', 'class': ['link']}
print("直接取值:", a_node['href'])  # https://example1.com
print("get方法:", a_node.get('class'))  # ['link']
print("get默认值:", a_node.get('target', '_blank'))  # _blank

# 案例2:判断属性是否存在
print("是否有href:", a_node.has_attr('href'))  # True

# 案例3:修改属性
a_node['href'] = 'https://new-example.com'
a_node['class'] = 'new-link'
print("修改后属性:", a_node.attrs)  # {'href': 'https://new-example.com', 'class': 'new-link'}

# 案例4:删除属性
del a_node['class']
print("删除后属性:", a_node.attrs)  # {'href': 'https://new-example.com'}

五、节点修改方法

BS4支持动态添加、删除、替换节点,适用于修改HTML结构。

方法语法说明
append()tag.append(new_tag/text)向标签末尾添加文本/子标签
extend()tag.extend([tag1, tag2])向标签末尾添加多个子标签
insert()tag.insert(index, new_tag)在指定位置插入子标签
replace_with()old_tag.replace_with(new_tag)替换节点
unwrap()tag.unwrap()移除标签(保留其内容)
clear()tag.clear()清空标签内所有内容
extract()tag.extract()移除节点并返回该节点(从树中删除)
decompose()tag.decompose()移除节点并销毁(无返回值)
案例
from bs4 import Tag, NavigableString

# 案例1:append添加文本/标签
container = soup.find('div', id='container')
container.append(NavigableString('新增文本'))  # 添加文本
new_p = soup.new_tag('p')  # 创建新标签
new_p.string = '新增段落'
container.append(new_p)  # 添加标签
print("append后:", container.text.strip())  # 容器内的段落新增文本新增段落

# 案例2:insert插入标签(索引1位置)
insert_p = soup.new_tag('p', class_='inserted')
insert_p.string = '插入的段落'
container.insert(1, insert_p)
print("insert后:", container.text.strip())  # 容器内的段落插入的段落新增文本新增段落

# 案例3:replace_with替换节点
old_p = soup.find('p', class_='title')
new_title = soup.new_tag('h1')
new_title.string = '新标题'
old_p.replace_with(new_title)
print("替换后标题:", soup.find('h1').text)  # 新标题

# 案例4:unwrap移除标签(移除b标签,保留文本)
h1 = soup.find('h1')  # 假设h1内有b标签(还原测试:h1.append(soup.new_tag('b', string='新标题')))
h1.b.unwrap()
print("unwrap后:", h1.text)  # 新标题

# 案例5:extract移除节点
a2 = soup.find_all('a')[1]
extracted_a = a2.extract()
print("移除后剩余a标签:", [a.text for a in soup.find_all('a')])  # ['链接1']
print("被移除的a标签:", extracted_a.text)  # 链接2

# 案例6:clear清空内容
content_p = soup.find('p', class_='content')
content_p.clear()
print("clear后:", content_p.text)  # 空字符串

六、其他常用辅助方法

方法语法说明
prettify()soup.prettify()格式化输出HTML(带缩进,便于阅读)
encode()tag.encode('utf-8')将节点转换为字节串(可指定编码)
decode()tag.decode('utf-8')将字节串转换为字符串
new_tag()soup.new_tag('a')创建新标签(可指定属性:new_tag('a', href='xxx')
original_encodingsoup.original_encoding获取文档原始编码(自动检测)
案例
# 案例1:prettify格式化输出
print("格式化输出:")
print(soup.prettify())

# 案例2:encode/decode
a_node = soup.find('a')
byte_data = a_node.encode('utf-8')
print("字节串:", byte_data)  # b'<a href="https://new-example.com">链接1</a>'
str_data = byte_data.decode('utf-8')
print("字符串:", str_data)  # <a href="https://new-example.com">链接1</a>

# 案例3:创建新标签
new_a = soup.new_tag('a', href='https://test.com', class_='test-link')
new_a.string = '测试链接'
soup.body.append(new_a)
print("新增a标签:", soup.find('a', class_='test-link').text)  # 测试链接

七、总结

BS4的核心能力可归纳为:

  1. 查找find_all()/find()(通用)、select()(CSS语法)是高频方法;
  2. 遍历:通过contents/parent/next_sibling等遍历树形结构;
  3. 操作:文本/属性的获取/修改、节点的增删改;
  4. 辅助prettify()格式化、new_tag()创建节点。

学习重点:

  • 区分find_all()(列表)和find()(单个节点);
  • 掌握CSS选择器(前端通用语法,复用性高);
  • 注意class_的使用(避免Python关键字冲突);
  • 解析器优先选择lxml(效率+兼容性最优)。

建议结合实际场景(如爬虫解析网页、修改HTML模板)反复练习,熟练掌握核心方法的参数和使用场景。

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值