第3章:HTML解析入门:BeautifulSoup库安装、选择器基础(find/find_all)、提取文本与属性、实战:抓取新闻标题列表

各位好,欢迎来到数据采集的核心环节——HTML解析。

说实话,很多新手在拿到网页源码后,第一反应是「这堆标签怎么读?」。我当年也踩过这个坑,用正则表达式硬啃HTML,结果改一次需求就要重写一遍,简直崩溃。后来遇到BeautifulSoup,才明白什么叫「专业的事交给专业的工具」。

3.1 为什么选BeautifulSoup?

Python里解析HTML的库不少,比如lxml、html.parser、pyquery。我个人习惯用BeautifulSoup,原因有三:

  • 容错性强:网页源码经常不标准,缺标签、多属性是常态。BeautifulSoup能自动修复,不会因为一个
  • 没闭合就罢工。
  • 选择器友好:支持CSS选择器、标签名、属性、文本等多种方式定位,写起来像在写自然语言。
  • 社区成熟:遇到问题,StackOverflow上基本都有答案。

说白了,它就是一把瑞士军刀,能处理90%的HTML解析场景。

3.2 安装与快速上手

安装很简单,一行命令搞定:

pip install beautifulsoup4 lxml

这里我建议同时装lxml,因为它是BeautifulSoup的推荐解析器,速度比默认的html.parser快不少。我在项目中遇到过,用默认解析器解析一个500KB的页面要3秒,换成lxml后直接降到0.5秒。

导入方式:

from bs4 import BeautifulSoup

然后,把HTML字符串或文件传进去:

soup = BeautifulSoup(html_content, 'lxml')

嗯,这里要注意:第二个参数指定解析器。如果你不写,BeautifulSoup会自动选一个可用的,但为了稳定,我建议每次都显式指定。

3.3 选择器基础:find与find_all

这两个方法是你最常用的武器。简单说:

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

来看个例子。假设我们有这样的HTML:

<div class="news-list">
  <h2>今日要闻</h2>
  <ul>
    <li class="item">新闻标题一</li>
    <li class="item hot">新闻标题二</li>
    <li class="item">新闻标题三</li>
  </ul>
</div>

用find和find_all来提取:

# 找到第一个li
first_li = soup.find('li')
print(first_li.text)  # 输出:新闻标题一

# 找到所有li
all_li = soup.find_all('li')
for li in all_li:
    print(li.text)
# 输出:新闻标题一、新闻标题二、新闻标题三

你想想看,如果我要找class为"hot"的那个li呢?可以用属性过滤:

hot_li = soup.find('li', class_='hot')
print(hot_li.text)  # 输出:新闻标题二

注意,这里用的是class_,不是class。因为class是Python的保留关键字,BeautifulSoup加了个下划线来区分。我第一次用的时候就写错了,查了半天bug……

3.4 提取文本与属性

拿到标签对象后,我们通常要提取两样东西:文本内容和属性值。

  • 提取文本:用.text.get_text()。前者更简洁,后者可以指定分隔符等参数。
  • 提取属性:用.get('属性名')或直接['属性名']。比如提取链接:a.get('href')

举个例子:

<a href="https://example.com/news/1" class="title">重磅消息</a>
tag = soup.find('a')
print(tag.text)        # 输出:重磅消息
print(tag.get('href')) # 输出:https://example.com/news/1
print(tag['class'])    # 输出:['title']

这里有个坑:如果属性不存在,用['属性名']会报KeyError,而.get()会返回None。所以我个人习惯用.get(),更安全。

3.5 实战:抓取新闻标题列表

理论说完了,咱们来点实际的。假设我们要抓取一个新闻网站的标题列表,页面结构大概是这样的:

<div class="news-container">
  <div class="news-item">
    <h3><a href="/news/1">AI技术突破:新模型超越人类</a></h3>
    <span class="date">2025-01-15</span>
  </div>
  <div class="news-item">
    <h3><a href="/news/2">芯片产能再创新高</a></h3>
    <span class="date">2025-01-14</span>
  </div>
  <!-- 更多新闻项 -->
</div>

完整代码:

from bs4 import BeautifulSoup
import requests

# 获取页面内容(这里用本地HTML模拟)
html_content = """
<div class="news-container">
  <div class="news-item">
    <h3><a href="/news/1">AI技术突破:新模型超越人类</a></h3>
    <span class="date">2025-01-15</span>
  </div>
  <div class="news-item">
    <h3><a href="/news/2">芯片产能再创新高</a></h3>
    <span class="date">2025-01-14</span>
  </div>
</div>
"""

soup = BeautifulSoup(html_content, 'lxml')

# 找到所有新闻项
news_items = soup.find_all('div', class_='news-item')

# 遍历提取标题和链接
for item in news_items:
    title_tag = item.find('a')
    title = title_tag.text.strip()
    link = title_tag.get('href')
    date_tag = item.find('span', class_='date')
    date = date_tag.text.strip() if date_tag else '未知'
    
    print(f'标题:{title}')
    print(f'链接:{link}')
    print(f'日期:{date}')
    print('---')

输出结果:

标题:AI技术突破:新模型超越人类
链接:/news/1
日期:2025-01-15
---
标题:芯片产能再创新高
链接:/news/2
日期:2025-01-14
---

核心要点

  • 先用find_all定位到每个新闻项容器
  • 再在容器内用find提取具体字段
  • .text拿文本,用.get()拿属性

小技巧:如果页面结构复杂,可以先用浏览器开发者工具(F12)查看元素的层级关系,再写选择器。我一般会先在控制台用document.querySelectorAll()测试CSS选择器,确认无误后再写到Python代码里。

避坑指南:我曾经遇到过一个页面,新闻标题里混着广告标签,用find_all('a')抓到了很多无关链接。后来我加了一层父容器过滤,只抓class="news-item"内部的a标签,问题就解决了。所以,定位时尽量精确,别偷懒。

3.6 知识体系图

下面这张图帮你理清本章的核心逻辑:

BeautifulSoup HTML解析核心流程 1. 安装与导入 pip install beautifulsoup4 2. 创建soup对象 soup = BeautifulSoup(html, 'lxml') 3. 选择元素 find() / find_all() 提取文本与属性 .text / .get_text() 获取标签内纯文本 .get('属性名') 获取href、class等属性 输出:标题列表、链接、日期

这张图展示了从安装到最终输出的完整链路。你跟着这个流程走,基本不会迷路。

3.7 本章小结

今天我们掌握了:

  • BeautifulSoup的安装与基本用法
  • find()find_all()的区别与使用场景
  • 如何提取标签内的文本和属性
  • 一个完整的新闻标题抓取实战

这些是HTML解析的基石。下一章我们会深入更强大的CSS选择器,以及如何处理分页和动态加载的内容。嗯,先消化好今天的内容,动手写几行代码试试看。


公众号:蓝海资料掘金营,微信deep3321