BeautifulSoup 是 Python 爬虫领域最经典的HTML/XML 解析库,核心作用是从网页源码中精准提取你想要的数据。 通俗理解:如果把网页比作一本杂乱的书,requests负责把书下载下来,BeautifulSoup 就是帮你快速翻书、定位指定内容的智能工具 —— 不用逐行找、不用写复杂的正则表达式,通过标签名、属性、CSS 选择器就能快速抠出数据,是新手入门爬虫的必学工具。
本文从环境搭建、基础语法到完整实战,全程配可运行代码 + 逐行解释 + 避坑指南,零基础也能一步步上手。
⚠️ 合规提示:本文仅用于技术学习,请遵守目标网站的
robots协议,不要恶意爬取、批量请求他人网站。
一、前置说明
1. BeautifulSoup 的定位
它只能解析已经拿到的网页源码,本身不能下载网页。爬虫的标准流程是:
- 用
requests等库发送请求,下载网页的 HTML 源码 - 把源码交给 BeautifulSoup 解析,提取目标数据
补充:BeautifulSoup 只能处理静态 HTML(浏览器右键 “查看网页源码” 能看到的内容);如果是 JavaScript 动态渲染的内容(比如需要滚动、点击才加载的数据),直接拿源码是看不到的,需要配合 Selenium 或者抓接口。
2. 安装
BeautifulSoup 是第三方库,需要手动安装。注意库名是beautifulsoup4(末尾的 4 是版本号):
pip install beautifulsoup43. 关于解析器
BeautifulSoup 本身只提供解析逻辑,需要搭配解析器使用,新手记住两种即可:
| 解析器 | 说明 | 推荐度 |
|---|---|---|
html.parser | Python 内置,无需额外安装,足够应对绝大多数场景 | ⭐⭐⭐⭐⭐(新手首选) |
lxml | 第三方解析器,解析速度更快,需要单独安装pip install lxml | ⭐⭐⭐⭐ |
本文所有例子都用内置的html.parser,不用额外装东西。
二、快速入门:第一个解析示例
我们先不用联网,用一段手写的 HTML 字符串演示,直观感受 BeautifulSoup 的用法。
完整入门代码
# 1. 导入库:从 bs4 包里导入 BeautifulSoup 类 from bs4 import BeautifulSoup # 2. 准备一段HTML源码(模拟我们下载到的网页内容) html_doc = """ <html> <head> <title>测试网页标题</title> </head> <body> <h1 class="main-title">欢迎来到测试网站</h1> <p class="intro">这是一个用于测试BeautifulSoup的示例网页。</p> <ul class="news-list"> <li><a href="news/1.html" class="news-link">第一条新闻</a></li> <li><a href="news/2.html" class="news-link">第二条新闻</a></li> <li><a href="news/3.html" class="news-link">第三条新闻</a></li> </ul> <div id="footer"> <p>版权所有 © 2024</p> </div> </body> </html> """ # 3. 创建BeautifulSoup解析对象 # 第一个参数:HTML源码字符串 # 第二个参数:解析器,这里用内置的html.parser soup = BeautifulSoup(html_doc, "html.parser") # 4. 提取数据:获取网页标题 title = soup.title.string print("网页标题:", title) # 5. 提取所有新闻链接的文本和地址 links = soup.find_all("a", class_="news-link") print("\n所有新闻:") for link in links: # .text 获取标签内的文字;.get("href") 获取href属性的值 print(f"标题:{link.text},链接:{link.get('href')}")运行结果:
网页标题: 测试网页标题 所有新闻: 标题:第一条新闻,链接:news/1.html 标题:第二条新闻,链接:news/2.html 标题:第三条新闻,链接:news/3.html逐行解释核心步骤
BeautifulSoup(html_doc, "html.parser"):把字符串格式的 HTML,变成结构化的「解析对象」,之后所有提取操作都通过这个对象完成soup.title:直接找到页面里第一个<title>标签.string:获取标签里的文本内容soup.find_all("a", class_="news-link"):找到所有class="news-link"的<a>标签,返回列表.get("href"):安全获取标签的href属性值
三、核心解析方法(重点必学)
BeautifulSoup 提供了多种提取数据的方法,新手优先掌握最常用的 3 类:标签直接选择、find()/find_all()、CSS 选择器select()。
1. 直接通过标签名选择
最简单的写法:soup.标签名,直接找到页面中第一个匹配的标签。
示例
from bs4 import BeautifulSoup soup = BeautifulSoup(html_doc, "html.parser") # 获取第一个h1标签 h1 = soup.h1 print(h1) # <h1 class="main-title">欢迎来到测试网站</h1> # 获取第一个a标签 first_a = soup.a print(first_a) # <a href="news/1.html" class="news-link">第一条新闻</a>⚠️ 注意:这种方式只能拿到第一个匹配的标签,适合页面里只有一个该标签的场景(比如 title);要找多个必须用后面的find_all()。
2. find () 和 find_all ()(最核心最常用)
这是 BeautifulSoup 最核心的两个方法,90% 的提取场景都用它们。
| 方法 | 作用 | 返回值 |
|---|---|---|
find() | 找第一个符合条件的标签 | 单个标签对象,找不到返回 None |
find_all() | 找所有符合条件的标签 | 标签对象组成的列表,找不到返回空列表 |
通用语法
soup.find(name="标签名", attrs={"属性名": "属性值"}, text="文本内容") soup.find_all(name="标签名", attrs={"属性名": "属性值"}, text="文本内容")用法 1:只按标签名查找
# 找到所有的li标签 all_li = soup.find_all("li") print(f"找到{len(all_li)}个li标签") # 找到第一个p标签 first_p = soup.find("p") print("第一个p标签内容:", first_p.text)用法 2:按属性查找(最常用)
通过标签的class、id、href等属性精准定位,有两种写法:
写法 A:通过attrs字典传属性(通用写法)
# 找到id为footer的div标签 footer = soup.find("div", attrs={"id": "footer"}) print(footer.text.strip()) # 版权所有 © 2024 # 找到所有class为news-link的a标签 links = soup.find_all("a", attrs={"class": "news-link"})写法 B:关键字参数简写(更常用)
# 按id查找 footer = soup.find("div", id="footer") # 按class查找:注意class后面要加下划线_! # 因为class是Python的关键字,直接写会语法报错 links = soup.find_all("a", class_="news-link")⚠️ 新手头号坑:
class是 Python 关键字,作为参数必须写成class_(加下划线),否则直接语法报错!
用法 3:按文本内容查找
通过text参数匹配标签里的文字:
# 找到文本为"第二条新闻"的a标签 target = soup.find("a", text="第二条新闻") print(target.get("href")) # news/2.html注意:
text是完全匹配,必须和标签里的文字一模一样才行;模糊匹配需要配合正则表达式。
3. 获取标签的文本和属性
找到标签对象后,我们最常用两个操作:拿文字、拿属性值。
① 获取文本内容
| 写法 | 作用 | 推荐度 |
|---|---|---|
.text | 获取标签内所有文本(包括子标签里的文字),自动拼接 | ⭐⭐⭐⭐⭐(最常用) |
.get_text() | 和.text 效果一致,还可以加参数控制分隔符 | ⭐⭐⭐⭐ |
.string | 仅当标签内只有纯文本、没有子标签时才能拿到,否则返回 None | ⭐⭐ |
示例:
div = soup.find("div", id="footer") print(div.text) # 版权所有 © 2024 print(div.get_text(strip=True)) # 自动去掉首尾空白② 获取属性值
表格
| 写法 | 作用 | 推荐度 |
|---|---|---|
.get("属性名") | 属性存在返回值,不存在返回 None,不会报错 | ⭐⭐⭐⭐⭐(安全首选) |
["属性名"] | 属性不存在直接报错 | ⭐⭐ |
示例:
a_tag = soup.find("a") print(a_tag.get("href")) # news/1.html print(a_tag.get("class")) # ['news-link'] print(a_tag.get("xxx")) # None(属性不存在,不报错)4. CSS 选择器:select () 方法
如果你懂前端 CSS 选择器,用select()会非常顺手,语法和 CSS 完全一致,返回匹配的所有元素列表。
常用选择器对应写法
| 选择器类型 | 示例 | 作用 |
|---|---|---|
| 标签选择器 | soup.select("a") | 找到所有 a 标签 |
| class 选择器 | soup.select(".news-link") | 找到所有 class 为 news-link 的元素 |
| id 选择器 | soup.select("#footer") | 找到 id 为 footer 的元素 |
| 后代选择器 | soup.select("ul li a") | 找到 ul 里面的 li 里面的 a |
| 子选择器 | soup.select("ul > li") | 找到 ul 的直接子元素 li |
示例
# 找到所有新闻链接 links = soup.select("ul.news-list a.news-link") for link in links: print(link.text, link.get("href")) # 找到id为footer的div里的p标签 footer_p = soup.select("#footer p") print(footer_p[0].text)补充:
select()永远返回列表,哪怕只有一个结果也要用[0]取出来;如果只要第一个,可以用select_one(),返回单个元素。
四、进阶:标签层级导航
有时候我们先找到一个父标签,再在它内部找子标签,这就是「相对查找」,可以避免页面其他同名标签的干扰。
在标签内部继续查找
标签对象本身也支持find()、find_all()、select()等所有方法,只在当前标签的内部查找:
# 先找到整个ul父标签 ul_tag = soup.find("ul", class_="news-list") # 只在ul内部找a标签,不会找到页面其他地方的a links = ul_tag.find_all("a")其他层级属性(了解即可)
.parent:获取当前标签的父标签.children:获取所有直接子标签(迭代器).next_sibling/.previous_sibling:下一个 / 上一个同级兄弟标签
五、完整实战:requests + BeautifulSoup 爬取练习网站
我们用专门的爬虫练习网站https://quotes.toscrape.com/做完整实战,这个网站没有反爬、结构清晰,专门供新手练习。
需求
爬取第一页的所有名言、作者、标签。
完整代码 + 逐行解释
# 1. 导入需要的库 import requests from bs4 import BeautifulSoup # 2. 目标网址 url = "https://quotes.toscrape.com/" # 3. 发送GET请求,获取网页响应 # 加User-Agent模拟浏览器,避免被拦截 headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" } response = requests.get(url, headers=headers) # 4. 设置编码,防止中文乱码 response.encoding = "utf-8" # 5. 把响应的HTML文本交给BeautifulSoup解析 soup = BeautifulSoup(response.text, "html.parser") # 6. 定位所有名言条目:每个名言都在class为quote的div里 quote_items = soup.find_all("div", class_="quote") print(f"共找到{len(quote_items)}条名言:\n") # 7. 遍历每条,提取详细信息 for index, item in enumerate(quote_items, 1): # 提取名言内容:在class为text的span里 text = item.find("span", class_="text").text.strip("“”") # 提取作者:在class为author的small标签里 author = item.find("small", class_="author").text # 提取标签:所有class为tag的a标签 tags = [tag.text for tag in item.find_all("a", class_="tag")] # 打印结果 print(f"【第{index}条】") print(f"名言:{text}") print(f"作者:{author}") print(f"标签:{', '.join(tags)}") print("-"*50)运行效果
共找到10条名言: 【第1条】 名言:The world as we have created it is a process of our thinking. 作者:Albert Einstein 标签:change, deep-thoughts, thinking, world -------------------------------------------------- ...关键步骤说明
- 加请求头:
User-Agent模拟浏览器,防止网站识别为爬虫直接拒绝 - 编码设置:
response.encoding = "utf-8"避免中文乱码;如果不确定编码,可以用response.apparent_encoding自动识别 - 先找父容器,再找子元素:先定位每个名言的大盒子
div.quote,再在里面分别提取文字、作者、标签,结构清晰不容易错
六、新手高频易错点 & 避坑指南
1.class忘加下划线
❌ 错误:soup.find("a", class="news-link")✅ 正确:soup.find("a", class_="news-link")原因:class是 Python 关键字,不能直接当参数名。
2.find_all()结果直接.text报错
find_all()返回的是列表,不是单个标签对象,不能直接.text,必须遍历或者用下标取单个元素。 ❌ 错误:soup.find_all("a").text✅ 正确:
links = soup.find_all("a") for link in links: print(link.text)3. 找不到元素,先看源码对不对
如果定位语法没错但就是找不到元素,先打印soup.prettify()看看拿到的源码是不是和浏览器里的一样:
- 如果源码里根本没有这个内容 → 是 JS 动态渲染的,BeautifulSoup 搞不定,需要用 Selenium 或者抓接口
- 如果源码里有 → 检查标签名、属性名有没有写错,注意大小写、拼写
4. 属性值有多个 class 时的匹配问题
如果一个标签有多个 class,比如<div class="item active">,用class_="item"是可以匹配到的,BeautifulSoup 会匹配单个 class。
5. 网页乱码
拿到的内容中文全是乱码,基本都是编码没设置对:
# 手动指定编码 response.encoding = "utf-8" # 或者自动识别编码 response.encoding = response.apparent_encoding七、调试小技巧
print(soup.prettify())可以把解析后的 HTML 格式化输出,结构清晰,调试的时候用来检查拿到的源码对不对、元素结构是什么样的,非常好用。
八、核心总结
- 定位:BeautifulSoup 是解析工具,负责从 HTML 源码里提取数据,配合 requests 下载网页使用
- 核心方法:
- 找单个:
find() - 找所有:
find_all() - CSS 选择器:
select()/select_one()
- 找单个:
- 取值:
- 文本:
.text/.get_text() - 属性:
.get("属性名")
- 文本:
- 避坑:
class_加下划线、find_all 返回列表要遍历、动态页面用不了