查看全课大纲(第 47 / 51 节)
  1. 1.为什么人人都该学点编程?
  2. 2.计算机与程序设计:你的电脑到底在“想”什么?
  3. 3.Python 这门语言,到底有什么魔力?
  4. 4.Python 开发环境配置:如何拥有你的第一个“编程工具箱”?
  5. 5.如何安装一个更强大的代码编辑器?
  6. 6.汇率兑换1.0:你的第一个“钱生钱”小程序
  7. 7.你的第一行代码,藏着哪些秘密?
  8. 8.汇率兑换2.0(上):让程序看懂“CNY”和“USD”
  9. 9.汇率兑换程序,如何让它“聪明”地判断币种?
  10. 10.汇率兑换程序能一直运行,直到我喊停吗?
  11. 11.汇率兑换4.0:如何把“算钱”功能打包成积木?
  12. 12.汇率兑换5.0:给你的程序穿上“结构化”的西装
  13. 13.怎么用 Python 画一个会动的五角星?
  14. 14.如何让程序自动画出不同大小的五角星?
  15. 15.函数自己调用自己,会发生什么?
  16. 16.用递归画出神奇的分形树
  17. 17.Python 怎么计算你一天躺着不动能消耗多少热量?
  18. 18.如何让程序“活”起来,持续为你服务?
  19. 19.如何让 BMR 计算器更“聪明”?
  20. 20.当程序遇到“不按套路出牌”的用户怎么办?
  21. 21.怎么用 Python 模拟一个“滚雪球”式的存钱计划?
  22. 22.如何用列表记录每一周的存款?
  23. 23.如何让程序自己数数?—— 用 for 循环解放你的双手
  24. 24.52周存钱挑战4.0:你的存款计算器能有多灵活?
  25. 25.52周存钱挑战:怎么根据日期自动查存款?
  26. 26.Python 怎么判断一年中的第几天?
  27. 27.列表 vs 元组:哪个更适合存储月份天数?
  28. 28.用集合给月份分个队,换个思路算天数
  29. 29.把月份和天数“锁”在一起:用字典给数据配个对
  30. 30.Python 怎么判断密码强弱?
  31. 31.Python 怎么给密码设置“防沉迷”系统?
  32. 32.Python 怎么把密码保存到文件里?
  33. 33.Python 怎么读取文件里的密码?
  34. 34.Python 怎么把密码检查工具“打包”起来?
  35. 35.Python 怎么把文件操作也“打包”起来?
  36. 36.计算机怎么“扔骰子”?——用Python模拟随机事件
  37. 37.如何用 Python 模拟掷两个骰子?
  38. 38.怎么用 Python 把骰子轨迹画出来?
  39. 39.骰子点数分布长啥样?用直方图一眼看穿!
  40. 40.用科学计算库让骰子程序“起飞”
  41. 41.空气质量指数怎么算?手把手教你写一个AQI计算器
  42. 42.空气质量指数计算:从零开始,让程序看懂空气
  43. 43.如何读取空气质量数据文件并找出空气质量最好的五个监测点?
  44. 44.JSON 数据怎么变成 Excel 表格?—— 让数据换个“马甲”
  45. 45.Python 怎么让程序自己判断文件类型?
  46. 46.如何让程序自己上网查空气质量?
  47. 47.用美味的汤来解析网页:Beautiful Soup 入门
  48. 48.如何让爬虫自动抓取全国所有城市的空气质量?
  49. 49.爬到的数据怎么存下来?—— 把全国空气质量数据保存为CSV文件
  50. 50.用 pandas 玩转空气质量数据:找出全国空气最好和最差的城市
  51. 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解析网页,通常只需要三步:

  1. 创建汤碗(Soup对象):把网页源代码“倒进”BeautifulSoup这个碗里。
  2. 用筷子找菜(查找节点):告诉它你想找什么样的“菜”(HTML标签)。
  3. 夹起来吃掉(提取内容):从找到的“菜”里,取出我们想要的文字或属性。

第一步:创建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
  空气质量: 良

代码解析:

  1. div_list = soup.find_all('div', {'class': 'span1'}):这行代码是核心。它一次性找到了网页中所有符合条件的数据块,返回一个列表。
  2. div_content.find('div', {'class': 'caption'}):在每一个数据块内部,继续使用find方法定位更具体的元素。注意,find()和find_all()可以在任何找到的节点上继续调用,实现“层层深入”的查找。
  3. .text获取标签内的纯文本,.strip()去掉文本首尾多余的空白字符(如换行、空格)。
  4. 我们把每项污染物的名称和值组成一个元组(caption, value),最后返回一个包含8个元组的列表。

对比之前的版本:我们完全摆脱了手动计算字符串索引、用split()切割、担心格式变化的烦恼。Beautiful Soup直接理解了网页结构,让代码变得清晰、健壮,也更易维护。

小提示:有时运行程序会发现所有数据都是0。这可能是因为网站正在更新数据(例如每天凌晨定时清空)。过一段时间再运行,或者换个城市试试,数据就会正常显示。这正说明了我们程序是实时从网上获取数据的!

📝 动手练一练

  1. 改造练习:我们现在的程序只获取了8项污染物数据。请你修改get_city_aqi函数,让它能获取div_list中所有的span1数据,而不是固定前8个。(提示:用len(div_list)代替8)

  2. 探索练习:除了污染物数值,空气质量网站上还有城市名称、数据更新时间等信息。尝试修改程序,让它也能抓取并打印出城市的中文名称。(提示:用浏览器检查元素,找到城市名称所在的标签,然后用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,自己写程序解析今天这种网页,代码会有多复杂?对比之后,你就能深刻体会到“工具”的价值。

在下一节,我们将让程序变得更“智能”。现在还需要我们手动输入城市,能不能让程序自己发现网页上所有城市的链接,然后自动抓取全国所有城市的空气质量数据呢?当然可以!这就是我们下一个要攻克的挑战。

—— 小象教研组

配套资料 · 直接下载

课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。

  • 第9章代码包:空气质量指数计算(含全部版本 10 个 .py)
    ZIP · 64KB
    下载
  • 数据集:aqi.csv(各城市 AQI 长表)
    772B
    下载
  • 数据集:beijing_aqi.json(北京监测点)
    3KB
    下载
  • 数据集:bottom10_aqi.csv(空气质量最差 10 城)
    412B
    下载
  • 数据集:china_city_aqi.csv(全国城市 AQI)
    13KB
    下载
  • 数据集:shanghai_aqi.json(上海监测点)
    3KB
    下载
  • 数据集:top10_aqi.csv(空气质量最好 10 城)
    372B
    下载
  • 数据集:top5_aqi.json(空气质量最好 5 城)
    1007B
    下载
看图文没弄懂?展开看本节视频讲解(约 17 分钟)
📺 正在播放小象学院官方高清录播(支持倍速与清晰度调节)