Day 28:【99天精通Python】HTML解析库 BeautifulSoup - 像喝汤一样提取网页数据
前言
欢迎来到第28天!
在昨天的课程中,我们学会了用requests库把网页源代码(HTML)下载下来。但是,打印出来的response.text是一大坨密密麻麻的 HTML 标签,看得人头皮发麻。
比如你想提取网页中所有的"新闻标题",如果用正则表达式去匹配<h3>(.*?)</h3>,不仅写起来麻烦,而且一旦网页结构稍微变一下(比如标签里多了个 class),正则就失效了。
今天介绍的BeautifulSoup(美味的汤),就是一个专门用来"解析" HTML 的库。它能把乱七八糟的 HTML 代码变成一个复杂的树形结构,你只需要告诉它:“给我找所有的<h3>标签”,或者"给我找id='content'的那个div",它就能乖乖把数据端到你面前。
本节内容:
- 安装 BeautifulSoup4
- 解析 HTML 结构
- 核心方法
find()与find_all() - CSS 选择器
select() - 提取文本与属性
- 实战练习:爬取豆瓣电影Top250(简易版)
一、安装与准备
BeautifulSoup 是第三方库,通常配合lxml解析器使用(速度快,容错能力强)。
pipinstallbeautifulsoup4 lxml1.1 初始化 Soup 对象
我们先用一段简单的 HTML 字符串来演示。
frombs4importBeautifulSoup html_doc=""" <html> <head><title>这是网页标题</title></head> <body> <p class="title"><b>故事是从这里开始的</b></p> <p class="story"> 曾经有三个小伙伴: <a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>, <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> 和 <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>; 他们生活在井底。 </p> </body> </html> """# 创建 BeautifulSoup 对象# 第二个参数指定解析器 'lxml'soup=BeautifulSoup(html_doc,'lxml')# 格式化输出看看结构print(soup.prettify())二、遍历文档树:指哪打哪
2.1 直接访问标签
可以直接通过.来访问标签,这会返回第一个匹配到的标签。
print(soup.title)# <title>这是网页标题</title>print(soup.title.string)# 这是网页标题print(soup.a)# <a ...>Elsie</a> (只返回第一个a标签)print(soup.p)# <p class="title">...</p>三、搜索文档树:find 与 find_all (重点)
这是最常用的两个方法。
3.1 find_all():查找所有匹配项
返回一个列表。
# 1. 查找所有 <a> 标签links=soup.find_all('a')forlinkinlinks:print(link)# 2. 查找所有 class="sister" 的标签# 注意:class 是 Python 关键字,所以参数名要写成 class_sisters=soup.find_all(class_='sister')# 3. 查找 id="link1" 的标签link1=soup.find_all(id='link1')3.2 find():查找第一个匹配项
返回单个标签对象(如果找不到返回None)。
# 查找第一个 <b> 标签b_tag=soup.find('b')print(b_tag.string)# 故事是从这里开始的3.3 混合搜索
# 查找 id=link2 的 a 标签target=soup.find('a',id='link2')print(target)四、CSS 选择器:select (推荐)
如果你熟悉前端开发(CSS/jQuery),你会爱死select()方法。它允许你用 CSS 选择器语法来查找元素。
| 选择器 | 语法 | 示例 |
|---|---|---|
| 标签选择器 | tag | soup.select('a') |
| 类选择器 | .class | soup.select('.sister') |
| ID选择器 | #id | soup.select('#link1') |
| 层级选择器 | parent child | soup.select('p a')(p标签下的所有a) |
| 属性选择器 | [attr] | soup.select('a[href^="http"]') |
# select 返回的是列表tags=soup.select('p.story > a')# p标签(class=story)下的直接子标签aprint(tags)# select_one 返回的是单个对象 (等同于 find)tag=soup.select_one('#link3')print(tag.string)五、提取数据:拿走肉,留下汤
找到标签后,我们的目标通常是里面的文字或链接。
5.1 获取文本get_text()
p=soup.select_one('.story')# get_text() 会自动去除标签,只提取文字print(p.get_text())# 输出:曾经有三个小伙伴:Elsie, Lacie 和 Tillie; 他们生活在井底。# 或者用 .string 属性 (如果标签内还有子标签,.string 可能是 None)print(soup.title.string)5.2 获取属性get('attr')
link=soup.select_one('#link1')# 像字典一样取值url=link.get('href')# 或者 link['href']print(url)# http://example.com/elsie六、实战练习:爬取豆瓣电影 Top250
这是一个经典的爬虫练习。我们要提取每部电影的标题、评分和引言。
注意:豆瓣有反爬机制,必须带
User-Agent。
importrequestsfrombs4importBeautifulSoupdefscrape_douban():url="https://movie.douban.com/top250"headers={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"}try:response=requests.get(url,headers=headers)ifresponse.status_code!=200:print("请求失败")returnsoup=BeautifulSoup(response.text,'lxml')# 1. 找到所有电影的条目 (每个电影都在一个 class="item" 的 div 里)items=soup.select('.item')print(f"本页共找到{len(items)}部电影:\n")foriteminitems:# 2. 提取标题 (span class="title")title=item.select_one('.title').get_text()# 3. 提取评分 (span class="rating_num")rating=item.select_one('.rating_num').get_text()# 4. 提取引言 (span class="inq"),有的电影可能没有引言,要做防错处理quote_tag=item.select_one('.inq')quote=quote_tag.get_text()ifquote_tagelse"无"print(f"《{title}》 评分:{rating}| 简评:{quote}")exceptExceptionase:print(f"发生错误:{e}")# 运行 (请勿频繁请求,以免被封IP)# scrape_douban()七、常见问题
Q1:find和select哪个好?
find/find_all:Python 风格,参数直观,适合简单的查找。select/select_one:CSS 风格,适合复杂的层级查找(比如找div下的ul下的第3个li)。
建议:掌握select,因为它和前端逻辑一致,通用性更强。
Q2:.text和.string的区别?
.string: 只有当标签内只有纯文本(没有子标签)时才返回文本,否则返回None。.text/get_text(): 无论有多少子标签,都会把里面所有的文本拼起来返回。通常用这个。
Q3:为什么解析结果和浏览器F12看到的不一样?
浏览器 F12 看到的是 JS 执行后渲染的最终结果。而requests拿到的只是服务器返回的原始 HTML。如果网页内容是 JS 动态加载的(比如 AJAX),BeautifulSoup 就抓不到了(需要用到 Selenium 或分析 API,后续课程会讲)。
八、小结
关键要点:
- BeautifulSoup是 HTML 解析神器,配合
requests使用。 soup.select()支持 CSS 选择器,非常强大。- 提取三要素:找节点、取文本、取属性。
九、课后作业
- 博客标题爬取:找一个你喜欢的技术博客网站(如 CSDN 首页或某个专栏),爬取首页所有的文章标题和链接。
- 古诗词抓取:在一个古诗词网站(如古诗文网),搜索"李白",抓取搜索结果中前 5 首诗的题目和作者。
- 图片批量下载器升级:结合 Day 27 的知识,先用 BeautifulSoup 解析网页找到所有
<img>标签的src,然后用requests批量下载图片。
下节预告
Day 29:数据持久化 - CSV与Excel- 爬下来的数据光打印在屏幕上没用,我们要把它们存进 Excel 表格里,方便后续分析和汇报!
系列导航:
- 上一篇:Day 27 - HTTP协议与Requests库
- 下一篇:Day 29 - 数据持久化CSV与Excel(待更新)