今天我们来了解一下在数据分析领域最为常见一种文件格式:CSV 文件,然后我们再将上一篇文章案例中抓取到的数据保存到 CSV 文件中。
CSV(Comma-Separated Values) 是一种使用逗号分隔来实现存储表格数据的文本文件。
我们都知道表格有多种形式的存储,比如 Excel 的格式或者数据库的格式。CSV 文件也可以存储表格数据,并且能够被多种软件兼容,比如 Excel 就能直接打开 CSV 文件的表格,很多数据库软件也支持导入 CSV 文件。除了兼容性好之外,CSV 格式还是所有能存储表格的格式中最简单的一种。
比如我们新建一个文件,名称改为:info.csv,在文件中输入以下数据:
姓名,年龄,籍贯,部门
小明,22,河北,IT部
小亮,25,广东,IT部
小E,23,四川,财务部
保存成功后,我们使用excel打开这个csv文件,可以看到:
打开后的展示样式与excel基本一样,在这里,我们需要注意csv文件的一些事项:
在了解csv的格式后,下面我们再来看一下Python中关于csv的模块。
了解了 CSV 文件的基本概念,今天我们来看如何使用 Python 来操作 CSV 文件。因为对于数据分析场景而言,最常见的操作就是读取和写入。
现在我们来读取上面的info.csv文件内容。
现在VS CODE 中新建一个cell,导入csv模块
import csv
要读取 CSV 文件,我们需要用到 CSV 模块中的 DictReader 类,DictReader 可以将每一行以字典的形式读出来,key 就是表头,value 就是对应单元格的内容。代码如下:
# 通过 open 函数打开 info.csv ,并将文件对象保存在 fo 中
fo = open("info.csv ")
# 通过打开 CSV 文件的文件对象作为参数来创建 DictReader 类的对象,存在 reader 变量中
reader = CSV.DictReader(fo)
# 调用 reader 对象的 fieldnames 属性,获取 CSV 文件中表格的表头
headers = reader.fieldnames
# 关闭文件
fo.close()
# 打印表头的信息
print(headers)
输出如下:
['姓名', '年龄', '籍贯', '部门']
接下来,我们尝试获取表格的实际内容。
# 打开 info.csv
fo = open("info.csv ")
# 创建 DictReader 对象
reader = CSV.DictReader(fo)
# 创建列表,用于存储读到的行
row_list = []
# 使用遍历循环,直接对 reader 对象进行遍历
# 每次执行循环时,row 变量都存储了当前行的内容
for row in reader:
# 直接将 row 变量添加到行列表中
row_list.append(row)
# 关闭文件
fo.close()
# 打印第一行的表格数据
print(row_list[0])
打印的结果显示:
{'姓名': '小明', '年龄': '22', '籍贯': '河北', '部门': 'IT部'}
可以看到,我们拿到了第一行的内容,并且是以字典的形式,字典把每个单元格的内容和表头联系了起来,表头是 key,而具体内容就是 value。每行都是这样的一个字典,所有字典都存储在 row_list 列表中。
接下来,我们来演示对于 row_list 列表的常见操作:打印某一行、某一列的值。
print("打印年龄一列的内容:")
# 遍历循环 row_list,d 为循环变量
for d in row_list:
# 因为 d 是字典,直接打印 key 为 年龄的值即可。
print(d["年龄"])
# 打印一个换行
print("")
print("打印第三行的内容:")
d = row_list[2]
print("姓名:", d["姓名"])
print("年龄:",d["年龄"])
print("籍贯:",d["籍贯"])
print("部门:",d["部门"])
输出如下:
打印年龄一列的内容
22
25
23
打印第三行的内容
姓名: 小E
年龄: 23
籍贯: 四川
部门: 财务部
与读取类似,Python 的 CSV 模块提供了 DictWriter 方法,使得我们可以将表格数据以字典的形式存在到 CSV 文件中。
具体用法如下:
# 打开一个文件,假设是 info2.CSV,因为是写入,所以需要指定模式 "w"
# newline='',在写入 CSV 时,需要指定这个参数,这个记住即可。
fo = open("info2.CSV", "w", newline='')
# 将表头存储在一个列表中
header = ["姓名", "年龄", "籍贯", "部门"]
# 创建一个 DictWriter 对象,第二个参数就是上面创建的表头
writer = CSV.DictWriter(fo, header)
# 写入表头
writer.writeheader()
# 写入一行记录,以字典的形式,key 需要和表头对应。
writer.writerow({"姓名": "小刚", "年龄":"28", "籍贯":"福建", "部门":"行政部"})
# 关闭写入的文件
fo.close()
上述代码的关键点就在于,创建了 DictWriter 后,需要首先调用 writeheader 来写入表头,然后再调用 writerow 来写入行。
执行上述代码之后,并不会有内容输出,但是 源代码文件夹下会多出一个 Info2.csv, 用Excel 打开后,如下图所示。
DictWriter 除了提供 writerow 方法来将单个字典保存为 CSV 表格中的一行,还提供了 writerows 方法来一次性地保存多行的内容。
现在我们尝试使用 writerow 方法来一次性写入多条记录。将我们手工建的 CSV 表格的内容存储在 row_list 变量中的数据一起写入新文件中。代码如下:
# 新打开一个 info3.CSV 文件
fo = open("info3.CSV", "w", newline='')
# 将表头存储在一个列表中
header = ["姓名", "年龄", "籍贯", "部门"]
# 创建一个 DictWriter 对象,第二个参数就是上面创建的表头
writer = CSV.DictWriter(fo, header)
# 将小刚的记录插入到row_list 中
row_list.append({"姓名": "小刚", "年龄":"28", "籍贯":"福建", "部门":"行政部"})
# 写表头
writer.writeheader()
# 调用 writerows 方法,一次性写多个字典(一个字典列表)到 CSV 文件中
writer.writerows(row_list)
# 关闭文件
fo.close()
执行完毕后,源代码文件夹下生成了新的 info3.csv,打开后如下图所示,包含了一开始的三条记录,以及我们插入的“小刚”的记录。
接下来,我们来将上一讲中过滤出来的新闻列表写入 CSV 文件中。
我们将前面下载的jandan.html文件复制到本源码文件夹下,再将之前获取新闻标题的代码整理成几个函数,方便调用。
from bs4 import BeautifulSoup
# 输入参数为要分析的 html 文件名,返回值为对应的 BeautifulSoup 对象
def create_doc_from_filename(filename):
fo = open(filename, "r", encoding='utf-8')
html_content = fo.read()
fo.close()
doc = BeautifulSoup(html_content)
return doc
# 输入参数是 BeautifulSoup 对象,返回包含新闻的 div 元素列表
def find_index_labels(doc):
index_labels = doc.find_all("div", class_="indexs")
return index_labels
# 从第一次 find_all 获取的标签对象中抽取标题
def get_title(label_object):
# 从刚才的参数传入的标签对象中过滤出所有 target=_blank 的 a 标签
a_labels = label_object.find_all("a",target="_blank")
# 取第一个标签对象
my_label = a_labels[0]
# 将标签的文字内容作为返回值返回
return my_label.get_text()
# 和 get_title 函数一样,传入 label_object, 返回发布时间
def get_pub_time(label_object):
# 找到 class=comment-link 的 span 标签
spans = label_object.find_all("span", class_="comment-link")
# 取第一个
span = spans[0]
# 返回标题属性
return span["title"]
至此,我们四个基础函数已经准备好了,以上的 Cell 都需要注意执行,这样我们接下来才可以使用这些函数。
接下来,我们开始使用上面的函数来获得新闻的标题与新闻列表。
# 调用 create_doc_from_filename 函数,创建 BeautifulSoup 对象
doc = create_doc_from_filename("jiandan.html")
# 调用find_index_labels 函数,传入 BeautifulSoup 对象
# 将返回的 div 列表存储在 index_labels 中
index_labels = find_index_labels(doc)
# 使用遍历循环遍历 index_labels 列表,循环变量为 label_object
for label_object in index_labels:
# 调用 get_title, 传入当前处理的 div 元素对象,获取标题
title = get_title(label_object)
# 调用 get_pub_time,传入当前处理的 div 元素对象,获取发布时间
pub_time = get_pub_time(label_object)
# 将标题和发布时间打印出来
print("标题:", title)
print("发布时间:", pub_time)
上述代码把我们刚才准备的四个函数都串了起来。大概的思路就是首先创建 BeautifulSoup 对象,之后针对该对象查询 class = indexs 的列表,然后使用遍历循环遍历该列表,对于每一个 div 元素,分别调用 get_title 以及 get_pub_time 函数来获得标题与发布时间。
执行上述代码后,输出如下所示。可以看到,我们的新闻标题和时间都已经被成功打印了出来。
标题: 引发普通感冒的鼻病毒会将新冠病毒排挤出细胞!
发布时间: 1小时 ago
标题: 无厘头研究:植入虚假的记忆再抹去它们
发布时间: 4小时 ago
标题: 什么是仇恨犯罪?
发布时间: 8小时 ago
标题: 突发:LHCb发现了违背标准模型的现象
发布时间: 12小时 ago
标题: 今日带货 20210324
发布时间: 14小时 ago
标题: 舌战裸猿:IBM搞出了可以打辩论赛的AI
发布时间: 23小时 ago
标题: 大吐槽:「我没醉,醉的是世界」
发布时间: 1天 ago
标题: 今年世界总发电量的0.6%被用于挖比特币
发布时间: 1天 ago
标题: 接种疫苗后还是感染新冠?不要为此惊讶
发布时间: 1天 ago
标题: 今日带货:蛋友家的血橙
发布时间: 2天 ago
标题: 科学家首次在野外检测到抗多药的超级真菌
发布时间: 2天 ago
标题: 未在iPhone12盒中搭配充电器,苹果被巴西消协罚200万美元
发布时间: 2天 ago
标题: 工程师将解决城市陷坑的问题
发布时间: 2天 ago
标题: 今日带货:粉面专场
发布时间: 3天 ago
标题: 科学家在碟子里培育出了泪腺,并让它哭泣
发布时间: 3天 ago
标题: 疯狂实验进行时:把志愿者禁闭在黑暗的空间里40天
发布时间: 3天 ago
标题: 今日带货 20210321
发布时间: 4天 ago
标题: 我们已向外星人发送了哪些消息?
发布时间: 4天 ago
标题: 脑力小体操:石头+剪刀 VS 石头+布
发布时间: 4天 ago
标题: 发霉啦:今天,我终于向母亲摊牌了
发布时间: 5天 ago
标题: 普渡大学的经济学家计算出世界各地幸福的价格
发布时间: 5天 ago
标题: 人类首次观察到木星上极光黎明风暴的成形过程
发布时间: 5天 ago
标题: 为女儿出头,母亲编辑假裸照败坏高中啦啦队队员的名誉
发布时间: 5天 ago
标题: 今日带货:淘宝京东蛋友推荐
发布时间: 6天 ago
要存储到 CSV,首先我们需要将我们的数据创建为字典的形式,我们可以在(2)的循环中将标题和时间存储为字典,然后使用一个字典列表来存储每个新闻对应的字典。最后直接使用 DictWriter 的 writerows 方法来将字典列表写入 CSV 文件即可。
我们直接修改刚才打印标题和发布时间的 Cell,删除原本的打印代码,并添加字典相关操作的代码。
# 调用 create_doc_from_filename 函数,创建 BeautifulSoup 对象
doc = create_doc_from_filename("jiandan.html")
# 调用find_index_labels 函数,传入 BeautifulSoup 对象
# 将返回的 div 列表存储在 index_labels 中
index_labels = find_index_labels(doc)
# 【新增代码】存储新闻的字典列表
news_dict_list = []
# 使用遍历循环遍历 index_labels 列表,循环变量为 label_object
for label_object in index_labels:
# 调用 get_title, 传入当前处理的 div 元素对象,获取标题
title = get_title(label_object)
# 调用 get_pub_time,传入当前处理的 div 元素对象,获取发布时间
pub_time = get_pub_time(label_object)
# 【新增代码】创建单条新闻的字典
news = {"标题": title, "发布时间": pub_time}
# 【新增代码】将新闻字典添加到字典列表
news_dict_list.append(news)
# 【新增代码】打印出字典列表
print(news_dict_list)
通过循环,我们将新闻以字典的形式逐个添加到了字典列表中,然后在最后打印出列表,输出如下所示。
[{'标题': '引发普通感冒的鼻病毒会将新冠病毒排挤出细胞!', '发布时间': '1小时 ago'}, {'标题': '无厘头研究:植入虚假的记忆再抹去它们', '发布时间': '4小时 ago'}, {'标题': '什么是仇恨犯罪?', '发布时间': '8小时 ago'}, {'标题': '突发:LHCb发现了违背标准模型的现象', '发布时间': '12小时 ago'}, {'标题': '今日带货 20210324', '发布时间': '14小时 ago'}, {'标题': '舌战裸猿:IBM搞出了可以打辩论赛的AI', '发布时间': '23小时 ago'}, {'标题': '大吐槽:「我没醉,醉的是世界」', '发布时间': '1天 ago'}, {'标题': '今年世界总发电量的0.6%被用于挖比特币', '发布时间': '1天 ago'}, {'标题': '接种疫苗后还是感染新冠?不要为此惊讶', '发布时间': '1天 ago'}, {'标题': '今日带货:蛋友家的血橙', '发布时间': '2天 ago'}, {'标题': '科学家首次在野外检测到抗多药的超级真菌', '发布时间': '2天 ago'}, {'标题': '未在iPhone12盒中搭配充电器,苹果被巴西消协罚200万美元', '发布时间': '2天 ago'}, {'标题': '工程师将解决城市陷坑的问题', '发布时间': '2天 ago'}, {'标题': '今日带货:粉面专场', '发布时间': '3天 ago'}, {'标题': '科学家在碟子里培育出了泪腺,并让它哭泣', '发布时间': '3天 ago'}, {'标题': '疯狂实验进行时:把志愿者禁闭在黑暗的空间里40天', '发布时间': '3天 ago'}, {'标题': '今日带货 20210321', '发布时间': '4天 ago'}, {'标题': '我们已向外星人发送了哪些消息?', '发布时间': '4天 ago'}, {'标题': '脑力小体操:石头+剪刀 VS 石头+布', '发布时间': '4天 ago'}, {'标题': '发霉啦:今天,我终于向母亲摊牌了', '发布时间': '5天 ago'}, {'标题': '普渡大学的经济学家计算出世界各地幸福的价格', '发布时间': '5天 ago'}, {'标题': '人类首次观察到木星上极光黎明风暴的成形过程', '发布时间': '5天 ago'}, {'标题': '为女儿出头,母亲编辑假裸照败坏高中啦啦队队员的名誉', '发布时间': '5天 ago'}, {'标题': '今日带货:淘宝京东蛋友推荐', '发布时间': '6天 ago'}]
现在,我们已经将网页中抓取到的数据都保存在一个字典列表中:news_dict_list ,接下来就是将这个列表写入到 CSV 文件中即可。
代码如下:
# 创建 news.CSV 文件
fo = open("news.CSV", "w", newline='', encoding='utf_8_sig')
# 这一次的表头
header = ["标题", "发布时间"]
# 使用文件对象和表头初始化 DictWriter 对象
writer = CSV.DictWriter(fo, header)
# 写入表头
writer.writeheader()
# 将上一步计算的字典列表写入 CSV 文件中
writer.writerows(news_dict_list)
# 关闭文件对象
fo.close()
执行之后,在源代码文件夹下会生成 news.CSV 文件,用 Excel 打开后如下图所示。可以看到,我们的数据已经成功以表格的形式呈现了。