数据存储(CSV/JSON/Excel)
掌握爬虫数据的存储技术
一、数据存储概述
在爬虫开发中,数据采集只是第一步,如何将抓取到的数据高效、规范地持久化存储同样至关重要。不同的存储格式适用于不同的场景,选择合适的存储方式可以大幅提升数据处理的效率和后续使用的便利性。
常见存储格式
- CSV(逗号分隔值): 最通用的表格数据格式,兼容性极强,几乎所有数据处理工具都支持
- JSON(JavaScript对象表示法): 灵活的树形/嵌套结构,天然适配爬虫返回的嵌套数据
- Excel(.xlsx): 功能丰富的电子表格格式,支持样式、公式、图表,适合非技术人员查看
- 数据库(MySQL/MongoDB): 适合大规模、高并发、需要复杂查询的场景
选择存储方式的考虑因素
- 数据结构: 扁平表格数据适合CSV/Excel,嵌套结构适合JSON,复杂关系适合数据库
- 数据量: 小数据量(<1万条)可随意选择,中等规模(1-100万条)需考虑性能,大规模(>100万条)必须用数据库
- 使用场景: 机器学习训练多用CSV,API数据交换多用JSON,业务报表多用Excel
- 兼容性要求: CSV跨平台跨语言支持最好,JSON在Web生态中天然适配,Excel在办公场景中最常用
文本格式 vs 二进制格式
文本格式(CSV、JSON)使用人类可读的字符编码存储数据,优点是可直接用文本编辑器查看和修改,跨平台兼容性好,版本控制友好(可diff对比)。缺点是解析开销略高,数值精度可能丢失。 二进制格式(Excel .xlsx 本质上是ZIP压缩包内的XML文件)存储效率高,支持丰富的数据类型和格式化功能,但不便于直接文本编辑和版本控制。
二、CSV文件存储
CSV(Comma-Separated Values)是爬虫数据存储中最常用的格式之一,Python内置的 csv 模块提供了完善的读写支持。
2.1 使用csv模块写入
csv.writer 基本写入
import csv
headers = ['书名', '作者', '价格', '评分']
books = [
['Python编程', 'Eric Matthes', 59.0, 9.5],
['流畅的Python', 'Luciano Ramalho', 89.0, 9.7],
['利用Python进行数据分析', 'Wes McKinney', 78.0, 9.4]
]
with open('books.csv', 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.writer(f)
writer.writerow(headers)
writer.writerows(books)
csv.DictWriter 字典写入
import csv
books_dict = [
{'书名': 'Python编程', '作者': 'Eric Matthes', '价格': 59.0, '评分': 9.5},
{'书名': '流畅的Python', '作者': 'Luciano Ramalho', '价格': 89.0, '评分': 9.7},
]
with open('books.csv', 'w', newline='', encoding='utf-8-sig') as f:
fieldnames = ['书名', '作者', '价格', '评分']
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(books_dict)
编码最佳实践
写入包含中文的CSV文件时,务必使用 encoding='utf-8-sig'(带BOM的UTF-8),这样Excel打开时能正确识别中文编码,避免乱码。同时设置 newline='' 可避免写入空行。
2.2 读取CSV文件
import csv
with open('books.csv', 'r', encoding='utf-8-sig') as f:
reader = csv.reader(f)
for row in reader:
print(row) # 每行是一个列表
# 使用DictReader按列名访问
with open('books.csv', 'r', encoding='utf-8-sig') as f:
reader = csv.DictReader(f)
for row in reader:
print(row['书名'], row['价格'])
2.3 追加模式写入
爬虫通常需要增量采集数据,使用追加模式('a')写入CSV文件,每次运行爬虫时添加新数据而不会覆盖已有内容。
import csv
new_book = ['深度学习入门', '斋藤康毅', 68.0, 9.3]
with open('books.csv', 'a', newline='', encoding='utf-8-sig') as f:
writer = csv.writer(f)
writer.writerow(new_book)
2.4 处理特殊字符与quoting参数
当字段内容包含逗号、换行符或引号时,需要使用 quoting 参数控制引用行为:
import csv
data = [['商品名', '描述', '价格'],
['Python书', '经典教材,值得拥有', 59.0],
['"入门"书', '包含\n多行\n描述', 39.0]]
with open('products.csv', 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.writer(f, quoting=csv.QUOTE_ALL)
writer.writerows(data)
# csv.QUOTE_ALL: 所有字段加引号
# csv.QUOTE_MINIMAL: 仅在必要时加引号(默认)
# csv.QUOTE_NONNUMERIC: 非数字字段加引号
# csv.QUOTE_NONE: 不加引号(需配合escapechar)
2.5 使用Pandas操作CSV
对于数据分析场景,Pandas提供了更简洁的CSV读写接口:
import pandas as pd
# 写入CSV
df = pd.DataFrame({
'书名': ['Python编程', '流畅的Python'],
'作者': ['Eric Matthes', 'Luciano Ramalho'],
'价格': [59.0, 89.0]
})
df.to_csv('books_pd.csv', index=False, encoding='utf-8-sig')
# 读取CSV
df_read = pd.read_csv('books_pd.csv', encoding='utf-8-sig')
print(df_read.head())
# 追加写入(无表头)
df_new = pd.DataFrame({'书名': ['新书'], '作者': ['某人'], '价格': [45.0]})
df_new.to_csv('books_pd.csv', mode='a', header=False, index=False, encoding='utf-8-sig')
CSV vs Pandas 写入方式对比
- csv模块: 轻量级,无额外依赖,适合简单的爬虫数据写入
- Pandas: 功能丰富,支持数据预处理和类型转换,适合需要清洗分析后再存储的场景
- 性能对比: 百万行级别csv模块更快且内存占用更低;Pandas提供更多数据处理能力但开销更大
三、JSON文件存储
JSON(JavaScript Object Notation)是目前Web API中最主流的数据交换格式,它支持嵌套结构,非常适合存储爬虫采集的复杂数据(如商品评论、用户信息、多层级分类等)。
3.1 json模块核心函数
import json
# 序列化:Python对象 -> JSON字符串
json_str = json.dumps(data)
# 序列化到文件
with open('data.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
# 反序列化:JSON字符串 -> Python对象
data = json.loads(json_str)
# 从文件反序列化
with open('data.json', 'r', encoding='utf-8') as f:
data = json.load(f)
重要:ensure_ascii=False
写入包含中文的JSON时,务必设置 ensure_ascii=False,否则中文会被转义为 \uXXXX 形式的Unicode转义序列,导致文件可读性极差。
3.2 写入JSON文件(带缩进格式化)
import json
# 爬虫采集的商品数据(嵌套结构)
product_data = {
"商品名称": "Python编程 第3版",
"作者": "Eric Matthes",
"价格": 59.00,
"分类": ["编程", "Python", "入门"],
"评价": {
"平均评分": 9.5,
"评价数": 12580,
"好评率": "98%"
},
"商家": {
"名称": "图灵教育",
"信誉": "天猫旗舰店"
}
}
# indent=2 输出格式化的JSON,便于阅读
with open('product.json', 'w', encoding='utf-8') as f:
json.dump(product_data, f, ensure_ascii=False, indent=2)
3.3 JSON Lines(JSONL)格式
JSON Lines(也称NDJSON)是每行一个独立JSON对象的格式,特别适合爬虫的逐行追加写入场景:
import json
# 爬虫逐条采集,逐条追加写入
products = [
{"id": 1, "name": "Python编程", "price": 59.0},
{"id": 2, "name": "流畅的Python", "price": 89.0},
{"id": 3, "name": "利用Python进行数据分析", "price": 78.0}
]
# 写入JSON Lines文件
with open('products.jsonl', 'w', encoding='utf-8') as f:
for product in products:
f.write(json.dumps(product, ensure_ascii=False) + '\n')
# 读取JSON Lines文件
with open('products.jsonl', 'r', encoding='utf-8') as f:
for line in f:
product = json.loads(line.strip())
print(product['name'])
JSONL优势
JSONL格式在爬虫开发中非常实用:①支持逐行追加,无需加载整个文件;②每行独立,某行损坏不影响其他行;③可压缩性高,适合日志型数据存储;④兼容Hadoop/Spark等大数据处理框架。
3.4 爬虫JSON数据存储实战
以下是一个完整的爬虫数据存储示例,展示从抓取到存储JSON的全流程:
import requests
import json
def crawl_and_save(url, output_file):
"""爬取API数据并保存为JSON"""
response = requests.get(url)
data = response.json() # API通常直接返回JSON
# 提取需要的字段
items = []
for item in data.get('results', []):
items.append({
'id': item['id'],
'title': item['title'],
'price': item.get('price', 0),
'created_at': item.get('created_at', ''),
'tags': item.get('tags', [])
})
# 存储为格式化JSON
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(items, f, ensure_ascii=False, indent=2)
print(f"已保存 {len(items)} 条数据到 {output_file}")
return items
3.5 使用Pandas操作JSON
import pandas as pd
# 读取JSON(支持嵌套JSON展开)
df = pd.read_json('products.json')
print(df.head())
# 读取JSON Lines
df = pd.read_json('products.jsonl', lines=True)
# DataFrame写入JSON
df.to_json('output.json', orient='records', force_ascii=False, indent=2)
# 不同的orient方向
# orient='records': [{"col1": val1}, ...]
# orient='columns': {"col1": {"idx": val}, ...}
# orient='index': {"idx": {"col1": val}, ...}
# orient='table': 包含schema信息的完整格式
四、Excel文件存储
Excel文件(.xlsx格式)在数据汇报、团队协作、非技术人员数据查看等场景中非常常用。Python操作Excel主要依赖 openpyxl 库,同时 Pandas 也提供了便捷的Excel接口。
4.1 openpyxl基础操作
import openpyxl
# 创建工作簿
wb = openpyxl.Workbook()
# 获取活动工作表
ws = wb.active
ws.title = "图书数据"
# 写入表头
headers = ['书名', '作者', '价格', '评分']
for col, header in enumerate(headers, 1):
ws.cell(row=1, column=col, value=header)
# 写入数据行
books = [
['Python编程', 'Eric Matthes', 59.0, 9.5],
['流畅的Python', 'Luciano Ramalho', 89.0, 9.7],
['利用Python进行数据分析', 'Wes McKinney', 78.0, 9.4]
]
for row_idx, book in enumerate(books, 2):
for col_idx, value in enumerate(book, 1):
ws.cell(row=row_idx, column=col_idx, value=value)
# 保存文件
wb.save('books.xlsx')
4.2 工作表管理
import openpyxl
wb = openpyxl.Workbook()
ws1 = wb.active
ws1.title = "爬虫数据"
# 创建新工作表
ws2 = wb.create_sheet(title="清洗后数据")
# 在指定位置创建
ws3 = wb.create_sheet(title="汇总", index=0)
# 访问工作表
sheet = wb['爬虫数据']
# 删除工作表
del wb['清洗后数据']
# 获取所有工作表名
print(wb.sheetnames)
4.3 单元格样式设置
openpyxl支持丰富的样式设置,让生成的Excel表格更加美观专业:
import openpyxl
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
wb = openpyxl.Workbook()
ws = wb.active
ws.title = "图书数据"
# 定义样式
header_font = Font(name='微软雅黑', bold=True, size=12, color='FFFFFF')
header_fill = PatternFill(start_color='2980B9', end_color='2980B9', fill_type='solid')
header_alignment = Alignment(horizontal='center', vertical='center')
thin_border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
# 写入表头并应用样式
headers = ['书名', '作者', '价格', '评分']
for col, header in enumerate(headers, 1):
cell = ws.cell(row=1, column=col, value=header)
cell.font = header_font
cell.fill = header_fill
cell.alignment = header_alignment
cell.border = thin_border
# 设置列宽
ws.column_dimensions['A'].width = 25
ws.column_dimensions['B'].width = 20
ws.column_dimensions['C'].width = 10
ws.column_dimensions['D'].width = 10
# 冻结首行(滚动时表头始终可见)
ws.freeze_panes = 'A2'
wb.save('books_styled.xlsx')
openpyxl常用功能汇总
- 字体设置: Font(name, size, bold, color, italic)
- 填充颜色: PatternFill(start_color, end_color, fill_type)
- 对齐方式: Alignment(horizontal, vertical, wrap_text)
- 边框设置: Border(left, right, top, bottom, style)
- 列宽行高: column_dimensions[列名].width, row_dimensions[行号].height
- 合并单元格: ws.merge_cells('A1:D1')
- 条件格式: ConditionalFormatting
4.4 使用Pandas操作Excel
import pandas as pd
# 写入Excel
df = pd.DataFrame({
'书名': ['Python编程', '流畅的Python', '利用Python进行数据分析'],
'作者': ['Eric Matthes', 'Luciano Ramalho', 'Wes McKinney'],
'价格': [59.0, 89.0, 78.0],
'评分': [9.5, 9.7, 9.4]
})
# 单表写入
df.to_excel('books_pd.xlsx', index=False, sheet_name='图书数据')
# 多表写入(多个DataFrame写入不同sheet)
with pd.ExcelWriter('books_multi.xlsx', engine='openpyxl') as writer:
df.to_excel(writer, sheet_name='图书数据', index=False)
summary = df.describe()
summary.to_excel(writer, sheet_name='统计摘要')
# 读取Excel
df_read = pd.read_excel('books_pd.xlsx', sheet_name='图书数据')
print(df_read.head())
Pandas vs openpyxl选型建议
Pandas: 适合快速将DataFrame导出为Excel,一行代码即可完成,推荐在数据分析流程中使用。 openpyxl: 需要对样式、格式、布局精细控制的场景使用,如生成专业报表、设置条件格式、插入图表等。两者可以结合使用:先用Pandas处理数据,再用openpyxl美化格式。
五、数据清洗与格式化
爬虫采集的原始数据往往包含各种质量问题,在存储前进行清洗和格式化是保证数据可用性的关键步骤。
5.1 去除空白字符
def clean_text(text):
"""清洗文本数据"""
if not isinstance(text, str):
return text
# 去除首尾空白
text = text.strip()
# 替换多个连续空格为单个
text = ' '.join(text.split())
# 去除特殊控制字符
text = text.replace(' ', '').replace('\xa0', ' ')
return text
5.2 统一日期格式
from datetime import datetime
def normalize_date(date_str):
"""将各种日期格式统一为标准格式"""
formats = [
'%Y年%m月%d日', '%Y-%m-%d', '%Y/%m/%d',
'%Y.%m.%d', '%m/%d/%Y', '%Y%m%d'
]
for fmt in formats:
try:
dt = datetime.strptime(date_str, fmt)
return dt.strftime('%Y-%m-%d')
except ValueError:
continue
return date_str # 无法解析则返回原值
5.3 字段类型转换与数据验证
def clean_price(price_str):
"""清洗价格字段"""
if price_str is None:
return 0.0
# 移除货币符号和空白
price_str = str(price_str).replace('¥', '').replace('$', '').strip()
try:
return float(price_str)
except ValueError:
return 0.0
def validate_book_data(book):
"""验证图书数据完整性"""
required_fields = ['书名', '作者', '价格']
for field in required_fields:
if field not in book or book[field] is None:
return False
return True
爬虫数据清洗标准流程
- 去重: 基于关键字段(如URL、ID)去除重复记录
- 空值处理: 填充默认值或删除无效记录
- 类型转换: 字符串转数字/日期等正确类型
- 格式统一: 文本编码、日期格式、数字精度统一
- 数据验证: 检查必填字段、值范围、格式正确性
- 标准化输出: 按目标格式(CSV/JSON/Excel)输出清洗结果
六、存储策略选择
不同存储格式各有优劣,选择合适的存储策略需要根据数据量、使用场景、团队技术栈综合考量。
格式对比表
| 特性 |
CSV |
JSON |
Excel |
数据库 |
| 数据结构 |
扁平表格 |
任意嵌套 |
扁平 + 多表 |
关系/文档 |
| 可读性 |
高 |
中高 |
高 |
低 |
| 写入性能 |
快 |
中 |
慢 |
快(批量) |
| 查询能力 |
弱 |
弱 |
中 |
强 |
| 跨语言支持 |
极好 |
极好 |
好 |
好 |
| 文件大小 |
小 |
中 |
大 |
依赖引擎 |
| 版本控制友好 |
是 |
是 |
否 |
否 |
| 样式/格式 |
无 |
无 |
丰富 |
无 |
| 推荐数据量 |
<1万条 |
<10万条 |
<5万条 |
任意 |
场景推荐
小数据量(<1万条)
推荐使用 CSV 格式。轻量简洁,用Excel或文本编辑器都能直接打开,Python/Java/R等任何语言都原生支持解析,是最通用无负担的存储方案。
需要共享查看
推荐使用 Excel 格式。非技术人员(如运营、产品经理)可以直接双击打开,支持筛选、排序、条件格式等交互操作。结合Pandas的 to_excel 和openpyxl的样式设置,可以生成美观专业的数据报表。
结构化/嵌套数据
推荐使用 JSON 格式。爬虫采集的常见数据(如商品详情含多图、用户信息含地址列表、文章含标签分类等)天然具有嵌套结构,JSON可以无损保留这种层次关系。
大数据量(>10万条)
推荐使用 数据库(MySQL/MongoDB)。文件格式在大数据量场景下面临内存溢出、检索缓慢等问题,数据库提供了索引、分页、事务、并发控制等企业级能力。
混合存储策略
实际项目中常采用混合方案:①爬虫原始数据存储为JSON Lines(便于回溯和增量追加);②清洗后转为CSV供机器学习模型使用;③汇总统计导出为Excel供团队查看和汇报;④核心业务数据存入数据库支持实时查询。各取所长,打通从数据采集到最终消费的全链路。
七、核心要点总结
- CSV场景: 扁平表格数据,需要跨平台通用性,追求轻量和性能,使用
csv 模块或 Pandas,注意 encoding='utf-8-sig'
- JSON场景: 嵌套结构数据,API响应存储,需要保留数据层次关系,使用
json 模块,注意 ensure_ascii=False 和 indent 格式化
- Excel场景: 需要样式和格式,供非技术人员查看,生成专业报表,使用
openpyxl 精细控制或 Pandas 快速导出
- 数据清洗: 存储前必须进行清洗,包括去重、空值处理、类型转换、格式统一、数据验证等步骤
- 增量写入: 爬虫采集建议采用追加模式(CSV的
'a' 模式、JSONL的逐行追加),避免每次重复写入全部数据
- 选型原则: 小数据量优先CSV,嵌套数据优先JSON,汇报展示优先Excel,大规模数据优先数据库
八、进一步思考与实践
综合练习:构建一个完整的数据采集存储流水线
- 用
requests 爬取某个API的数据
- 用
json 模块保存原始数据到JSONL文件(备份)
- 用
Pandas 读取JSONL并清洗数据
- 将清洗后的结果同时导出为CSV(供分析)和Excel(供汇报)
- 为Excel文件设置美观的样式(表头颜色、列宽、冻结窗格)
数据存储是爬虫工程中承上启下的关键环节。好的存储策略不仅能让数据"存得下",更能让数据在后续分析、展示、共享环节中"用得好"。建议在实际项目中逐步积累适合自身业务的最佳实践,将数据存储作为爬虫系统架构的重要组成部分来设计和优化。
注意事项
- 爬虫数据可能涉及版权和隐私问题,存储和使用时需遵守相关法律法规
- 大规模数据文件要关注内存管理,避免一次性加载全部数据
- 对敏感数据(如用户信息)进行脱敏处理后存储
- 定期备份重要数据文件,避免因磁盘损坏导致数据丢失
- 文件命名建议包含时间戳和采集批次信息,便于后续管理和追溯