查看全课大纲(第 49 / 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.空气质量数据怎么“洗”干净,还能画成图?
爬到的数据怎么存下来?—— 把全国空气质量数据保存为CSV文件
约 11 分钟
小象实战讲义 · Python 入门:8 个小项目边玩边学
上一节我们实现了一个完整的网络爬虫,能爬到全国所有城市的实时空气质量数据。不过当时我们只是简单地把数据打印出来验证一下,这就像你从超市买了一大堆东西,却只是看一眼就扔在门口——太浪费了!这一节,我们要把这些宝贵的数据好好地“收纳”起来,存到硬盘里,做成一个随时可以查看、分析的CSV文件。
💡 核心导读
- 本节新增什么:只加一件事——把爬到的数据写入CSV文件
- 复习老朋友:用
with open安全操作文件,用csv.writer写入数据 - 实用小技巧:显示处理进度,让你知道程序“活着”没卡住
- 数据拼接:把城市名和空气质量数据组合成一行记录
从“看一眼”到“存下来”
还记得我们上一节爬到的数据吗?每个城市都有AQI、PM2.5、PM10等8个指标,加上城市名一共9个数据。现在全国有375个城市,如果只是打印出来,屏幕会飞快滚动,你根本看不清,而且关掉程序数据就没了。
CSV文件就像电子表格,一行就是一条记录,一列就是一个指标。存成CSV后,你可以用Excel打开看,也可以用Python做数据分析,这才是数据的正确归宿!
# 这是我们上一节的核心代码,现在要给它加上"保存"功能
import requests
from bs4 import BeautifulSoup
import csv # 新增:引入CSV模块
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 = []
for i in range(8):
div_content = div_list[i]
value = div_content.find('div', {'class': 'value'}).text.strip()
city_aqi.append(value) # 只存数值,不要标签名
return city_aqi
def get_all_cities():
"""获取所有城市列表"""
url = 'http://pm25.in/'
city_list = []
r = requests.get(url, timeout=30)
soup = BeautifulSoup(r.text, 'lxml')
city_div = soup.find_all('div', {'class': 'bottom'})[1]
city_link_list = city_div.find_all('a')
for city_link in city_link_list:
city_name = city_link.text
city_pinyin = city_link['href'][1:]
city_list.append((city_name, city_pinyin))
return city_list注意看get_city_aqi函数里的一个小改动:原来我们存的是(caption, value)元组,现在只存value。因为CSV的列名我们会单独设置,不需要在每个数据里重复存储标签名。
设计CSV的“表头”
CSV文件的第一行通常是列名,就像Excel的表头。我们需要告诉程序每列数据代表什么:
# CSV文件的表头(列名)
header = ['City', 'AQI', 'PM2.5/1h', 'PM10/h', 'CO/1h', 'NO2/1h', 'O3/1h', 'O3/8h', 'SO2/1h']这9个列名对应:城市名、AQI指数、PM2.5(1小时)、PM10(1小时)、一氧化碳、二氧化氮、臭氧(1小时)、臭氧(8小时)、二氧化硫。
核心操作:写入CSV文件
现在到了最关键的部分——把数据写到文件里。我们用with open来安全地操作文件,确保无论发生什么,文件都会被正确关闭:
import csv
import requests
from bs4 import BeautifulSoup
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 = []
for i in range(8):
div_content = div_list[i]
value = div_content.find('div', {'class': 'value'}).text.strip()
city_aqi.append(value) # 只存数值,不要标签名
return city_aqi
def get_all_cities():
"""获取所有城市列表(上一节的函数)"""
url = 'http://pm25.in/'
city_list = []
r = requests.get(url, timeout=30)
soup = BeautifulSoup(r.text, 'lxml')
city_div = soup.find_all('div', {'class': 'bottom'})[1]
city_link_list = city_div.find_all('a')
for city_link in city_link_list:
city_name = city_link.text
city_pinyin = city_link['href'][1:]
city_list.append((city_name, city_pinyin))
return city_list
def main():
"""主函数:获取所有城市数据并保存为CSV"""
city_list = get_all_cities()
# 定义表头
header = ['City', 'AQI', 'PM2.5/1h', 'PM10/h', 'CO/1h', 'NO2/1h', 'O3/1h', 'O3/8h', 'SO2/1h']
# 打开文件准备写入
with open('china_city_aqi.csv', 'w', encoding='utf-8', newline='') as f:
writer = csv.writer(f) # 创建CSV写入器
writer.writerow(header) # 先写入表头
# 遍历所有城市
for i, city in enumerate(city_list):
city_name = city[0]
city_pinyin = city[1]
city_aqi = get_city_aqi(city_pinyin)
# 把城市名和空气质量数据拼成一行
row = [city_name] + city_aqi
writer.writerow(row)
# 每处理10个城市显示一次进度
if (i + 1) % 10 == 0:
print(f'已处理{i + 1}条记录。(共{len(city_list)}条记录)')
# 运行程序
main()运行这个程序,你会看到控制台每隔10个城市输出一次进度。全国375个城市,全部爬完大概需要5-6分钟,耐心等待一下。
代码里的几个“小心思”
1. 字符串和列表怎么“相加”?
你可能注意到了这行代码:row = [city_name] + city_aqi。为什么要把city_name放在列表里?
因为city_name是字符串(如”北京”),city_aqi是列表(如['120', '55', '78', ...])。Python不允许直接把字符串和列表相加,会报错。但我们可以把字符串变成单元素列表['北京'],然后两个列表就能用+连接了。
# 演示:字符串和列表的拼接技巧
city_name = '北京'
city_aqi = ['120', '55', '78', '45', '0.8', '40', '65', '60', '12']
# ❌ 错误写法:字符串 + 列表
# row = city_name + city_aqi # 会报错!
# ✅ 正确写法:把字符串变成列表再相加
row = [city_name] + city_aqi
print(row) # 输出:['北京', '120', '55', '78', '45', '0.8', '40', '65', '60', '12']2. 为什么要显示进度?
想象一下:程序运行5分钟,控制台一片空白,你会不会怀疑“是不是卡住了?是不是出错了?”显示进度就像给程序装了个“心跳显示器”,让你知道它还在努力工作。
enumerate(city_list)会同时给出索引i和城市信息city,i从0开始计数。我们每处理10条记录((i+1) % 10 == 0)就打印一次进度。
3. 文件编码为什么用utf-8?
因为城市名里有中文(北京、上海、广州…)。如果不指定encoding='utf-8',写入的中文可能会变成乱码。
看看成果:生成的CSV文件
程序运行完成后,会在当前目录生成一个china_city_aqi.csv文件。用Excel或文本编辑器打开,你会看到这样的结构:
City,AQI,PM2.5/1h,PM10/h,CO/1h,NO2/1h,O3/1h,O3/8h,SO2/1h
北京,120,55,78,0.8,40,65,60,12
上海,95,42,68,0.6,35,58,55,10
广州,80,38,62,0.5,30,52,50,8
...第一行是表头,后面每一行都是一个城市的完整空气质量数据。现在这些数据就永久保存在你的电脑里了,随时可以查看、分析!
完整代码一览
"""
作者:小象教研组
功能:获取全国城市空气质量数据并保存为CSV
版本:8.0
"""
import requests
from bs4 import BeautifulSoup
import csv
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 = []
for i in range(8):
div_content = div_list[i]
value = div_content.find('div', {'class': 'value'}).text.strip()
city_aqi.append(value)
return city_aqi
def get_all_cities():
"""获取所有城市列表"""
url = 'http://pm25.in/'
city_list = []
r = requests.get(url, timeout=30)
soup = BeautifulSoup(r.text, 'lxml')
city_div = soup.find_all('div', {'class': 'bottom'})[1]
city_link_list = city_div.find_all('a')
for city_link in city_link_list:
city_name = city_link.text
city_pinyin = city_link['href'][1:]
city_list.append((city_name, city_pinyin))
return city_list
def main():
"""主函数"""
city_list = get_all_cities()
header = ['City', 'AQI', 'PM2.5/1h', 'PM10/h', 'CO/1h', 'NO2/1h', 'O3/1h', 'O3/8h', 'SO2/1h']
with open('china_city_aqi.csv', 'w', encoding='utf-8', newline='') as f:
writer = csv.writer(f)
writer.writerow(header)
for i, city in enumerate(city_list):
if (i + 1) % 10 == 0:
print('已处理{}条记录。(共{}条记录)'.format(i + 1, len(city_list)))
city_name = city[0]
city_pinyin = city[1]
city_aqi = get_city_aqi(city_pinyin)
row = [city_name] + city_aqi
writer.writerow(row)
main()📝 动手练一练
- 进度显示优化:现在的进度显示是“已处理10条记录。(共375条记录)”,你能改成显示百分比吗?比如“已处理10条,完成2.7%”。
👉 点击查看参考答案
思路:用(i+1)/总条数*100计算百分比,用round()保留一位小数。
# 在进度显示的地方修改print 语句
for i, city in enumerate(city_list):
if (i + 1) % 10 == 0:
percent = round((i + 1) / len(city_list) * 100, 1)
print('已处理{}条,完成{}%。(共{}条)'.format(i + 1, percent, len(city_list)))(这段要嵌进”边遍历边写文件”的循环里,所以单独拿出来跑不了:i 来自 enumerate,city_list 是上一段程序爬好的城市列表。)
- 数据过滤:如果我只想保存AQI大于100(污染较重)的城市数据,该怎么修改代码?
👉 点击查看参考答案
思路:获取到城市的AQI数据后,先判断AQI值是否大于100,只有大于100的才写入文件。
# 在循环里拿到该城市的数据后,先判断 AQI,达标才写入(其余代码不变):
aqi_value = int(city_aqi[0]) # AQI 是返回列表的第 1 项,转成整数方便比较
if aqi_value > 100: # 只保存污染较重的城市
row = [city_name] + city_aqi
writer.writerow(row)(这段嵌在遍历城市的 for 循环里,city_aqi、writer 都来自上文完整程序。)
注意:这样修改后,生成的文件里就只有AQI>100的城市了。
- 文件命名:现在的文件名是固定的
china_city_aqi.csv,你能让文件名包含当前日期吗?比如china_city_aqi_2025_03_20.csv。
👉 点击查看参考答案
思路:用datetime模块获取当前日期,然后拼接到文件名中。
import datetime # 在文件开头添加这个import
# 固定成 2025-03-20,方便你每次运行看到的文件名都一样、便于对照;
# 真实程序里把这一行换成 datetime.datetime.now() 即可取“今天”
today = datetime.datetime(2025, 3, 20)
date_str = today.strftime('%Y_%m_%d') # 格式化成 '2025_03_20'
print('今天对应的日期字符串是:', date_str)
# 用带日期的名字创建文件,写入两行示例数据
filename = f'china_city_aqi_{date_str}.csv'
print('文件将保存为:', filename)
with open(filename, 'w', encoding='utf-8', newline='') as f:
f.write('City,AQI\n')
f.write('北京,120\n')
f.write('上海,95\n')
print('已写入文件:', filename)今天对应的日期字符串是: 2025_03_20
文件将保存为: china_city_aqi_2025_03_20.csv
已写入文件: china_city_aqi_2025_03_20.csv这样每天运行程序(把 now() 换回来即可),都会生成一个带日期的文件,方便区分不同日期的数据。
本章小结
恭喜!你现在已经完成了数据分析项目中最关键的第一步——数据获取与存储。我们从一个简单的空气质量计算器开始,一步步升级,现在居然能自动爬取全国所有城市的实时数据并保存下来,是不是很有成就感?
📋 行动清单
学完这一节,你可以:
- 运行完整程序,生成属于自己的全国空气质量CSV文件
- 用Excel打开生成的CSV文件,看看你所在城市的空气质量如何
- 尝试修改进度显示的频率(比如每5条显示一次)
- 思考一下:如果我想每小时自动爬一次数据保存起来,该怎么实现?(提示:需要用到定时任务)
下一节,我们将用Python直接分析这个CSV文件,找出空气质量最好和最差的城市,还会画出直观的图表。数据已经准备好了,分析工具即将登场!
—— 小象教研组
配套资料 · 直接下载
课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。