查看全课大纲(第 47 / 51 节)
- 1.为什么人人都该学点编程?
- 2.计算机与程序设计:你的电脑到底在“想”什么?
- 3.Python 这门语言,到底有什么魔力?
- 4.Python 开发环境配置:如何拥有你的第一个“编程工具箱”?
- 5.如何安装一个更强大的代码编辑器?
- 6.汇率兑换1.0:你的第一个“钱生钱”小程序
- 7.你的第一行代码,藏着哪些秘密?
- 8.汇率兑换2.0(上):让程序看懂“CNY”和“USD”
- 9.汇率兑换程序,如何让它“聪明”地判断币种?
- 10.汇率兑换程序能一直运行,直到我喊停吗?
- 11.汇率兑换4.0:如何把“算钱”功能打包成积木?
- 12.汇率兑换5.0:给你的程序穿上“结构化”的西装
- 13.怎么用 Python 画一个会动的五角星?
- 14.如何让程序自动画出不同大小的五角星?
- 15.函数自己调用自己,会发生什么?
- 16.用递归画出神奇的分形树
- 17.Python 怎么计算你一天躺着不动能消耗多少热量?
- 18.如何让程序“活”起来,持续为你服务?
- 19.如何让 BMR 计算器更“聪明”?
- 20.当程序遇到“不按套路出牌”的用户怎么办?
- 21.怎么用 Python 模拟一个“滚雪球”式的存钱计划?
- 22.如何用列表记录每一周的存款?
- 23.如何让程序自己数数?—— 用 for 循环解放你的双手
- 24.52周存钱挑战4.0:你的存款计算器能有多灵活?
- 25.52周存钱挑战:怎么根据日期自动查存款?
- 26.Python 怎么判断一年中的第几天?
- 27.列表 vs 元组:哪个更适合存储月份天数?
- 28.用集合给月份分个队,换个思路算天数
- 29.把月份和天数“锁”在一起:用字典给数据配个对
- 30.Python 怎么判断密码强弱?
- 31.Python 怎么给密码设置“防沉迷”系统?
- 32.Python 怎么把密码保存到文件里?
- 33.Python 怎么读取文件里的密码?
- 34.Python 怎么把密码检查工具“打包”起来?
- 35.Python 怎么把文件操作也“打包”起来?
- 36.计算机怎么“扔骰子”?——用Python模拟随机事件
- 37.如何用 Python 模拟掷两个骰子?
- 38.怎么用 Python 把骰子轨迹画出来?
- 39.骰子点数分布长啥样?用直方图一眼看穿!
- 40.用科学计算库让骰子程序“起飞”
- 41.空气质量指数怎么算?手把手教你写一个AQI计算器
- 42.空气质量指数计算:从零开始,让程序看懂空气
- 43.如何读取空气质量数据文件并找出空气质量最好的五个监测点?
- 44.JSON 数据怎么变成 Excel 表格?—— 让数据换个“马甲”
- 45.Python 怎么让程序自己判断文件类型?
- 46.如何让程序自己上网查空气质量?
- 47.用美味的汤来解析网页:Beautiful Soup 入门
- 48.如何让爬虫自动抓取全国所有城市的空气质量?
- 49.爬到的数据怎么存下来?—— 把全国空气质量数据保存为CSV文件
- 50.用 pandas 玩转空气质量数据:找出全国空气最好和最差的城市
- 51.空气质量数据怎么“洗”干净,还能画成图?
用美味的汤来解析网页:Beautiful Soup 入门
约 17 分钟
小象实战讲义 · Python 入门:8 个小项目边玩边学
在上一节,我们学会了用网络爬虫从网上抓取网页内容。但分析网页内容时,我们用的是最原始的字符串查找方法,就像在一本厚厚的书里用肉眼一行行找某个词——效率不高,还容易出错。这一节,我们要请出一位“网页解析专家”:Beautiful Soup(美味的汤)。它能帮我们像用筷子夹菜一样,轻松地从网页这碗“汤”里捞出我们想要的“食材”!
💡 核心导读
- 网页为什么能解析? 网页其实是一棵结构化的“树”(DOM树),而不是一堆乱码。
- Beautiful Soup是什么? 一个专门用来解析HTML/XML网页的Python库,名字很形象。
- 本节新增什么? 只用Beautiful Soup的
find_all()方法,替换掉之前繁琐的字符串查找,轻松获取城市的所有污染物数据。 - 你会得到什么? 一个更优雅、更强大的空气质量数据抓取程序,能一次性获取某个城市的所有污染物数值。
网页是一棵树,不是一锅粥
你可能觉得网页就是一堆文字和标签混在一起的代码,但其实它有着非常清晰的结构。这种结构叫做 DOM树(Document Object Model,文档对象模型)。
想象一下家谱图:最上面是老祖宗(根节点),下面是儿子辈,再下面是孙子辈。网页也是这样组织的:
html(根节点)
├── head(头部)
│ └── title(网页标题)
└── body(主体)
├── h1(大标题)
├── div(区块1)
│ ├── p(段落)
│ └── a(链接)
└── div(区块2)我们之前访问的空气质量网站,它的数据也是按照这种树形结构排列的。每个城市的AQI和污染物数据,都放在特定的“树枝”(HTML标签)上。Beautiful Soup就是那个能看懂这棵树,并帮我们摘取特定果实的工具。
来一碗“美味的汤”:安装与导入
Beautiful Soup不是Python自带的,但安装非常简单。如果你用的是我们课程推荐的Anaconda,它很可能已经装好了。如果没有,打开命令行(终端),输入下面这行命令即可:
pip install beautifulsoup4安装好后,在Python程序中这样导入它:
from bs4 import BeautifulSoup
注意:库的名字是beautifulsoup4,但导入时我们用的是bs4。BeautifulSoup(注意大小写)是这个库里最主要的类。
喝汤三步走:创建对象、查找节点、提取内容
使用Beautiful Soup解析网页,通常只需要三步:
- 创建汤碗(Soup对象):把网页源代码“倒进”BeautifulSoup这个碗里。
- 用筷子找菜(查找节点):告诉它你想找什么样的“菜”(HTML标签)。
- 夹起来吃掉(提取内容):从找到的“菜”里,取出我们想要的文字或属性。
第一步:创建Soup对象
我们需要把之前用requests获取到的网页文本,交给BeautifulSoup来解析。
import requests
from bs4 import BeautifulSoup
# 1. 获取网页内容(以北京为例)
url = 'http://pm25.in/beijing'
r = requests.get(url, timeout=30)
html_text = r.text
# 2. 创建BeautifulSoup对象,指定解析器为lxml
soup = BeautifulSoup(html_text, 'lxml')
# 现在,soup就是一个包含了整个网页结构的对象
这里'lxml'是一种解析器,它解析速度快,是常用的选择。创建对象时,你还可以指定网页的编码(encoding),确保中文不会显示成乱码。
第二步:查找节点(这才是重点!)
Soup对象创建好后,我们就可以用它的方法来查找节点了。最常用的两个方法是:
find(): 找到第一个符合条件的节点。find_all(): 找到所有符合条件的节点。
怎么告诉它条件呢?看例子最直观。假设网页里有这样一个链接标签:
<a href="next.html" class="nav_link">下一页</a>我们可以用多种方式找到它:
from bs4 import BeautifulSoup
from bs4 import BeautifulSoup
import requests
url = 'http://pm25.in/beijing'
r = requests.get(url, timeout=30)
html_text = r.text
soup = BeautifulSoup(html_text, 'lxml')
# 假设soup已经创建好
# 1. 按标签类型查找:找到所有的<a>标签
all_links = soup.find_all('a')
# 2. 按属性查找:找到href属性为"next.html"的<a>标签
next_link = soup.find_all('a', href='next.html')
# 3. 按属性和文本内容查找:找到href为"next.html"且文字是"下一页"的<a>标签
next_link = soup.find_all('a', href='next.html', string='下一页')
# 4. 按class属性查找(特殊!):因为class是Python关键字,所以要加下划线
nav_links = soup.find_all('a', class_='nav_link')
# 5. 也可以用字典传递多个属性条件
nav_links = soup.find_all('a', {'class': 'nav_link', 'href': 'next.html'})
关键技巧:查找class属性时,必须写成class_(带下划线),或者用字典{'class': 'nav_link'}的形式。这是Python语法和HTML属性名冲突时的特殊处理。
实战:用Beautiful Soup改造我们的AQI爬虫
现在,让我们用这个新工具,彻底改造上一版的程序。本节我们只新增一件事:用Beautiful Soup的find_all方法,替换掉原来复杂的字符串切片和查找。
我们的目标是:输入城市拼音,程序返回该城市所有8项污染物的数据(比如AQI、PM2.5、PM10等),而不仅仅是AQI一个值。
先来看一下网页结构。打开http://pm25.in/beijing,用浏览器的“检查”功能查看,你会发现每一项污染物数据(如“AQI”、“PM2.5”)都包裹在这样一个结构里:
<div class="span1">
<div class="caption">AQI</div>
<div class="value">55</div>
</div>我们需要做的就是:找到所有class="span1"的div标签,然后从每一个里面分别提取caption和value。
下面是完整的v6.0程序:
"""
作者:梁斌
功能:AQI计算(使用BeautifulSoup解析)
版本:6.0
"""
import requests
from bs4 import BeautifulSoup
def get_city_aqi(city_pinyin):
"""
获取城市的所有污染物AQI数据
"""
# 构造目标城市的URL
url = 'http://pm25.in/' + city_pinyin
# 获取网页内容
r = requests.get(url, timeout=30)
# 创建BeautifulSoup对象来解析网页
soup = BeautifulSoup(r.text, 'lxml')
# 关键步骤:找到所有class为'span1'的div标签
# 这些标签包含了我们需要的污染物数据
div_list = soup.find_all('div', {'class': 'span1'})
# 准备一个列表来存放结果
city_aqi = []
# 通常前8个div就是我们需要的8项污染物数据
for i in range(8):
# 取出第i个div标签
div_content = div_list[i]
# 在这个div内部,找到class为'caption'的div,获取其文本(污染物名称)
caption = div_content.find('div', {'class': 'caption'}).text.strip()
# 找到class为'value'的div,获取其文本(污染物数值)
value = div_content.find('div', {'class': 'value'}).text.strip()
# 将(污染物名称, 数值)作为一个元组,添加到结果列表
city_aqi.append((caption, value))
return city_aqi
def main():
"""
主函数
"""
# 原程序这里用input()让用户输入城市拼音,例如"beijing"
city_pinyin = 'beijing' # 这里用固定值代替,你可以改成'shanghai', 'guangzhou'等试试
city_aqi = get_city_aqi(city_pinyin)
print(f"{city_pinyin}的空气质量数据:")
for item in city_aqi:
print(f" {item[0]}: {item[1]}")
# 程序入口
main()运行这个程序,你可能会看到类似这样的输出(数值取决于当时网站的数据):
beijing的空气质量数据:
AQI: 55
PM2.5: 32
PM10: 58
CO: 0.8
NO2: 38
O3: 65
SO2: 12
空气质量: 良代码解析:
div_list = soup.find_all('div', {'class': 'span1'}):这行代码是核心。它一次性找到了网页中所有符合条件的数据块,返回一个列表。div_content.find('div', {'class': 'caption'}):在每一个数据块内部,继续使用find方法定位更具体的元素。注意,find()和find_all()可以在任何找到的节点上继续调用,实现“层层深入”的查找。.text获取标签内的纯文本,.strip()去掉文本首尾多余的空白字符(如换行、空格)。- 我们把每项污染物的名称和值组成一个元组
(caption, value),最后返回一个包含8个元组的列表。
对比之前的版本:我们完全摆脱了手动计算字符串索引、用split()切割、担心格式变化的烦恼。Beautiful Soup直接理解了网页结构,让代码变得清晰、健壮,也更易维护。
小提示:有时运行程序会发现所有数据都是0。这可能是因为网站正在更新数据(例如每天凌晨定时清空)。过一段时间再运行,或者换个城市试试,数据就会正常显示。这正说明了我们程序是实时从网上获取数据的!
📝 动手练一练
改造练习:我们现在的程序只获取了8项污染物数据。请你修改
get_city_aqi函数,让它能获取div_list中所有的span1数据,而不是固定前8个。(提示:用len(div_list)代替8)探索练习:除了污染物数值,空气质量网站上还有城市名称、数据更新时间等信息。尝试修改程序,让它也能抓取并打印出城市的中文名称。(提示:用浏览器检查元素,找到城市名称所在的标签,然后用
soup.find()去定位它。)
👉 点击查看第1题参考答案
思路:原程序用for i in range(8)限定了只取前8项。我们可以改为遍历整个div_list列表的长度,这样无论网站显示多少项数据,我们都能全部获取。
关键代码修改:
def get_city_aqi(city_pinyin):
url = 'http://pm25.in/' + city_pinyin
r = requests.get(url, timeout=30)
soup = BeautifulSoup(r.text, 'lxml')
div_list = soup.find_all('div', {'class': 'span1'})
city_aqi = []
# 修改这里:遍历div_list中的所有元素
for div_content in div_list:
caption = div_content.find('div', {'class': 'caption'}).text.strip()
value = div_content.find('div', {'class': 'value'}).text.strip()
city_aqi.append((caption, value))
return city_aqi
👉 点击查看第2题参考答案
思路:使用浏览器的检查工具,查看城市名称(如“北京”)被什么HTML标签包裹。假设我们发现它在一个<h2>标签里,并且这个<h2>标签有一个特定的class,比如city_name。
关键代码添加: 在get_city_aqi函数中,创建soup对象后,可以添加如下代码来查找城市名:
# 查找城市名称(假设它在class为'city_name'的h2标签里)
city_name_tag = soup.find('h2', {'class': 'city_name'})
if city_name_tag:
city_name = city_name_tag.text.strip()
print(f"城市: {city_name}")
else:
print("未找到城市名称。")注意:实际的标签和class需要你通过检查网页源代码来确定,这里只是一个示例。探索和发现正是编程的乐趣之一!
本章小结
恭喜!你刚刚解锁了一个极其强大的网页处理工具。Beautiful Soup让解析HTML从一件繁琐的“体力活”,变成了清晰的“指路游戏”。你不再需要和杂乱无章的字符串搏斗,而是直接对结构化的网页树进行操作。
📋 行动清单
- 运行今天的v6.0程序,输入你所在城市的拼音,看看实时的空气质量数据。
- 尝试“动手练一练”,让你的程序变得更通用、更强大。
- 思考一下:如果让你不用Beautiful Soup,自己写程序解析今天这种网页,代码会有多复杂?对比之后,你就能深刻体会到“工具”的价值。
在下一节,我们将让程序变得更“智能”。现在还需要我们手动输入城市,能不能让程序自己发现网页上所有城市的链接,然后自动抓取全国所有城市的空气质量数据呢?当然可以!这就是我们下一个要攻克的挑战。
—— 小象教研组
配套资料 · 直接下载
课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。