导航菜单

  • 1.vector
  • 2.milvus
  • 3.pymilvus
  • 4.rag
  • 5.rag_measure
  • 7.search
  • ragflow
  • heapq
  • HNSW
  • cosine_similarity
  • math
  • typing
  • etcd
  • minio
  • collections
  • jieba
  • random
  • beautifulsoup4
  • chromadb
  • sentence_transformers
  • numpy
  • lxml
  • openpyxl
  • PyMuPDF
  • python-docx
  • requests
  • python-pptx
  • text_splitter
  • all-MiniLM-L6-v2
  • openai
  • llm
  • BPETokenizer
  • Flask
  • RAGAS
  • BagofWords
  • langchain
  • Pydantic
  • abc
  • faiss
  • MMR
  • scikit-learn
  • 1. 什么是 BeautifulSoup4?
  • 2. 环境准备
    • 2.1 检查 Python 版本
    • 2.2 安装 BeautifulSoup4
    • 2.3 验证安装
  • 3. 快速体验
  • 4. 解析器对比与编码处理
    • 4.1 解析器对比
    • 4.2 解析器示例
    • 4.3 编码处理
  • 5. DOM 导航:父节点、子节点、兄弟节点
    • 5.1 基本概念
    • 5.2 DOM 导航示例
  • 6. 搜索语法:find、find_all 和 select
    • 6.1 find 和 find_all
    • 6.2 find 和 find_all 示例
    • 6.3 CSS 选择器 select
    • 6.4 CSS 选择器示例
  • 7. 提取文本与属性
    • 7.1 提取文本
    • 7.2 提取属性
  • 8. 修改与构造 HTML 片段
  • 9. 实战案例:迷你新闻采集器
  • 10. 性能优化与常见问题
    • 10.1 稳健的请求函数
    • 10.2 常见问题速查
  • 11. 学习资源

1. 什么是 BeautifulSoup4? #

BeautifulSoup4(简称 BS4)是 Python 中最易上手的 HTML/XML 解析库。它可以帮助我们从网页中提取文本、链接、图片等各种信息。想象一下,你看到一个网页上有许多商品的名称和价格,但一个个复制太麻烦,BS4 就能帮你自动提取这些信息。

BS4 的主要优势:

  • 自动处理编码:即使网页编码混乱,BS4 也能正确处理
  • 修复损坏的标签:有些网页 HTML 标签不完整,BS4 会自动修复
  • 简单的 API:用简单的代码就能提取复杂的内容
  • 与 requests 配合:可以轻松抓取网页内容然后解析

前置知识补充:如果你对 HTML 不熟悉,这里简单介绍一下。HTML 是网页的结构,由各种标签组成,比如 <a> 表示链接,<p> 表示段落,<div> 表示容器。标签可以包含属性,比如 <a href="/home">首页</a> 中,href 是属性名,/home 是属性值。了解这些基本概念会让学习 BS4 更顺利。

2. 环境准备 #

在学习 BS4 之前,需要确保你的 Python 环境已经准备好。BS4 支持 Python 3.8 及以上版本。

2.1 检查 Python 版本 #

在安装之前,先检查一下你的 Python 版本是否符合要求。

# Windows PowerShell:查看 Python 版本
python --version
# macOS 终端:查看 Python 版本
python3 --version

2.2 安装 BeautifulSoup4 #

安装前请确保 Python ≥ 3.8、pip ≥ 21。建议同时安装 lxml 解析器以获得更好性能。

# 说明:Windows PowerShell 安装 BeautifulSoup4 与 lxml
# 先升级 pip 到最新版本
python -m pip install --upgrade pip
# 安装 BeautifulSoup4 和 lxml 解析器
python -m pip install beautifulsoup4 lxml
# 说明:macOS / Linux 终端安装 BeautifulSoup4 与 lxml
# 先升级 pip 到最新版本
python3 -m pip install --upgrade pip
# 安装 BeautifulSoup4 和 lxml 解析器
python3 -m pip install beautifulsoup4 lxml

解析器说明:lxml 是 BS4 推荐使用的解析器,速度快且功能强大。如果遇到结构极其混乱的 HTML,可以额外安装 html5lib(pip install html5lib)。本教程后续代码默认使用 lxml。

2.3 验证安装 #

安装完成后,验证一下是否安装成功。

# -*- coding: utf-8 -*-
# 说明:验证 BeautifulSoup4 是否安装成功

# 说明:导入 BeautifulSoup 类
from bs4 import BeautifulSoup

# 说明:创建一个简单的 HTML 字符串用于测试
test_html = "<html><body><p>测试</p></body></html>"

# 说明:使用 BeautifulSoup 解析 HTML
soup = BeautifulSoup(test_html, "lxml")

# 说明:提取段落文本,验证安装是否成功
print("安装成功!提取的文本:", soup.p.get_text())

# 说明:如果没有报错并输出了"测试",说明安装成功

3. 快速体验 #

下面这个例子会展示 BS4 的基本用法:创建一个 Soup 对象、获取标题、遍历列表。通过这个简单的例子,你可以快速了解 BS4 是如何工作的。

# -*- coding: utf-8 -*-
# 说明:快速体验 BeautifulSoup4 的基本用法

# 说明:导入 BeautifulSoup 类
from bs4 import BeautifulSoup

# 说明:准备一段待解析的 HTML 字符串
# 这是一个简单的网页结构,包含标题和链接列表
html_doc = """
<html>
  <head><title>示例页面</title></head>
  <body>
    <h1>热门链接</h1>
    <ul id="menu">
      <li><a href="/home">首页</a></li>
      <li><a href="/about">关于我们</a></li>
      <li><a href="/contact">联系我们</a></li>
    </ul>
  </body>
</html>
"""

# 说明:创建 BeautifulSoup 对象,指定使用 lxml 解析器
# 第一个参数是 HTML 字符串,第二个参数是解析器名称
soup = BeautifulSoup(html_doc, "lxml")

# 说明:获取网页标题文本
# soup.title 获取 title 标签,.string 获取标签内的文本
print("网页标题:", soup.title.string)

# 说明:使用 CSS 选择器查找所有菜单链接
# "#menu li a" 表示选择 id 为 "menu" 的元素内的所有 li 标签下的 a 标签
for item in soup.select("#menu li a"):
    # 说明:获取链接文本,strip=True 表示去除首尾空白
    text = item.get_text(strip=True)
    # 说明:获取链接地址,通过属性名获取属性值
    href = item["href"]
    # 说明:打印菜单项文本和链接
    print("菜单项:", text, "| 链接:", href)

运行上面的代码,你会看到:

  • 网页标题:示例页面
  • 菜单项:首页 | 链接:/home
  • 菜单项:关于我们 | 链接:/about
  • 菜单项:联系我们 | 链接:/contact

4. 解析器对比与编码处理 #

BS4 支持三种解析器:lxml、html.parser、html5lib。不同解析器有不同的特点,选择合适的解析器很重要。

4.1 解析器对比 #

  • lxml:速度最快,功能强大,推荐使用(需要安装 lxml 库)
  • html.parser:Python 内置,无需额外安装,速度中等
  • html5lib:容错性最好,可以解析结构混乱的 HTML,但速度较慢(需要安装 html5lib 库)

4.2 解析器示例 #

# -*- coding: utf-8 -*-
# 说明:演示不同解析器的使用方法

# 说明:导入 BeautifulSoup
from bs4 import BeautifulSoup

# 说明:准备一个示例 HTML 字符串
# 其中包含一些 HTML 实体编码(&lt; 表示 <,&gt; 表示 >)
sample = "<p>演示 &lt;b&gt; 解析器 &lt;/b&gt;</p>"

# 说明:使用 lxml 解析器解析 HTML
# lxml 速度最快,是推荐使用的解析器
soup_lxml = BeautifulSoup(sample, "lxml")
print("lxml 输出:", soup_lxml.p)

# 说明:使用 html.parser 解析器解析 HTML
# html.parser 是 Python 内置的解析器,无需额外安装
soup_html = BeautifulSoup(sample, "html.parser")
print("html.parser 输出:", soup_html.p)

# 说明:使用 html5lib 解析器解析 HTML
# html5lib 容错性最好,适合解析结构混乱的 HTML
# 注意:需要先安装 html5lib(pip install html5lib)
try:
    soup_html5 = BeautifulSoup(sample, "html5lib")
    print("html5lib 输出:", soup_html5.p)
except Exception as e:
    print("html5lib 未安装,请运行:pip install html5lib")

4.3 编码处理 #

中文网页常见编码问题,如果遇到乱码,可以这样处理:

# -*- coding: utf-8 -*-
# 说明:演示如何处理网页编码问题

# 说明:导入 requests 和 BeautifulSoup
import requests
from bs4 import BeautifulSoup

# 说明:如果使用 requests 获取网页内容
url = "https://example.com"
# 说明:发送 GET 请求获取网页内容
response = requests.get(url)
# 说明:自动推测网页编码并设置
# apparent_encoding 会根据网页内容自动判断编码格式
response.encoding = response.apparent_encoding

# 说明:使用 BeautifulSoup 解析网页内容
# 此时编码已经正确设置,不会出现乱码
soup = BeautifulSoup(response.text, "lxml")

# 说明:如果直接从字符串解析,可以指定编码
html_string = "<p>中文内容</p>"
# 说明:from_encoding 参数指定原始编码(如果已知)
# soup = BeautifulSoup(html_string, "lxml", from_encoding="utf-8")
soup = BeautifulSoup(html_string, "lxml")
print(soup.p.get_text())

5. DOM 导航:父节点、子节点、兄弟节点 #

DOM(文档对象模型)是 HTML 的树状结构表示。每个标签都是一个节点,节点之间有父子、兄弟关系。理解这些关系对于提取数据非常重要。

5.1 基本概念 #

  • 父节点(Parent):包含当前节点的节点
  • 子节点(Child):被当前节点包含的节点
  • 兄弟节点(Sibling):具有相同父节点的节点

5.2 DOM 导航示例 #

# -*- coding: utf-8 -*-
# 说明:演示如何在 DOM 树中进行导航(父节点、子节点、兄弟节点)

# 说明:导入 BeautifulSoup
from bs4 import BeautifulSoup

# 说明:准备一段包含多层嵌套的 HTML 文档
html_doc = """
<div class="content">
  <h1>标题</h1>
  <p class="intro">第一段</p>
  <p class="desc">第二段</p>
</div>
"""

# 说明:创建 BeautifulSoup 对象并解析 HTML
soup = BeautifulSoup(html_doc, "lxml")

# 说明:查找 class 为 "intro" 的段落标签
intro = soup.find("p", class_="intro")
print("找到的段落:", intro.get_text())

# 说明:获取父节点
# parent 属性返回当前节点的父节点
print("父节点标签:", intro.parent.name)

# 说明:遍历兄弟节点
# find_next_siblings() 查找后面所有的兄弟节点(相同标签名)
# 参数 "p" 表示只查找 p 标签的兄弟节点
for sibling in intro.find_next_siblings("p"):
    print("兄弟段落文本:", sibling.get_text())

# 说明:遍历子节点
# children 属性返回当前节点的所有直接子节点(包括文本节点)
# 注意:children 返回的是一个生成器,需要用循环遍历
for child in soup.div.children:
    # 说明:过滤掉文本节点(空白字符),只处理标签节点
    # name 属性如果是 None,说明是文本节点
    if child.name:
        print("子节点标签:", child.name)

6. 搜索语法:find、find_all 和 select #

BS4 提供了多种方式来查找标签:find()、find_all() 和 select()。每种方法都有其适用场景。

6.1 find 和 find_all #

  • find():查找第一个匹配的标签
  • find_all():查找所有匹配的标签

6.2 find 和 find_all 示例 #

# -*- coding: utf-8 -*-
# 说明:演示使用 find 和 find_all 查找标签

# 说明:导入 BeautifulSoup
from bs4 import BeautifulSoup

# 说明:准备示例 HTML 文档
html_doc = """
<ul class="links">
  <li><a href="/ai" data-tag="tech">AI</a></li>
  <li><a href="/python" data-tag="lang">Python</a></li>
  <li><a href="/travel" data-tag="life">旅行</a></li>
</ul>
"""

# 说明:创建 BeautifulSoup 对象
soup = BeautifulSoup(html_doc, "lxml")

# 说明:使用 find_all 查找所有 a 标签
# find_all() 返回一个列表,包含所有匹配的标签
links = soup.find_all("a")
print("找到的所有链接:")
for link in links:
    # 说明:获取链接文本
    text = link.text
    # 说明:获取链接地址(href 属性)
    href = link["href"]
    print("文本:", text, "| 地址:", href)

# 说明:使用 find 查找第一个匹配的标签
# find() 只返回第一个匹配的标签,如果没有找到返回 None
first_link = soup.find("a")
print("\n第一个链接:", first_link.get_text() if first_link else "未找到")

# 说明:根据属性查找标签
# 查找 data-tag 属性为 "tech" 的 a 标签
tech_link = soup.find("a", attrs={"data-tag": "tech"})
if tech_link:
    print("科技链接:", tech_link["href"])

6.3 CSS 选择器 select #

select() 方法使用 CSS 选择器语法,更加直观和强大。

6.4 CSS 选择器示例 #

# -*- coding: utf-8 -*-
# 说明:演示使用 CSS 选择器查找标签

# 说明:导入 BeautifulSoup
from bs4 import BeautifulSoup

# 说明:准备示例 HTML 文档
html_doc = """
<ul class="links">
  <li><a href="/ai" data-tag="tech">AI</a></li>
  <li><a href="/python" data-tag="lang">Python</a></li>
  <li><a href="/travel" data-tag="life">旅行</a></li>
</ul>
"""

# 说明:创建 BeautifulSoup 对象
soup = BeautifulSoup(html_doc, "lxml")

# 说明:使用 CSS 选择器查找所有 a 标签
# select() 方法使用 CSS 选择器语法
all_links = soup.select("a")
print("所有链接:")
for link in all_links:
    print("文本:", link.get_text(), "| 地址:", link["href"])

# 说明:使用 CSS 属性选择器
# a[data-tag="tech"] 表示选择 data-tag 属性值为 "tech" 的 a 标签
tech_links = soup.select('a[data-tag="tech"]')
print("\n科技链接:")
for tech_link in tech_links:
    print("地址:", tech_link["href"])

# 说明:使用类选择器
# .links 表示选择 class 为 "links" 的元素
links_container = soup.select(".links")
print("\n链接容器数量:", len(links_container))

# 说明:组合选择器
# ul.links li a 表示选择 ul 标签且 class 为 "links" 下的所有 li 标签下的 a 标签
combined_links = soup.select("ul.links li a")
print("\n组合选择器找到的链接:")
for link in combined_links:
    print("文本:", link.get_text())

CSS 选择器语法速查:

  • tag:标签名,如 div、p
  • .class:类选择器,如 .content
  • #id:ID 选择器,如 #menu
  • [attr="value"]:属性选择器
  • parent child:后代选择器(空格)
  • parent > child:直接子元素选择器(>)

7. 提取文本与属性 #

提取数据是使用 BS4 的核心操作。本节介绍如何安全、准确地提取文本和属性。

7.1 提取文本 #

提取文本有多种方法,推荐使用 get_text() 方法。

# -*- coding: utf-8 -*-
# 说明:演示如何提取标签中的文本内容

# 说明:导入 BeautifulSoup
from bs4 import BeautifulSoup

# 说明:准备示例 HTML 片段
html = '<a href="/about" title="关于我们">了解更多</a>'

# 说明:创建 BeautifulSoup 对象并解析
soup = BeautifulSoup(html, "lxml")

# 说明:获取 a 标签对象
link = soup.a

# 说明:方法1:使用 get_text() 提取文本(推荐)
# strip=True 表示去除文本首尾的空白字符
text1 = link.get_text(strip=True)
print("方法1 - get_text():", text1)

# 说明:方法2:使用 .text 属性提取文本
# .text 会提取标签及其所有子标签的文本,并去除多余空白
text2 = link.text
print("方法2 - .text:", text2)

# 说明:方法3:使用 .string 属性提取文本
# .string 只返回标签的直接文本内容(不包含子标签)
# 如果标签包含子标签,.string 会返回 None
text3 = link.string
print("方法3 - .string:", text3)

# 说明:处理包含子标签的情况
html_with_children = '<p>这是普通文本 <strong>这是加粗文本</strong></p>'
soup2 = BeautifulSoup(html_with_children, "lxml")
p_tag = soup2.p

# 说明:get_text() 会提取所有文本(包括子标签)
print("\n包含子标签的情况:")
print("get_text():", p_tag.get_text())
print(".text:", p_tag.text)
print(".string:", p_tag.string)  # 因为包含子标签,返回 None

7.2 提取属性 #

提取属性可以使用字典方式或 get() 方法。

# -*- coding: utf-8 -*-
# 说明:演示如何提取标签的属性值

# 说明:导入 BeautifulSoup
from bs4 import BeautifulSoup

# 说明:准备示例 HTML 片段
html = '<a href="/about" title="关于我们">了解更多</a>'

# 说明:创建 BeautifulSoup 对象并解析
soup = BeautifulSoup(html, "lxml")

# 说明:获取 a 标签对象
link = soup.a

# 说明:方法1:使用字典方式直接获取属性(如果属性不存在会报错)
# 这种方式适合确定属性一定存在的情况
href1 = link["href"]
print("方法1 - 字典方式获取 href:", href1)

# 说明:方法2:使用 get() 方法安全获取属性(推荐)
# get() 方法如果属性不存在,会返回默认值(如果不提供默认值则返回 None)
href2 = link.get("href", "#")
print("方法2 - get() 方法获取 href:", href2)

title = link.get("title", "无")
print("get() 方法获取 title:", title)

# 说明:获取不存在的属性,不会报错
data_id = link.get("data-id", "默认值")
print("获取不存在的属性 data-id:", data_id)

# 说明:获取所有属性
# attrs 返回一个字典,包含所有属性
all_attrs = link.attrs
print("\n所有属性:", all_attrs)

8. 修改与构造 HTML 片段 #

BS4 不仅可以提取数据,还可以修改 HTML 结构。这在需要清洗 HTML 或生成新的 HTML 片段时很有用。

# -*- coding: utf-8 -*-
# 说明:演示如何修改标签和构造新的 HTML 片段

# 说明:导入 BeautifulSoup
from bs4 import BeautifulSoup

# 说明:准备原始 HTML
html = '<p class="info">原始文本</p>'

# 说明:创建 BeautifulSoup 对象并解析
soup = BeautifulSoup(html, "lxml")

# 说明:获取 p 标签对象
p_tag = soup.p
print("原始标签:", p_tag)

# 说明:修改标签名称
# name 属性可以修改标签的类型
p_tag.name = "span"
print("修改标签名后:", p_tag)

# 说明:修改属性
# 直接通过字典方式修改或添加属性
p_tag["class"] = ["updated"]
p_tag["data-flag"] = "demo"
p_tag["id"] = "new-id"
print("修改属性后:", p_tag)

# 说明:修改文本内容
# string 属性可以替换标签内的文本
p_tag.string = "替换后的文本"
print("修改文本后:", p_tag)

# 说明:构造新的 HTML 片段
# 使用 BeautifulSoup 可以轻松创建新的标签
new_tag = soup.new_tag("div")
new_tag["class"] = "container"
new_tag.string = "这是新创建的标签"
print("\n新创建的标签:", new_tag)

# 说明:添加子标签
# 使用 append() 方法添加子标签
new_link = soup.new_tag("a")
new_link["href"] = "/new-page"
new_link.string = "新链接"
new_tag.append(new_link)
print("添加子标签后:", new_tag)

# 说明:输出完整的 HTML
print("\n完整 HTML:", soup.prettify())

9. 实战案例:迷你新闻采集器 #

下面是一个完整的实战案例,展示如何结合 requests 和 BS4 从网页中提取新闻标题和链接。

# -*- coding: utf-8 -*-
# 说明:实战案例 - 迷你新闻采集器
# 演示如何结合 requests 和 BeautifulSoup 从网页提取新闻标题和链接

# 说明:导入 requests 库用于发送 HTTP 请求
import requests

# 说明:导入 BeautifulSoup 用于解析 HTML
from bs4 import BeautifulSoup

# 说明:定义新闻采集函数
def fetch_news(url: str, limit: int = 5):
    """
    从指定 URL 获取新闻标题和链接

    参数:
        url (str): 要抓取的网页地址
        limit (int): 返回的新闻数量,默认为 5
    """
    try:
        # 说明:发送 GET 请求获取网页内容
        # timeout=10 表示请求超时时间为 10 秒
        resp = requests.get(url, timeout=10)

        # 说明:检查 HTTP 状态码,如果不是 200 会抛出异常
        resp.raise_for_status()

        # 说明:自动推测并设置网页编码
        # apparent_encoding 会根据网页内容自动判断编码格式
        resp.encoding = resp.apparent_encoding

        # 说明:使用 BeautifulSoup 解析 HTML 内容
        soup = BeautifulSoup(resp.text, "lxml")

        # 说明:使用 CSS 选择器查找新闻链接
        # 这里使用通用的选择器,实际使用时需要根据目标网站调整
        # "article h2 a" 表示选择 article 标签下的 h2 标签下的 a 标签
        # ".news-item a" 表示选择 class 为 "news-item" 的元素下的 a 标签
        items = soup.select("article h2 a, .news-item a")

        # 说明:打印找到的新闻数量
        print(f"找到 {len(items)} 条新闻,显示前 {limit} 条:\n")

        # 说明:遍历前 limit 条结果
        for idx, anchor in enumerate(items[:limit], 1):
            # 说明:提取链接文本(标题)
            title = anchor.get_text(strip=True)

            # 说明:提取链接地址
            # 如果 href 是相对路径,可能需要拼接完整的 URL
            href = anchor.get("href", "#")

            # 说明:如果是相对路径,转换为绝对路径
            if href.startswith("/"):
                from urllib.parse import urljoin
                href = urljoin(url, href)

            # 说明:打印新闻信息
            print(f"{idx}. 标题:{title}")
            print(f"   链接:{href}")
            print("-" * 50)

    except requests.RequestException as e:
        # 说明:捕获网络请求异常
        print(f"请求失败:{e}")
    except Exception as e:
        # 说明:捕获其他异常
        print(f"处理失败:{e}")


# 说明:主程序入口
if __name__ == "__main__":
    # 说明:示例调用
    # 注意:实际使用时请替换成真实可访问的网址
    # 并遵守网站的 robots.txt 和使用条款

    # 说明:这里使用一个示例 URL,实际使用时请替换
    example_url = "https://example.com"
    print("注意:这是一个示例,请替换为真实的新闻网站 URL\n")

    # 说明:调用函数获取新闻
    # fetch_news(example_url, limit=5)

    # 说明:演示如何解析本地 HTML 文件
    print("\n 本地 HTML 示例 \n")

    # 说明:创建一个示例 HTML 字符串
    sample_html = """
    <html>
    <body>
        <article>
            <h2><a href="/news/1">第一条新闻标题</a></h2>
            <p>这是第一条新闻的内容...</p>
        </article>
        <article>
            <h2><a href="/news/2">第二条新闻标题</a></h2>
            <p>这是第二条新闻的内容...</p>
        </article>
        <div class="news-item">
            <a href="/news/3">第三条新闻标题</a>
        </div>
    </body>
    </html>
    """

    # 说明:解析示例 HTML
    soup = BeautifulSoup(sample_html, "lxml")
    items = soup.select("article h2 a, .news-item a")

    # 说明:打印结果
    print("从示例 HTML 中提取的新闻:\n")
    for idx, anchor in enumerate(items, 1):
        title = anchor.get_text(strip=True)
        href = anchor.get("href", "#")
        print(f"{idx}. 标题:{title}")
        print(f"   链接:{href}")
        print("-" * 30)

10. 性能优化与常见问题 #

在实际使用中,你可能会遇到编码问题、选择器错误、网络异常等问题。本节提供一些实用的解决方案。

10.1 稳健的请求函数 #

# -*- coding: utf-8 -*-
# 说明:封装一个稳健的网页请求函数,包含错误处理和编码处理

# 说明:导入 requests 库
import requests

# 说明:导入 BeautifulSoup
from bs4 import BeautifulSoup

# 说明:封装稳健的请求函数
def robust_fetch(url: str):
    """
    稳健地获取网页内容并解析为 BeautifulSoup 对象

    参数:
        url (str): 要请求的网页地址

    返回:
        BeautifulSoup 对象,如果失败返回 None
    """
    # 说明:创建 Session 对象,可以重用连接,提高性能
    session = requests.Session()

    # 说明:设置请求头,模拟浏览器访问
    # User-Agent 告诉服务器你使用的浏览器类型
    session.headers.update({
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
    })

    try:
        # 说明:发送 GET 请求,设置超时时间为 8 秒
        resp = session.get(url, timeout=8)

        # 说明:检查 HTTP 状态码
        resp.raise_for_status()

        # 说明:自动推测并设置网页编码
        resp.encoding = resp.apparent_encoding

        # 说明:使用 BeautifulSoup 解析网页内容
        soup = BeautifulSoup(resp.text, "lxml")

        # 说明:返回解析后的 BeautifulSoup 对象
        return soup

    except requests.RequestException as exc:
        # 说明:捕获网络请求相关异常
        print(f"请求失败:{exc}")
        return None
    except Exception as exc:
        # 说明:捕获其他未知异常
        print(f"未知错误:{exc}")
        return None


# 说明:封装安全的选择器函数,避免选择器错误导致程序崩溃
def safe_select(soup, selector):
    """
    安全地使用 CSS 选择器查找标签

    参数:
        soup (BeautifulSoup): BeautifulSoup 对象
        selector (str): CSS 选择器字符串

    返回:
        找到的标签列表,如果出错返回空列表
    """
    try:
        # 说明:使用 CSS 选择器查找标签
        return soup.select(selector)
    except Exception as exc:
        # 说明:捕获选择器错误
        print(f"选择器错误:{exc}")
        return []


# 说明:主程序入口,演示如何使用
if __name__ == "__main__":
    # 说明:示例调用(请替换成真实 URL)
    # url = "https://example.com"
    # page = robust_fetch(url)

    # 说明:如果成功获取网页
    # if page:
    #     # 说明:安全地查找标题
    #     titles = safe_select(page, "h1, h2")
    #     print("标题数量:", len(titles))
    #     for title in titles:
    #         print("标题:", title.get_text(strip=True))

    # 说明:本地 HTML 示例
    sample_html = "<html><body><h1>标题1</h1><h2>标题2</h2></body></html>"
    soup = BeautifulSoup(sample_html, "lxml")
    titles = safe_select(soup, "h1, h2")
    print("标题数量:", len(titles))
    for title in titles:
        print("标题:", title.get_text(strip=True))

10.2 常见问题速查 #

  1. 文本乱码问题

    • 原因:网页编码与解析时使用的编码不一致
    • 解决方案:

      # 使用 requests 时
      response.encoding = response.apparent_encoding
      
      # 或者直接指定编码
      soup = BeautifulSoup(html, "lxml", from_encoding="utf-8")
  2. 找不到标签

    • 原因1:标签由 JavaScript 动态生成,BS4 只能解析静态 HTML
    • 解决方案:使用 Selenium 或 Playwright 等工具先渲染页面
    • 原因2:选择器写错了
    • 解决方案:在浏览器中右键检查元素,确认正确的选择器
  3. 性能问题

    • 原因:一次性解析大量 HTML 或频繁请求
    • 解决方案:
      • 缩小选择器范围,只选择需要的部分
      • 使用 Session 重用连接
      • 设置合理的超时时间
      • 考虑使用多线程或异步请求
  4. 反爬虫限制

    • 表现:请求被拒绝或返回验证码页面
    • 解决方案:
      • 添加延时(time.sleep())
      • 随机更换 User-Agent
      • 遵守网站的 robots.txt
      • 不要过于频繁地访问

11. 学习资源 #

  • BeautifulSoup4 官方文档:https://www.crummy.com/software/BeautifulSoup/bs4/doc/
  • CSS 选择器参考:https://www.w3schools.com/cssref/css_selectors.asp
  • Python requests 库文档:https://requests.readthedocs.io/

访问验证

请输入访问令牌

Token不正确,请重新输入