第4章 用 CSV 和 Excel 存储数据
4.1 用 CSV 文件存储数据
CSV(Comma-Separated Values)其实就是纯文本,用逗号分隔值,可以分隔成多个单元格。CSV 文件除了可以用普通的文本编辑工具打开,还能用 Excel 打开,但 CSV 和 Excel
有以下不同:
- 所有值都是字符串类型。
- 不支持设置字体颜色和样式。
- 不能指定单元格宽、高或合并单元格。
- 没有多个工作表。
- 不能嵌入图片、图表。
Python 中内置了一个 csv 模块用来处理 CSV 文件。
4.1.1 CSV 写入
csv 模块提供了两个写入的函数。
- writerow:写入一行。
- writerows:写入多行。
使用代码示例如下:
import csv
import os
# 定义要保存的文件名
save_file_name_1 = os.path.join(os.getcwd(), '1.csv')
save_file_name_2 = os.path.join(os.getcwd(), '2.csv')
# 定义要写入的数据
data_1 = [
['id', '姓名', '性别', '年龄', '工作'],
[1, '小明', '男', '18', '学生'],
[2, '小红', '女', '24', '老师'],
[3, '小光', '男', '25', 'Python工程师']
]
# 单行写入示例
with open(save_file_name_1, 'w', newline='') as f:
writer = csv.writer(f)
for row in data_1:
writer.writerow(row)
# 多行写入示例
with open(save_file_name_2, 'w', newline='') as f:
writer = csv.writer(f)
writer.writerows(data_1)
上面的 newline=''参数,如果不设置的话,每写入一行后将会写入一个空行。除了用writer 函数,还可以用 DictWriter 写入字典形式的数据,代码示例如下:
import csv
import os
# 定义要保存的文件名
save_file_name_3 = os.path.join(os.getcwd(), '3.csv')
# 定义列标题和数据
headers = ['id', '姓名', '性别', '年龄', '工作']
data_2 = [
{'id': 1, '姓名': '小明', '性别': '男', '年龄': '18', '工作': '学生'},
{'id': 2, '姓名': '小红', '性别': '女', '年龄': '24', '工作': '老师'},
{'id': 3, '姓名': '小光', '性别': '男', '年龄': '25', '工作': 'Python工程师'}
]
# 字典写入
with open(save_file_name_3, 'w', newline='') as f:
# 使用DictWriter创建writer对象,headers传入以定义列标题
writer = csv.DictWriter(f, fieldnames=headers)
# 写入表头
writer.writeheader()
# 循环遍历数据列表,每次写入一行
for row in data_2:
writer.writerow(row)
以上代码执行后生成的 CSV 内容如下所示:
id,姓名,性别,年龄,工作
1,小明,男,18,学生
2,小红,女,24,老师
3,小光,男,25,Python工程师
4.1.2 CSV 读取
通过调用 csv.reader()函数获得一个可迭代对象,此对象只能迭代一次,不能直接打印,可以用 list()将其转换为列表,示例如下:
with open(save_file_name_1) as f:
reader = csv.reader(f)
print(list(reader))
代码执行结果如下:
[['id', '姓名', '性别', '年龄', '工作'], ['1', '小明', '男', '18', '学生'], ['2', '小红', '女', '24', '老师'], ['3', '
小光', '男', '25', 'Python工程师']]
除此之外,还有以下几种读取元素的方法:
# 直接通过下标获取
print(list(reader)[0][1])
# reader.line_num用于获取行号
for row in reader:
print(reader.line_num, row)
# 除此之外,由于reader是可迭代对象,可以使用next方法一次获取一行
head_row = next(reader)
代码执行结果如下:
1 ['id', '姓名', '性别', '年龄', '工作']
2 ['1', '小明', '男', '18', '学生']
3 ['2', '小红', '女', '24', '老师']
4 ['3', '小光', '男', '25', 'Python工程师']
另外,还可以使用 DictReader,像操作字典那样获取数据,把表的首行(表头)作为key,访问每行中对应 key 的数据,代码示例如下:
with open(save_file_name_1) as f:
reader = csv.DictReader(f)
for row in reader:
print(row['姓名'])
代码执行结果如下:
小明
小红
小光
4.2 实战:爬取星座运势
我们通过一个例子来巩固 csv 库的使用,爬取的站点为 http://www.xzw.com/fortune/
上面就是我们想爬取的内容,采集的数据格式是:星座—生日时间—运势评分—今日运势,我们来分析网页的目录结构。
接下来编写代码来解析对应的节点。
import csv
import requests
from lxml import etree
# 准备CSV文件
with open('fortune_data.csv', mode='w', encoding='utf-8', newline='') as file:
writer = csv.writer(file)
# 写入表头
writer.writerow(['星座', '时间', '综合运势', '爱情运势', '事业学业', '财富运势',
'健康运势'])
base_url = 'https://www.xzw.com/fortune/'
resp = requests.get(base_url)
html = etree.HTML(resp.text)
titles = html.xpath('//*[@id="list"]/div[1]/div/dl/dd/strong/text()')
sjs = html.xpath('//*[@id="list"]/div[1]/div/dl/dd/small/text()')
xz_urls = html.xpath('//*[@id="list"]/div[1]/div/dl/dd/p/a/@href')
for title, sj, xz_url in zip(titles, sjs, xz_urls):
xz_url = xz_url.split('/')[2]
url = base_url + xz_url
response = requests.get(url)
content = etree.HTML(response.text)
p1 = content.xpath('//*[@id="view"]/div[2]/div[3]/div[2]/p[1]/strong/text()')
p1_txt = content.xpath('//*[@id="view"]/div[2]/div[3]/div[2]/p[1]/span/text()')
p2 = content.xpath('//*[@id="view"]/div[2]/div[3]/div[2]/p[2]/strong/text()')
p2_txt = content.xpath('//*[@id="view"]/div[2]/div[3]/div[2]/p[2]/span/text()')
p3 = content.xpath('//*[@id="view"]/div[2]/div[3]/div[2]/p[3]/strong/text()')
p3_txt = content.xpath('//*[@id="view"]/div[2]/div[3]/div[2]/p[3]/span/text()')
p4 = content.xpath('//*[@id="view"]/div[2]/div[3]/div[2]/p[4]/strong/text()')
p4_txt = content.xpath('//*[@id="view"]/div[2]/div[3]/div[2]/p[4]/span/text()')
p5 = content.xpath('//*[@id="view"]/div[2]/div[3]/div[2]/p[5]/strong/text()')
p5_txt = content.xpath('//*[@id="view"]/div[2]/div[3]/div[2]/p[5]/span/text()')
# 将数据整合为一行
row = [title, sj]
for txt in zip(p1_txt+p2_txt+p3_txt+p4_txt+p5_txt):
row.append(''.join(txt)) # 将文本列表转换为字符串
# 写入CSV
writer.writerow(row)
代码执行后生成的文件里的内容如下:
星座,时间,综合运势,爱情运势,事业学业,财富运势,健康运势
白羊座,3.21-4.19,整体运势并不是很好,会感到自己没有太多的主导权,容易被他人牵着鼻子走。你可能会遇到一些挫折或阻碍,感到事事不顺心。建议保持冷静,不要冲动行事,避免做出错误的决策。尽量与身边的人保持良好的沟通,以避免产生误解或冲突。在生活方面,适合参加一些放松身心的活动,如瑜伽或冥想,平复情绪,调整自己的状态。,单身的感情运势不太好,你可能会因为过于幼稚而让对方对你失去兴趣。已有伴者的也需要注意自己的言行举止,不要让伴侣觉得你不够成熟。,需要保持清醒的认知,不要被琐事和情绪困扰。目前你可能会感觉进展缓慢,但这是一个耕耘的阶段。保持全力以赴的姿态稳步前行,不要过分追求速度。,财务状况基本保持现状。建议你继续保持理性和节制,不要盲目冲动消费或进行大额投资。保持现有的财务计划和支出,稳健管理财务,避免冲动行为。,注意不要沉迷手机。过度使用手机会导致眼睛疲劳和颈椎问题。记得多休息眼睛,每小时眺望远处5分钟,同时做些颈部伸展运动。保持适当的手机使用时间,对眼睛和颈椎都有好处。
...
4.3 用 Excel 文件存储数据
Excel 相比 CSV 功能会多一些,比如支持设置字体颜色和样式,而 Python 操作 Excel可以用两个库:xlwt(写 Excel) 和 xlrd(读Excel),可以通过 pip 命令直接安装:
pip install xlwt
pip install xlrd
4.3.1 Excel 写入
xlwt 库中有关写入的函数如下所述。
- xlwt.Workbook():创建一个工作簿。
- 工作簿对象.add_sheet(cell_overwrite_ok=True):添加工作表,括号里是可选参数,用于确认同一个 cell(单元)是否可以重设值。
- 工作表对象.write(行号,列号,插入数据,风格):第四个参数可选。
- 工作簿对象.save(Excel 文件名):保存到 Excel 文件中。
写入代码示例如下:
import xlwt
import xlrd
import os
# 新建一个工作簿
workbook = xlwt.Workbook()
sheet = workbook.add_sheet('工作表1',cell_overwrite_ok=True)
sheet.write(0, 0, '姓名')
sheet.write(0, 1, '学号')
sheet.write(1, 0, '小猪')
sheet.write(1, 1, '1')
workbook.save(os.path.join(os.getcwd(), 'result.xlsx'))
代码执行后生成的列表结构如下:
姓名 学号
小猪 1
4.3.2 Excel 读取
xlrd 库中有关读取的函数如下所述。
- xlrd.open_workbook():读取一个 Excel 文件,获得一个工作簿对象。
- 工作簿对象.sheets()[0]:根据索引获得工作簿里的一个工作表。
- 工作表对象.nrows:获得行数。
- 工作表对象.ncols:获得列数。
- 工作表对象.row_values(pos):读取某一行的数据,返回的结果是列表类型。
读取代码示例如下:
import xlrd
workbook = xlrd.open_workbook(os.path.join(os.getcwd(), 'result.xlsx'))
sheet = workbook.sheets()[0]
# 获得行数
row_count = sheet.nrows
for row in range(0, row_count):
print(sheet.row_values(row))
代码执行结果如下:
['姓名', '学号']
['小猪', '1']
4.4 实战:爬取某音乐平台排行榜
我们通过一个例子来巩固 xlwt 库和 xlrd 库的使用,爬取的站点为 https://music.douban.com/top250
爬取第一页
import requests
from lxml import etree
import xlwt
import re
# 初始化请求信息
url = 'https://music.douban.com/top250'
headers = {
'Host': 'music.douban.com',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
}
# 发送请求获取HTML内容
response = requests.get(url, headers=headers)
html = etree.HTML(response.text)
# 使用XPath解析所需数据
song_names = html.xpath('//td[2]/div/a/text()')
song_imgs = html.xpath('//td[1]/a/img/@src')
singer_names = html.xpath('//td[2]/div/p/text()')
rating_nums = html.xpath('//td[2]/div/div/span[2]/text()')
rating_people = html.xpath('//td[2]/div/div/span[3]/text()')
music_hrefs = html.xpath('//td[2]/div/a/@href')
# 创建Excel工作簿和工作表
workbook = xlwt.Workbook()
sheet = workbook.add_sheet('豆瓣音乐Top250', cell_overwrite_ok=True)
# 写入表头
columns = ['歌曲名', '封面图片链接', '歌手', '发行时间', '类型', '评分', '评价人数', '详情链接']
for i, column in enumerate(columns):
sheet.write(0, i, column)
# 遍历数据并写入
row = 1
for song_name, song_img, singer_message, rating_num, rating_people, music_href in zip(song_names, song_imgs, singer_names, rating_nums, rating_people, music_hrefs):
song_name = song_name.strip()
singer_name = singer_message.split('/')[0].strip()
song_time = singer_message.split('/')[1].strip()
type = '/'.join(singer_message.split('/')[2:]).strip()
rating_num = rating_num.strip()
match = re.search(r'\d+', rating_people)
if match:
number = match.group()
data = [song_name, song_img, singer_name, song_time, type, rating_num, number+'人评价', music_href]
for i, value in enumerate(data):
sheet.write(row, i, value)
row += 1
# 保存到当前目录下的Excel文件
workbook.save('douban_music_top250.xls')
爬取全部
import requests
from lxml import etree
import xlwt
import re
# 创建Excel工作簿和工作表
workbook = xlwt.Workbook()
sheet = workbook.add_sheet('豆瓣音乐Top250', cell_overwrite_ok=True)
# 写入表头
columns = ['歌曲名', '封面图片链接', '歌手', '发行时间', '类型', '评分', '评价人数', '详情链接']
for i, column in enumerate(columns):
sheet.write(0, i, column)
row = 1 # 初始化行号
# 循环遍历每一页
for page in range(0, 250, 25):
url = f'https://music.douban.com/top250?start={page}'
headers = {
'Host': 'music.douban.com',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
}
response = requests.get(url, headers=headers)
html = etree.HTML(response.text)
song_names = html.xpath('//td[2]/div/a/text()')
song_imgs = html.xpath('//td[1]/a/img/@src')
singer_names = html.xpath('//td[2]/div/p/text()')
rating_nums = html.xpath('//td[2]/div/div/span[2]/text()')
rating_people = html.xpath('//td[2]/div/div/span[3]/text()')
music_hrefs = html.xpath('//td[2]/div/a/@href')
for song_name, song_img, singer_message, rating_num, rating_people, music_href in zip(song_names, song_imgs, singer_names, rating_nums, rating_people, music_hrefs):
song_name = song_name.strip()
singer_name = singer_message.split('/')[0].strip()
song_time = singer_message.split('/')[1].strip()
type = '/'.join(singer_message.split('/')[2:]).strip()
rating_num = rating_num.strip()
match = re.search(r'\d+', rating_people)
if match:
number = match.group()
data = [song_name, song_img, singer_name, song_time, type, rating_num, number + '人评价', music_href]
for i, value in enumerate(data):
sheet.write(row, i, value)
row += 1 # 更新行号以便写入下一行
# 保存到当前目录下的Excel文件
workbook.save('douban_music_top250.xls')
读取 Excel
import xlrd
def read_data():
xlsx = xlrd.open_workbook('douban_music_top250.xls')
table = xlsx.sheets()[0]
nrows = table.nrows # 行数
ncols = table.ncols # 列数
# 从第二行开始,因为第一行是表头
for i in range(1, nrows):
row_value = table.row_values(i)
print(row_value)
# 调用函数
read_data()
显示 Console 的部分结果如下:
['We Sing. We Dance. We Steal Things.', 'https://img3.doubanio.com/view/subject/s/public/s2967252.jpg', 'Jason Mraz', '2008-05-13', 'Import / Audio CD / 民谣', '9.1', '116284人评价', 'https://music.douban.com/subject/2995812/']
使用此类脚本下载网站内容时应遵守网站的使用条款,以及相关的法律法规。
本系列文章皆做为学习使用,勿商用。