第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 知识体系图
下面这张图帮你理清本章的核心逻辑:
这张图展示了从安装到最终输出的完整链路。你跟着这个流程走,基本不会迷路。
3.7 本章小结
今天我们掌握了:
- BeautifulSoup的安装与基本用法
find()和find_all()的区别与使用场景- 如何提取标签内的文本和属性
- 一个完整的新闻标题抓取实战
这些是HTML解析的基石。下一章我们会深入更强大的CSS选择器,以及如何处理分页和动态加载的内容。嗯,先消化好今天的内容,动手写几行代码试试看。
公众号:蓝海资料掘金营,微信deep3321