程序员scholar 程序员scholar
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Python 基础

    • Python基础
  • Python 进阶

    • 装饰器与生成器
    • 异常处理
    • 标准库精讲
    • 模块与包
    • pip包管理工具
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
前端 (opens new window)
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Python 基础

    • Python基础
  • Python 进阶

    • 装饰器与生成器
    • 异常处理
    • 标准库精讲
    • 模块与包
    • pip包管理工具
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
前端 (opens new window)
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
npm

(进入注册为作者充电)

  • Python 基础

  • Python 进阶

  • Python爬虫

    • requests 库 - HTTP for Humans
    • Beautiful Soup - HTML解析利器
      • 1. 安装 Beautiful Soup 及解析器
      • 2. 创建 BeautifulSoup 对象
      • 3. Beautiful Soup 的四大对象
      • 4. 遍历文档树
        • 4.1 访问子节点
        • 4.2 访问父节点
        • 4.3 访问兄弟节点
      • 5. 搜索文档树 (核心功能)
        • 5.1 按标签名 (name) 搜索
        • 5.2 按 CSS 类名 (class_) 搜索
        • 5.3 按属性 (attrs) 搜索
        • 5.4 按文本内容 (string) 搜索
      • 6. CSS 选择器 (更现代、更高效的搜索方式)
      • 7. 从标签中提取信息
        • 7.1 提取文本内容 (.get_text() 或 .string)
        • 7.2 提取属性值
      • 8. 综合案例:爬取并解析一个真实的页面
    • Selenium - 动态网页抓取神器
    • Scrapy - 工业级爬虫框架
  • Scrapy 爬虫框架

  • Python
  • Python爬虫
scholar
2025-07-21
目录

Beautiful Soup - HTML解析利器

# Beautiful Soup - HTML解析利器

我们已经用 requests 库成功获取了网页的 HTML 源代码,但它本质上是一长串混杂着各种标签的字符串。如何从这团“乱麻”中精准地提取出我们需要的数据,比如文章标题、商品价格、图片链接呢?Beautiful Soup 就是解决这个问题的最佳工具。

Beautiful Soup (简称 BS4) 是一个可以从 HTML 或 XML 文件中提取数据的 Python 库。它将复杂的文档转换成一个树形结构,每个节点都是 Python 对象,极大地简化了文档的遍历和数据提取过程。


# 1. 安装 Beautiful Soup 及解析器

Beautiful Soup 自身只负责解析文档和遍历节点,但它需要一个底层的**解析器 (Parser)**来真正完成 HTML 的解析工作。你需要安装两个库:beautifulsoup4 和一个解析器。

推荐使用 lxml 解析器,因为它速度快、容错能力强。

# 同时安装 bs4 库和 lxml 解析器
pip install beautifulsoup4 lxml
1
2

Python 内置了 html.parser,但 lxml 在各方面都更胜一筹,因此强烈推荐安装和使用 lxml。


# 2. 创建 BeautifulSoup 对象

要使用 Beautiful Soup,首先要将一段 HTML 文档字符串传入它的构造函数,创建一个 BeautifulSoup 对象。

import requests
from bs4 import BeautifulSoup

# 准备一段 HTML 示例代码
html_doc = """
<html><head><title>一个简单的示例页面</title></head>
<body>
<p class="story">
    很久很久以前,有三个姐妹,她们的名字是:
    <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>;
    她们住在一个小房子的底部。
</p>
<p class="story">...</p>
<div id="footer">这是页脚</div>
</body></html>
"""

# 1. 创建 BeautifulSoup 对象
#    第一个参数是 HTML 字符串
#    第二个参数是所使用的解析器,'lxml' 是最佳选择
soup = BeautifulSoup(html_doc, 'lxml')

# 2. .prettify() 方法可以将解析后的 HTML 格式化输出,带缩进,方便查看
print(soup.prettify())

# 3. 我们可以轻松地获取一些基本信息
print(f"页面的标题是: {soup.title}")         # <title>一个简单的示例页面</title>
print(f"标题的标签名: {soup.title.name}")     # title
print(f"标题的文本内容: {soup.title.string}")  # 一个简单的示例页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

在实际爬虫中,html_doc 通常来自 requests.get(url).text。


# 3. Beautiful Soup 的四大对象

Beautiful Soup 将复杂的 HTML 文档转换成四种主要的对象类型,理解它们是高效使用的关键。

  1. Tag: HTML 中的标签,如 <p>, <a> 等。这是我们打交道最多的对象。
  2. NavigableString: 标签内部的文本内容。
  3. BeautifulSoup: 整个文档的根节点,可以看作是一个特殊的 Tag。
  4. Comment: HTML 中的注释,一个特殊的 NavigableString。
# Tag 对象
tag_p = soup.p
print(f"第一个 p 标签是: {type(tag_p)}") # <class 'bs4.element.Tag'>

# NavigableString 对象
title_string = soup.title.string
print(f"标题内容是: {type(title_string)}") # <class 'bs4.element.NavigableString'>

# BeautifulSoup 对象
print(f"soup 对象本身是: {type(soup)}") # <class 'bs4.BeautifulSoup'>
1
2
3
4
5
6
7
8
9
10

# 4. 遍历文档树

Beautiful Soup 的核心功能之一就是让你能像操作 Python 对象一样轻松地在文档树的节点间穿梭。

# 4.1 访问子节点

  • .contents: 获取一个 Tag 的所有直接子节点,返回一个列表。
  • .children: 获取一个 Tag 的所有直接子节点,返回一个可迭代的生成器。
# 获取 body 标签的所有直接子节点
body_contents = soup.body.contents
print(f"body 的子节点数量: {len(body_contents)}")
print(body_contents) # 注意,换行符也被解析为一个 NavigableString

# 使用 .children 生成器进行迭代,更节省内存
for child in soup.body.children:
    if child.name: # 过滤掉非标签节点(如换行符)
        print(f"找到一个子标签: <{child.name}>")
1
2
3
4
5
6
7
8
9

# 4.2 访问父节点

  • .parent: 获取一个节点的直接父节点。
title_tag = soup.title
print(f"title 标签的父节点是: {title_tag.parent.name}") # head
1
2

# 4.3 访问兄弟节点

  • .next_sibling: 获取当前节点的下一个直接兄弟节点。
  • .previous_sibling: 获取当前节点的上一个直接兄弟节点。
link1 = soup.find(id="link1") # 先找到第一个 a 标签
# 注意:兄弟节点可能是换行符或者普通文本,不一定是标签
next_sibling = link1.next_sibling.strip()
print(f"link1 的下一个兄弟节点内容: '{next_sibling}'")

next_tag_sibling = link1.find_next_sibling('a') # 寻找下一个 a 标签兄弟
print(f"link1 的下一个兄弟标签是: {next_tag_sibling}")
1
2
3
4
5
6
7

# 5. 搜索文档树 (核心功能)

遍历虽然直观,但效率低下。Beautiful Soup 提供了强大的搜索方法 find() 和 find_all(),它们是数据提取的关键。

  • find_all(name, attrs, string, limit, ...): 查找所有匹配条件的 Tag,返回一个列表。
  • find(name, attrs, string, ...): 只查找第一个匹配条件的 Tag,相当于 find_all 设置 limit=1,返回一个 Tag 对象或 None。

# 5.1 按标签名 (name) 搜索

# 查找所有的 a 标签
all_a_tags = soup.find_all('a')
for tag in all_a_tags:
    print(tag)

# 查找第一个 p 标签
first_p = soup.find('p')
print(f"\n第一个 p 标签: {first_p}")
1
2
3
4
5
6
7
8

# 5.2 按 CSS 类名 (class_) 搜索

由于 class 是 Python 的关键字,所以在 Beautiful Soup 中,我们使用 class_ 参数来按类名搜索。

# 查找所有 class="sister" 的标签
sister_tags = soup.find_all(class_="sister")
for tag in sister_tags:
    print(tag)
1
2
3
4

# 5.3 按属性 (attrs) 搜索

你可以传递一个字典给 attrs 参数来搜索具有特定属性的标签。

# 查找所有带有 href 属性的 a 标签
tags_with_href = soup.find_all('a', attrs={'href': True})

# 查找 id="link2" 的标签
link2 = soup.find(id="link2") # 这是一个简写形式
# 完整写法是: soup.find(attrs={'id': 'link2'})
print(f"\nID为link2的标签: {link2}")

# 查找 href="http://example.com/lacie" 的标签
link_lacie = soup.find(href="http://example.com/lacie") # 也是简写
print(f"href为.../lacie的标签: {link_lacie}")
1
2
3
4
5
6
7
8
9
10
11

# 5.4 按文本内容 (string) 搜索

你可以直接搜索与字符串完全匹配的文本内容。

# 查找文本内容为 "Lacie" 的标签
lacie_text_tag = soup.find(string="Lacie")
print(f"\n文本为'Lacie'的标签: {lacie_text_tag}")
print(f"它的父标签是: {lacie_text_tag.parent}")
1
2
3
4

# 6. CSS 选择器 (更现代、更高效的搜索方式)

如果你熟悉 CSS 或者 jQuery,你会爱上 Beautiful Soup 的 .select() 方法。它允许你使用 CSS 选择器语法来查找标签,非常简洁、强大。

  • .select(selector): 根据 CSS 选择器返回一个包含所有匹配 Tag 的列表。
  • .select_one(selector): 只返回第一个匹配的 Tag。
选择器语法 含义 示例
tag 按标签名 soup.select('title')
.class 按类名 soup.select('.story')
#id 按 ID soup.select('#link1')
parent > child 查找直接子元素 soup.select('p > a')
ancestor descendant 查找后代元素(不一定是直接子元素) soup.select('body a')
[attribute=value] 按属性值 soup.select('a[href="..."]')
# 使用 CSS 选择器查找所有 class="sister" 的 a 标签
sisters = soup.select('a.sister')
print("--- 使用 CSS 选择器 ---")
for s in sisters:
    print(s)

# 查找 id="footer" 的 div 标签
footer = soup.select_one('#footer')
print(f"页脚标签: {footer}")

# 查找 p 标签下的所有直接子代 a 标签
links_in_p = soup.select('p > a')
print(f"p 标签下的链接数量: {len(links_in_p)}")
1
2
3
4
5
6
7
8
9
10
11
12
13

find_all vs .select:

  • 两者功能相似,都能完成大部分任务。
  • .select 语法更简洁,特别是对于复杂的层级关系和属性组合。
  • find_all 在某些高级场景下(如使用正则表达式、自定义函数搜索)更灵活。
  • 日常使用中,推荐优先考虑 .select() 和 .select_one()。

# 7. 从标签中提取信息

找到标签后,我们的最终目的是提取里面的数据。

# 7.1 提取文本内容 (.get_text() 或 .string)

  • .string: 如果一个标签只有一个 NavigableString 子节点,你可以用 .string 获取它。否则,返回 None。
  • .get_text(): 获取一个标签内所有的文本内容,包括其所有子孙节点的文本,并将它们拼接成一个字符串。
p_tag = soup.find('p', class_='story')

# .string 对这个复杂的 p 标签无效,因为它有多个子节点
print(f"p 标签的 .string: {p_tag.string}") # 输出: None

# .get_text() 可以提取所有文本
# separator 参数可以在不同文本块之间插入分隔符
# strip=True 可以去除开头和结尾多余的空白
p_text = p_tag.get_text(separator='|', strip=True)
print(f"\np 标签的 .get_text():\n{p_text}")
1
2
3
4
5
6
7
8
9
10

# 7.2 提取属性值

可以像操作 Python 字典一样获取标签的属性。

link1 = soup.find(id="link1")

# 获取 href 属性
href = link1['href']
print(f"\nlink1 的 href 属性是: {href}")

# 获取 class 属性(注意,class 可能有多个值,返回一个列表)
css_class = link1['class']
print(f"link1 的 class 是: {css_class}") # 输出: ['sister']

# 安全地获取属性,如果不存在也不会报错
non_existent_attr = link1.get('data-custom')
print(f"一个不存在的属性: {non_existent_attr}") # 输出: None
1
2
3
4
5
6
7
8
9
10
11
12
13

# 8. 综合案例:爬取并解析一个真实的页面

让我们结合 requests 和 Beautiful Soup 来爬取豆瓣电影 Top250 的第一页。

import requests
from bs4 import BeautifulSoup

# 1. 目标 URL 和请求头
url = "https://movie.douban.com/top250"
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}

# 2. 发送请求并获取 HTML
response = requests.get(url, headers=headers)

if response.status_code == 200:
    # 3. 创建 BeautifulSoup 对象
    soup = BeautifulSoup(response.text, 'lxml')
    
    # 4. 使用 .select() 定位到所有电影的条目
    #    通过浏览器开发者工具分析,我们发现每个电影信息都包含在一个 class="item" 的 div 中
    all_movies = soup.select('div.item')
    
    # 5. 遍历每个电影条目,提取信息
    for movie in all_movies:
        # 提取排名
        rank = movie.select_one('em').get_text()
        
        # 提取电影标题
        # 标题在 class="title" 的 span 标签里
        title = movie.select_one('span.title').get_text()
        
        # 提取评分
        # 评分在 class="rating_num" 的 span 标签里
        rating = movie.select_one('span.rating_num').get_text()
        
        # 提取引言 (可能没有)
        quote_tag = movie.select_one('span.inq')
        quote = quote_tag.get_text() if quote_tag else "无"
        
        print(f"排名: {rank}\n电影: {title}\n评分: {rating}\n引言: {quote}\n" + "-"*20)
else:
    print(f"请求失败,状态码: {response.status_code}")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

掌握了 requests 和 Beautiful Soup,你就拥有了爬取绝大多数静态网页数据的能力。

编辑此页 (opens new window)
上次更新: 2025/07/25, 08:02:48
requests 库 - HTTP for Humans
Selenium - 动态网页抓取神器

← requests 库 - HTTP for Humans Selenium - 动态网页抓取神器→

Theme by Vdoing | Copyright © 2019-2025 程序员scholar
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式