Python爬虫第3课:BeautifulSoup解析HTML与数据提取

目录

    • 课程目标
    • [1. BeautifulSoup简介](#1. BeautifulSoup简介)
      • [1.1 安装BeautifulSoup](#1.1 安装BeautifulSoup)
      • [1.2 基本使用](#1.2 基本使用)
    • [2. HTML文档结构理解](#2. HTML文档结构理解)
      • [2.1 HTML基本结构](#2.1 HTML基本结构)
      • [2.2 DOM树概念](#2.2 DOM树概念)
    • [3. 基本查找方法](#3. 基本查找方法)
      • [3.1 按标签名查找](#3.1 按标签名查找)
      • [3.2 按属性查找](#3.2 按属性查找)
      • [3.3 按文本内容查找](#3.3 按文本内容查找)
    • [4. CSS选择器](#4. CSS选择器)
      • [4.1 基本CSS选择器](#4.1 基本CSS选择器)
      • [4.2 高级CSS选择器](#4.2 高级CSS选择器)
    • [5. 导航文档树](#5. 导航文档树)
      • [5.1 父子关系导航](#5.1 父子关系导航)
      • [5.2 兄弟关系导航](#5.2 兄弟关系导航)
    • [6. 提取数据](#6. 提取数据)
      • [6.1 获取文本内容](#6.1 获取文本内容)
      • [6.2 获取属性值](#6.2 获取属性值)
    • [7. 实战案例:爬取新闻列表](#7. 实战案例:爬取新闻列表)
    • [8. 处理特殊情况](#8. 处理特殊情况)
      • [8.1 处理编码问题](#8.1 处理编码问题)
      • [8.2 处理JavaScript生成的内容](#8.2 处理JavaScript生成的内容)
      • [8.3 处理表格数据](#8.3 处理表格数据)
    • [9. 性能优化技巧](#9. 性能优化技巧)
      • [9.1 选择合适的解析器](#9.1 选择合适的解析器)
      • [9.2 限制解析范围](#9.2 限制解析范围)
    • [10. 实践练习](#10. 实践练习)
    • [11. 课程小结](#11. 课程小结)
    • [12. 下节预告](#12. 下节预告)
    • [13. 作业](#13. 作业)

专栏导读

🌸 欢迎来到Python办公自动化专栏---Python处理办公问题,解放您的双手
🏳️‍🌈 个人博客主页:请点击------> 个人的博客主页 求收藏
🏳️‍🌈 Github主页:请点击------> Github主页 求Star⭐
🏳️‍🌈 知乎主页:请点击------> 知乎主页 求关注
🏳️‍🌈 CSDN博客主页:请点击------> CSDN的博客主页 求关注
👍 该系列文章专栏:请点击------>Python办公自动化专栏 求订阅
🕷 此外还有爬虫专栏:请点击------>Python爬虫基础专栏 求订阅
📕 此外还有python基础专栏:请点击------>Python基础学习专栏 求订阅
文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏
❤️ 欢迎各位佬关注! ❤️

课程目标

  • 掌握BeautifulSoup库的基本使用方法
  • 学会使用各种选择器定位HTML元素
  • 理解HTML文档的树形结构
  • 掌握从复杂HTML中提取数据的技巧

1. BeautifulSoup简介

BeautifulSoup是一个用于解析HTML和XML文档的Python库,它能够创建一个解析树,用于从HTML文档中提取数据。

1.1 安装BeautifulSoup

bash 复制代码
pip install beautifulsoup4
pip install lxml  # 推荐的解析器

1.2 基本使用

python 复制代码
from bs4 import BeautifulSoup
import requests

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

# 创建BeautifulSoup对象
soup = BeautifulSoup(html_content, 'html.parser')
# 或者使用lxml解析器(更快)
soup = BeautifulSoup(html_content, 'lxml')

2. HTML文档结构理解

2.1 HTML基本结构

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <title>页面标题</title>
    <meta charset="UTF-8">
</head>
<body>
    <div class="container">
        <h1 id="main-title">主标题</h1>
        <p class="content">段落内容</p>
        <ul>
            <li>列表项1</li>
            <li>列表项2</li>
        </ul>
    </div>
</body>
</html>

2.2 DOM树概念

HTML文档可以看作一个树形结构,每个HTML标签都是树的一个节点。

3. 基本查找方法

3.1 按标签名查找

python 复制代码
from bs4 import BeautifulSoup

html = """
<html>
<body>
    <h1>标题1</h1>
    <h1>标题2</h1>
    <p>段落1</p>
    <p>段落2</p>
</body>
</html>
"""

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

# 查找第一个h1标签
first_h1 = soup.find('h1')
print(first_h1.text)  # 输出:标题1

# 查找所有h1标签
all_h1 = soup.find_all('h1')
for h1 in all_h1:
    print(h1.text)

# 查找所有p标签
all_p = soup.find_all('p')
print(len(all_p))  # 输出:2

3.2 按属性查找

python 复制代码
html = """
<div class="container">
    <p class="intro">介绍段落</p>
    <p class="content">内容段落</p>
    <p id="special">特殊段落</p>
</div>
"""

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

# 按class查找
intro = soup.find('p', class_='intro')
print(intro.text)

# 按id查找
special = soup.find('p', id='special')
print(special.text)

# 按多个属性查找
content = soup.find('p', {'class': 'content'})
print(content.text)

3.3 按文本内容查找

python 复制代码
# 查找包含特定文本的标签
link = soup.find('a', string='首页')

# 使用正则表达式查找文本
import re
pattern = re.compile(r'联系.*')
contact_link = soup.find('a', string=pattern)

4. CSS选择器

4.1 基本CSS选择器

python 复制代码
html = """
<div class="container">
    <h1 id="title">主标题</h1>
    <div class="content">
        <p class="text">段落1</p>
        <p class="text highlight">段落2</p>
        <ul>
            <li>项目1</li>
            <li class="special">项目2</li>
        </ul>
    </div>
</div>
"""

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

# 标签选择器
titles = soup.select('h1')

# ID选择器
title = soup.select('#title')[0]

# 类选择器
texts = soup.select('.text')

# 属性选择器
special_items = soup.select('[class="special"]')

# 后代选择器
content_paragraphs = soup.select('.content p')

# 子元素选择器
direct_children = soup.select('.container > .content')

# 多类选择器
highlighted = soup.select('.text.highlight')

4.2 高级CSS选择器

python 复制代码
# 伪类选择器
first_li = soup.select('li:first-child')
last_li = soup.select('li:last-child')
nth_li = soup.select('li:nth-child(2)')

# 属性包含选择器
partial_class = soup.select('[class*="tex"]')

# 属性开始选择器
starts_with = soup.select('[class^="con"]')

# 属性结束选择器
ends_with = soup.select('[class$="ent"]')

5. 导航文档树

5.1 父子关系导航

python 复制代码
html = """
<div class="parent">
    <p>第一个段落</p>
    <p>第二个段落</p>
    <div class="child">
        <span>子元素</span>
    </div>
</div>
"""

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

# 获取父元素
span = soup.find('span')
parent_div = span.parent
print(parent_div['class'])  # ['child']

# 获取所有父元素
parents = span.parents
for parent in parents:
    if parent.name:
        print(parent.name)

# 获取子元素
parent_div = soup.find('div', class_='parent')
children = parent_div.children  # 生成器对象
for child in children:
    if child.name:  # 过滤文本节点
        print(child.name)

# 获取所有后代元素
descendants = parent_div.descendants

5.2 兄弟关系导航

python 复制代码
# 获取下一个兄弟元素
first_p = soup.find('p')
next_sibling = first_p.next_sibling
print(next_sibling)

# 获取上一个兄弟元素
second_p = first_p.next_sibling.next_sibling
prev_sibling = second_p.previous_sibling

# 获取所有后续兄弟元素
next_siblings = first_p.next_siblings
for sibling in next_siblings:
    if sibling.name:
        print(sibling.name)

6. 提取数据

6.1 获取文本内容

python 复制代码
html = """
<div class="article">
    <h1>文章标题</h1>
    <p>这是第一段内容。</p>
    <p>这是第二段内容。</p>
    <div class="meta">
        <span>作者:张三</span>
        <span>时间:2024-01-01</span>
    </div>
</div>
"""

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

# 获取单个元素的文本
title = soup.find('h1').text
print(title)  # 文章标题

# 获取元素及其子元素的所有文本
article = soup.find('div', class_='article')
all_text = article.get_text()
print(all_text)

# 获取文本时指定分隔符
clean_text = article.get_text(separator=' | ', strip=True)
print(clean_text)

6.2 获取属性值

python 复制代码
html = """
<div class="container">
    <a href="https://example.com" title="示例网站" target="_blank">链接</a>
    <img src="image.jpg" alt="图片描述" width="300" height="200">
</div>
"""

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

# 获取单个属性
link = soup.find('a')
href = link.get('href')  # 或者 link['href']
title = link.get('title')

# 获取所有属性
img = soup.find('img')
attrs = img.attrs
print(attrs)  # {'src': 'image.jpg', 'alt': '图片描述', 'width': '300', 'height': '200'}

# 检查属性是否存在
if link.has_attr('target'):
    print("链接有target属性")

7. 实战案例:爬取新闻列表

python 复制代码
import requests
from bs4 import BeautifulSoup
import csv
from datetime import datetime

class NewsSpider:
    def __init__(self):
        self.session = requests.Session()
        self.session.headers.update({
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
        })
    
    def get_news_list(self, url):
        """获取新闻列表"""
        try:
            response = self.session.get(url, timeout=10)
            response.raise_for_status()
            
            soup = BeautifulSoup(response.text, 'html.parser')
            return self.parse_news_list(soup)
            
        except Exception as e:
            print(f"获取新闻列表失败:{e}")
            return []
    
    def parse_news_list(self, soup):
        """解析新闻列表"""
        news_list = []
        
        # 假设新闻列表在class为'news-list'的div中
        news_container = soup.find('div', class_='news-list')
        if not news_container:
            return news_list
        
        # 查找所有新闻项
        news_items = news_container.find_all('div', class_='news-item')
        
        for item in news_items:
            news_data = self.extract_news_data(item)
            if news_data:
                news_list.append(news_data)
        
        return news_list
    
    def extract_news_data(self, item):
        """提取单条新闻数据"""
        try:
            # 提取标题和链接
            title_link = item.find('a', class_='title')
            if not title_link:
                return None
            
            title = title_link.get_text(strip=True)
            link = title_link.get('href')
            
            # 提取摘要
            summary_elem = item.find('p', class_='summary')
            summary = summary_elem.get_text(strip=True) if summary_elem else ''
            
            # 提取时间
            time_elem = item.find('span', class_='time')
            publish_time = time_elem.get_text(strip=True) if time_elem else ''
            
            # 提取作者
            author_elem = item.find('span', class_='author')
            author = author_elem.get_text(strip=True) if author_elem else ''
            
            return {
                'title': title,
                'link': link,
                'summary': summary,
                'publish_time': publish_time,
                'author': author,
                'crawl_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            }
            
        except Exception as e:
            print(f"提取新闻数据失败:{e}")
            return None
    
    def save_to_csv(self, news_list, filename='news.csv'):
        """保存数据到CSV文件"""
        if not news_list:
            print("没有数据需要保存")
            return
        
        with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
            fieldnames = ['title', 'link', 'summary', 'publish_time', 'author', 'crawl_time']
            writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
            
            writer.writeheader()
            for news in news_list:
                writer.writerow(news)
        
        print(f"数据已保存到 {filename}")

# 使用示例
if __name__ == "__main__":
    spider = NewsSpider()
    news_list = spider.get_news_list('https://example-news-site.com')
    spider.save_to_csv(news_list)

8. 处理特殊情况

8.1 处理编码问题

python 复制代码
import requests
from bs4 import BeautifulSoup
import chardet

def get_soup_with_encoding(url):
    """自动检测编码并创建soup对象"""
    response = requests.get(url)
    
    # 检测编码
    detected = chardet.detect(response.content)
    encoding = detected['encoding']
    
    # 使用检测到的编码解码
    html = response.content.decode(encoding)
    
    return BeautifulSoup(html, 'html.parser')

8.2 处理JavaScript生成的内容

python 复制代码
# BeautifulSoup只能解析静态HTML
# 对于JavaScript生成的内容,需要使用Selenium等工具
# 这里展示如何识别这种情况

def check_js_content(soup):
    """检查页面是否包含JavaScript生成的内容"""
    scripts = soup.find_all('script')
    
    for script in scripts:
        if script.string and 'document.write' in script.string:
            print("页面包含JavaScript生成的内容")
            return True
    
    return False

8.3 处理表格数据

python 复制代码
def parse_table(soup, table_selector):
    """解析HTML表格"""
    table = soup.select_one(table_selector)
    if not table:
        return []
    
    rows = []
    
    # 获取表头
    headers = []
    header_row = table.find('tr')
    if header_row:
        for th in header_row.find_all(['th', 'td']):
            headers.append(th.get_text(strip=True))
    
    # 获取数据行
    for row in table.find_all('tr')[1:]:  # 跳过表头
        row_data = {}
        cells = row.find_all(['td', 'th'])
        
        for i, cell in enumerate(cells):
            if i < len(headers):
                row_data[headers[i]] = cell.get_text(strip=True)
        
        if row_data:
            rows.append(row_data)
    
    return rows

9. 性能优化技巧

9.1 选择合适的解析器

python 复制代码
# 不同解析器的特点:
# html.parser: Python内置,容错性好,速度中等
# lxml: 速度快,功能强大,需要安装
# xml: 只能解析XML,速度快
# html5lib: 最好的容错性,速度慢

# 推荐使用lxml
soup = BeautifulSoup(html, 'lxml')

9.2 限制解析范围

python 复制代码
# 只解析需要的部分
from bs4 import SoupStrainer

# 只解析div标签
parse_only = SoupStrainer("div")
soup = BeautifulSoup(html, "lxml", parse_only=parse_only)

# 只解析特定class的元素
parse_only = SoupStrainer("div", class_="content")
soup = BeautifulSoup(html, "lxml", parse_only=parse_only)

10. 实践练习

练习1:爬取商品信息

编写程序爬取电商网站的商品列表,提取商品名称、价格、评分等信息。

练习2:解析论坛帖子

爬取论坛的帖子列表,提取标题、作者、回复数、发布时间等信息。

练习3:提取表格数据

从包含表格的网页中提取结构化数据,并保存为CSV格式。

11. 课程小结

本课程我们学习了:

  1. BeautifulSoup库的基本使用方法
  2. HTML文档的树形结构理解
  3. 各种元素查找方法
  4. CSS选择器的使用
  5. 文档树的导航方法
  6. 数据提取技巧
  7. 实战案例和特殊情况处理

12. 下节预告

下一课我们将学习:

  • XPath表达式的使用
  • lxml库的高级功能
  • 处理XML文档
  • 更复杂的数据提取场景

13. 作业

  1. 使用BeautifulSoup爬取一个新闻网站的文章列表
  2. 练习使用各种CSS选择器定位元素
  3. 编写一个通用的表格数据提取器
  4. 处理包含特殊字符和编码的网页

提示:BeautifulSoup是网页数据提取的核心工具,熟练掌握各种选择器和导航方法是关键。

结尾

希望对初学者有帮助;致力于办公自动化的小小程序员一枚
希望能得到大家的【❤️一个免费关注❤️】感谢!
求个 🤞 关注 🤞 +❤️ 喜欢 ❤️ +👍 收藏 👍
此外还有办公自动化专栏,欢迎大家订阅:Python办公自动化专栏
此外还有爬虫专栏,欢迎大家订阅:Python爬虫基础专栏
此外还有Python基础专栏,欢迎大家订阅:Python基础学习专栏
相关推荐
好家伙VCC4 小时前
**发散创新:渗透测试方法的深度探索与实践**随着网络安全形势日益严峻,渗透测试作为评估系统安全的
java·python·安全·web安全·系统安全
机器学习之心4 小时前
一个基于无干扰增量容量(IC)和差分电压(DV)分析的锂离子电池健康状态(SOH)与剩余寿命(RUL)预测的Python实现
python
Bellafu66610 小时前
selenium常用的等待有哪些?
python·selenium·测试工具
小白学大数据11 小时前
Python爬虫常见陷阱:Ajax动态生成内容的URL去重与数据拼接
爬虫·python·ajax
2401_8414956412 小时前
【计算机视觉】基于复杂环境下的车牌识别
人工智能·python·算法·计算机视觉·去噪·车牌识别·字符识别
Adorable老犀牛13 小时前
阿里云-ECS实例信息统计并发送统计报告到企业微信
python·阿里云·云计算·企业微信
倔强青铜三13 小时前
苦练Python第66天:文件操作终极武器!shutil模块完全指南
人工智能·python·面试
倔强青铜三13 小时前
苦练Python第65天:CPU密集型任务救星!多进程multiprocessing模块实战解析,攻破GIL限制!
人工智能·python·面试
Panda__Panda13 小时前
docker项目打包演示项目(数字排序服务)
运维·javascript·python·docker·容器·c#