什么是 CSV 文件

CSV(Comma-Separated Values) 是一种使用逗号分隔来实现存储表格数据的文本文件。

我们都知道表格有多种形式的存储,比如 Excel 的格式或者数据库的格式。CSV 文件也可以存储表格数据,并且能够被多种软件兼容,比如 Excel 就能直接打开 CSV 文件的表格,很多数据库软件也支持导入 CSV 文件。除了兼容性好之外,CSV 格式还是所有能存储表格的格式中最简单的一种。

下面,我们以一个例子来讲解 CSV 存储表格的原理。

假设有如下员工信息的表格。

 要存储类似上面的表格,以往我们只能将其输入到 Excel 中并保存为 xlsx 格式,现在我们来尝试将其以 CSV 的格式保存。

打开文本编辑器,如 Windows 下的记事本,新建空白文件。

输入以下内容,并保存为 info.csv (编码选择 ANSI)

姓名,年龄,籍贯,部门
小明,22,河北,IT部
小亮,25,广东,IT部
小E,23,四川,财务部

保存成功后,用 Excel 打开这个文件,可以看到 CSV 文件成功地在 Excel 中展示为表格。

  • 表格中的一行,对应 CSV 文件中的一行;

  • 一行中不同单元格的内容,在 CSV 文件中用逗号分隔;

  • 务必保证每行的逗号数量是一致的(对应表格中每行的单元格一致)。

在我们后续的数据分析任务中,CSV 文件将会是我们存储数据的主要格式。

Python 的 CSV 模块

熟悉了 CSV 文件的基本概念,今天我们来学习如何使用 Python 来操作 CSV 文件。因为对于数据分析场景而言,最常见的操作就是读取和写入。

从 CSV 文件读取内容

要读取 CSV 文件,就要用到 csv 模块中的 DictReader 类,DictReader 可以将每一行以字典的形式读出来,key 就是表头,value 就是对应单元格的内容。

例如:

1. 通过DictReader 对象的创建以及通过 fileldnames 属性获取了 CSV 表格的表头

import csv

# 通过 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)

输出:

['姓名', '年龄', '籍贯', '部门']

2. 获取表格实际内容

import csv

# 打开 info.csv ,并将文件对象保存在 f 中
with open("info.csv","r",encoding="utf-8") as f:
    # 通过打开 CSV 文件的文件对象作为参数来创建 DictReader 类的对象,存在 reader 变量中
    reader = csv.DictReader(f)
    # 调用 reader 对象的 fieldnames 属性,获取 CSV 文件中表格的表头
    headers = reader.fieldnames

    # 创建列表,用于存储读到的行
    row_list = []
    # 使用遍历循环,直接对 reader 对象进行遍历
    # 每次执行循环时,row 变量都存储了当前行的内容
    for row in reader:
        # 直接将 row 变量添加到行列表中
        row_list.append(row)

# 打印表头的信息
# print(headers)
# 打印第一行的表格数据
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
籍贯: 四川
部门: 财务部

写入 CSV 文件

在之前的例子中,我们写入 CSV 文件是手动写入的,现在我们来试着通过 Python 写入 CSV 文件。

与读取类似,Python 的 csv 模块提供了 DictWriter 方法,使得我们可以将表格数据以字典的形式存在到 CSV 文件中。

import csv

# 打开一个文件,假设是 info2.CSV,因为是写入,所以需要指定模式 "w"
# newline='',在写入 CSV 时,需要指定这个参数,这个记住即可。

with open("info2.CSV", "w", newline='') as f:
    # 将表头存储在一个列表中
    header = ["姓名", "年龄", "籍贯", "部门"]
    # 创建一个 DictWriter 对象,第二个参数就是上面创建的表头
    writer = csv.DictWriter(f, header)
    # 写入表头
    writer.writeheader()
    # 写入一行记录,以字典的形式,key 需要和表头对应。
    writer.writerow({"姓名": "小刚", "年龄":"28", "籍贯":"福建", "部门":"行政部"})

上述代码的关键点就在于,创建了 DictWriter 后,需要首先调用 writeheader 来写入表头,然后再调用 writerow 来写入行。

执行上述代码之后,并不会有内容输出,但是文件夹下会多出一个 Info2.csv, 用Excel 打开后,如下图所示。

可以看到,我们的表头和记录已经成功写入 CSV 文件中。

DictWriter 除了提供 writerow 方法来将单个字典保存为 CSV 表格中的一行,还提供了 writerows 方法来一次性地保存多行的内容。

import csv

# 打开一个文件,假设是 info2.CSV,因为是写入,所以需要指定模式 "w"
# newline='',在写入 CSV 时,需要指定这个参数,这个记住即可。

with open("info2.CSV", "w", newline='') as f:
    # 将表头存储在一个列表中
    header = ["姓名", "年龄", "籍贯", "部门"]
    # 创建一个 DictWriter 对象,第二个参数就是上面创建的表头
    writer = csv.DictWriter(f, header)
    # 写入表头
    writer.writeheader()
    # 写入一行记录,以字典的形式,key 需要和表头对应。
    writer.writerow({"姓名": "小刚", "年龄":"28", "籍贯":"福建", "部门":"行政部"})

    row_list = [{"姓名": "小hong", "年龄": "26", "籍贯": "山西", "部门": "开发"}]
    # 将小明的记录插入到row_list 中
    row_list.append({"姓名": "小明", "年龄": "26", "籍贯": "香港", "部门": "运维"})
    # 调用 writerows 方法,一次性写多个字典(一个字典列表)到 CSV 文件中
    writer.writerows(row_list)

实现煎蛋新闻列表保存到 CSV 文件中

接下来,我们来将上一篇中过滤出来的新闻列表写入 CSV 文件中。在上一篇中,我们在课程内容中获取了煎蛋的新闻标题。

我们今天的内容就是将每篇新闻的这两个内容保存到 CSV 中,相当于一个新闻就是一行,每一行有两列,一个是新闻标题,一列是发布时间。对应的表头就是:标题、发布时间。

将上一篇中抽取标题的代码整理成四个函数,方便后续调用:

(1)数据准备

第一步,将上一篇中的抽取标题的代码整理成几个函数,方便后续调用:

  1. 打开文件网页,读出内容,并创建对应的 BeautifulSoup 对象;
  2. 找到所有包含新闻的 div 元素列表(class=indexs 的 div);
  3. 从 2 中的 div 元素中抽取出标题;
  4. 从 2 中的 div 元素中抽取出时间。

我们把上述四个操作整理为四个函数。

1. 首先实现创建 BeautifulSoup 对象的函数。

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

2. 实现定位包含新闻的 div 元素的列表函数。

# 输入参数是 BeautifulSoup 对象,返回包含新闻的 div 元素列表
def find_index_labels(doc):
    index_labels = doc.find_all("div", class_="indexs")
    return index_labels

3. 实现新闻标题的抽取函数。

# 从第一次 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()

4. 实现获取新闻发布时间的函数。

# 和 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"]

(2)获取新闻标题与列表
接下来,我们开始使用上面的函数来获得新闻的标题与新闻列表。

# 调用 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

(3)将数据存储为字典的形式
要存储到 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'}]

(4)存储到 CSV 文件中

现在,我们已经将网页中抓取到的数据都保存在一个字典列表中: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()

至此,数据已经成功保存到了csv文件中。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐