Skip to content

Day 2: HTML解析与BeautifulSoup

学习目标

  • 理解 HTML 文档结构和 DOM 树
  • 掌握 BeautifulSoup 库的基本使用
  • 学会使用 CSS 选择器提取数据
  • 了解 XPath 基础语法

技术原理

2.1 HTML 文档结构

HTML(HyperText Markup Language)是网页的标准标记语言,由一系列标签组成。

基本结构:

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>网页标题</title>
</head>
<body>
    <header>
        <nav>导航栏</nav>
    </header>
    <main>
        <article>
            <h1>文章标题</h1>
            <p>段落内容</p>
        </article>
    </main>
    <footer>
        <p>页脚内容</p>
    </footer>
</body>
</html>

2.2 DOM 树结构

DOM(Document Object Model)是 HTML 文档的编程接口,将文档表示为节点树。

节点类型:

  • 元素节点: HTML 标签(如 <div><p>
  • 文本节点: 标签内的文本内容
  • 属性节点: 标签的属性(如 class、id)

2.3 BeautifulSoup 简介

BeautifulSoup 是一个用于解析 HTML 和 XML 的 Python 库,提供了简单的方式来导航、搜索和修改解析树。

主要特点:

  • 容错性强,能处理格式不规范的 HTML
  • 提供多种解析器(html.parser、lxml、html5lib)
  • 支持 CSS 选择器和 XPath
  • API 简单易用

案例

案例1:基本解析

python
from bs4 import BeautifulSoup

# HTML 文档
html_doc = """
<html>
<head><title>测试页面</title></head>
<body>
    <div class="container">
        <h1 id="title">欢迎访问</h1>
        <p class="content">这是一个段落</p>
        <p class="content">这是另一个段落</p>
    </div>
</body>
</html>
"""

# 创建 BeautifulSoup 对象
soup = BeautifulSoup(html_doc, 'html.parser')

# 获取标题
title = soup.title.text
print(f'标题: {title}')

# 获取 h1 标签
h1 = soup.find('h1')
print(f'H1: {h1.text}')

# 获取所有 p 标签
paragraphs = soup.find_all('p')
for p in paragraphs:
    print(f'段落: {p.text}')

案例2:通过属性查找

python
from bs4 import BeautifulSoup

html_doc = """
<div class="product">
    <h2 class="name">商品名称</h2>
    <span class="price">¥99.00</span>
    <p class="desc">商品描述</p>
</div>
"""

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

# 通过 class 查找
product_name = soup.find('h2', class_='name')
print(f'商品名: {product_name.text}')

# 通过 id 查找(如果有id属性)
# element = soup.find('div', id='product-1')

# 通过属性查找
price = soup.find('span', attrs={'class': 'price'})
print(f'价格: {price.text}')

应用场景

1. 网页数据提取

  • 提取文章标题和内容
  • 获取商品信息
  • 采集新闻数据

2. 网页结构分析

  • 分析网页布局
  • 提取导航链接
  • 获取页面元数据

3. 数据清洗

  • 去除 HTML 标签
  • 提取纯文本
  • 格式化数据

代码案例

案例3:提取所有链接

python
from bs4 import BeautifulSoup
import requests

# 获取网页内容
url = 'https://example.com'
response = requests.get(url)

# 解析 HTML
soup = BeautifulSoup(response.text, 'html.parser')

# 查找所有 a 标签
links = soup.find_all('a')

# 提取链接
for link in links:
    href = link.get('href')
    text = link.text.strip()
    if href:
        print(f'链接文本: {text}')
        print(f'链接地址: {href}')
        print('-' * 50)

案例4:提取表格数据

python
from bs4 import BeautifulSoup

html_doc = """
<table class="data-table">
    <thead>
        <tr>
            <th>姓名</th>
            <th>年龄</th>
            <th>城市</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>张三</td>
            <td>25</td>
            <td>北京</td>
        </tr>
        <tr>
            <td>李四</td>
            <td>30</td>
            <td>上海</td>
        </tr>
    </tbody>
</table>
"""

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

# 查找表格
table = soup.find('table', class_='data-table')

# 获取表头
headers = []
for th in table.find('thead').find_all('th'):
    headers.append(th.text)

# 获取数据行
rows = []
for tr in table.find('tbody').find_all('tr'):
    row = []
    for td in tr.find_all('td'):
        row.append(td.text)
    rows.append(dict(zip(headers, row)))

# 打印结果
for row in rows:
    print(row)

案例5:使用 CSS 选择器

python
from bs4 import BeautifulSoup

html_doc = """
<div class="container">
    <div class="card">
        <h2 class="card-title">标题1</h2>
        <p class="card-text">内容1</p>
    </div>
    <div class="card">
        <h2 class="card-title">标题2</h2>
        <p class="card-text">内容2</p>
    </div>
</div>
"""

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

# 使用 CSS 选择器查找
cards = soup.select('.card')

for card in cards:
    title = card.select_one('.card-title').text
    text = card.select_one('.card-text').text
    print(f'标题: {title}')
    print(f'内容: {text}')
    print('-' * 50)

# 嵌套选择
container_cards = soup.select('div.container > div.card')
print(f'找到 {len(container_cards)} 个卡片')

案例6:提取嵌套数据

python
from bs4 import BeautifulSoup

html_doc = """
<article class="post">
    <header>
        <h1 class="title">文章标题</h1>
        <div class="meta">
            <span class="author">作者</span>
            <span class="date">2024-01-01</span>
        </div>
    </header>
    <div class="content">
        <p>第一段内容</p>
        <p>第二段内容</p>
        <ul>
            <li>列表项1</li>
            <li>列表项2</li>
        </ul>
    </div>
</article>
"""

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

# 提取文章信息
article = soup.find('article', class_='post')

# 标题
title = article.find('h1', class_='title').text
print(f'标题: {title}')

# 作者和日期
author = article.find('span', class_='author').text
date = article.find('span', class_='date').text
print(f'作者: {author}')
print(f'日期: {date}')

# 内容段落
content_div = article.find('div', class_='content')
paragraphs = content_div.find_all('p')
for p in paragraphs:
    print(f'段落: {p.text}')

# 列表项
list_items = content_div.find_all('li')
for li in list_items:
    print(f'列表项: {li.text}')

课后练习

练习1:提取新闻标题

从新闻网站首页提取所有新闻标题和链接。

练习2:提取商品列表

从电商网站提取商品名称、价格、销量等信息。

练习3:提取评论数据

从论坛或评论区提取用户名、评论内容、发布时间。

常见问题

Q1: 如何选择解析器?

A: 推荐使用 lxml 解析器,速度快且容错性好。如果处理格式不规范的 HTML,可以使用 html5lib

Q2: find() 和 find_all() 有什么区别?

A: find() 返回第一个匹配的元素,find_all() 返回所有匹配的元素列表。

Q3: 如何处理动态加载的内容?

A: BeautifulSoup 只能解析静态 HTML,动态加载的内容需要使用 Selenium 或 Playwright。

下一步学习