「py数据分析」04如何将 Python 爬取的数据保存为 CSV 文件

如何将 Python 爬取的数据保存为 CSV 文件

从原始网络数据到纯净 CSV - 搭建通往分析的桥梁

恭喜你!经过前面的努力,你的 Python 脚本终于成功地从一个网站上爬取了数据,一个充满信息的宝库正静静地躺在你的变量中。但接下来呢?你该如何保存这些珍贵的信息,使其变得有用、可共享,并为下一步的分析做好准备?

这时,CSV (Comma-Separated Values,逗号分隔值) 文件就如同英雄般登场了。在本教程中,我们将深入探讨 CSV 格式的重要性。它是一种简单、人类可读且通用兼容的格式,被誉为数据世界的"通用语" 。无论是 Microsoft Excel、Google Sheets、各种数据库,还是最重要的------其他数据分析工具,都能够轻松地打开和处理它 。对于数据科学家和分析师来说,将网络爬取的数据、API 响应或任何结构化信息保存为 CSV 格式,是数据处理流程中的一个基础且关键的步骤。

在本指南中,我们将探索两条将数据写入 CSV 的主要路径:

  1. 内置工具包 :Python 标准库中的 csv 模块------它轻量、无需额外安装,非常适合处理简单的任务 。

  2. 数据科学家的"瑞士军刀"pandas 库------这是任何严肃的数据处理与分析工作流中的行业标准 。

这篇详尽的教程将引导你完成从建立一个网络爬虫项目,到使用上述两种方法保存数据,再到处理高级选项以及解决最常见错误的整个过程。读完本文,你将不仅知道"如何做",更会理解"为什么这么做",从而自信地将任何结构化数据转化为整洁、可用的 CSV 文件。


奠定基础 - 一个实用的网络爬虫项目

在学习如何保存数据之前,我们首先需要获取一些数据。本节将通过一个具体的实战项目来奠定基础:从 books.toscrape.com 网站上爬取书籍信息。这是一个专门为教学目的而设计的"沙箱"环境,可以让我们安全合法地练习网络爬虫技术 。这使得接下来的教程内容都基于实际操作,而非空泛的理论。

我们的目标是为网站上的每本书提取四个关键信息:书名 (Title)价格 (Price)星级评分 (Star Rating)库存状态 (Availability)

审查源代码(获取"蓝图")

任何网络爬虫项目的第一步都是了解目标网页的结构。使用现代浏览器的开发者工具(通常通过右键点击页面并选择"检查"来打开)是这项工作的核心技能 。通过检查 HTML 源代码,我们可以找到包含目标数据的特定标签和类名。

books.toscrape.com 上,我们发现:

  • 每一本书的所有信息都被包裹在一个 <article class="product_pod"> 标签内。

  • 书名位于 <h3> 标签内部的一个 <a> 标签的 title 属性中 。

  • 价格在一个 <p class="price_color"> 标签内 。

  • 星级评分是 <p> 标签的一个类名,例如 <p class="star-rating Three">,这里的 "Three" 就是我们需要的评分信息 。

爬虫脚本(附带注释的代码)

下面的 Python 脚本使用了两个核心库:requests 库用于向网站发送 HTTP 请求并获取网页的 HTML 内容,而 BeautifulSoup 库则用于解析这些 HTML,让我们能够轻松地提取所需信息 。

Python

复制代码
import requests
from bs4 import BeautifulSoup

# 目标 URL
url = 'http://books.toscrape.com/'

# 发送请求并获取网页内容
response = requests.get(url)
# 使用 BeautifulSoup 解析 HTML
soup = BeautifulSoup(response.content, 'html.parser')

# 找到所有书籍的容器
books = soup.find_all('article', class_='product_pod')

# 准备一个列表来存储所有书籍的数据
all_books_data =

for book in books:
    # 提取书名
    title = book.h3.a['title']
    
    # 提取价格
    price = book.find('p', class_='price_color').text
    
    # 提取星级评分 (例如 'star-rating Three' -> 'Three')
    rating = book.p['class']
    
    # 将提取的数据存入一个字典
    book_data = {
        'title': title,
        'price': price,
        'rating': rating
    }
    
    # 将这本数的数据字典添加到总列表中
    all_books_data.append(book_data)

# 打印第一本书的数据以验证
print(all_books_data)

结构化数据(关键步骤)

请特别注意我们是如何组织爬取到的数据的。我们将数据整理成了一个字典的列表 (a list of dictionaries) 。列表中的每一个字典都代表一本书(也就是我们未来 CSV 文件中的一行),字典的键(如 'title', 'price')则对应着列名。

选择这种数据结构并非偶然,而是一个深思熟虑的设计决策,它极大地简化了后续的数据保存工作。这种结构是"自描述"的------键本身就说明了值的含义,这使得代码更具可读性。更重要的是,它与我们接下来要学习的两种最高效、最可靠的数据保存方法------csv.DictWriterpandas.DataFrame------完美契合 。

这个决策的逻辑流程如下:

  1. 爬虫为每本书分别提取了多个独立的数据点(书名、价格等)。

  2. 我们需要将属于同一本书的数据点组织在一起。字典是 Python 中实现这一目标的自然选择,它使用描述性的键(如 'title')来标记每个值。

  3. 我们爬取了多本书,因此需要一个容器来存放这些书的字典。列表是容纳这些字典集合的理想结构。

  4. 最终形成的 list[dict] 格式之所以理想,是因为 csv 模块中的 DictWriter 就是专门为处理这种格式而设计的 ,而

    pandas 库的 DataFrame 构造函数可以直接接收这种格式,并智能地从字典的键中推断出列名 。

通过在爬取阶段就采用这种结构,我们为后续的数据保存铺平了道路,避免了手动管理表头或进行复杂的数据重组,使整个流程更简单、更不易出错。


标准库方法 - 使用 Python 的 csv 模块

现在我们有了一份干净、结构化的数据,是时候将它保存到文件中了。我们将从 Python 的内置 csv 模块开始。它的最大优点是无需安装任何第三方库(它已经包含在 Python 中),非常适合编写轻量级的脚本 。

使用 csv.writer 写入(列表的列表方法)

csv.writer 对象是 csv 模块的基础写入工具。它被设计用来处理序列数据,最常见的形式是一个"列表的列表"(a list of lists),其中每个内部列表代表 CSV 文件中的一行 。

要使用 csv.writer,我们需要先将我们的"字典列表"转换成"列表的列表"。这个额外的步骤恰好凸显了稍后将介绍的 csv.DictWriter 的便利性。

深入剖析 open() 函数及其关键参数

在写入文件之前,我们必须先用 open() 函数打开一个文件。这里的参数设置至关重要,尤其是对于初学者而言,很多常见的错误都源于此。

  • 上下文管理器 with open(...) :强烈推荐始终使用 with open(...) as f: 语法。这种结构被称为上下文管理器,它能确保在代码块执行完毕后自动关闭文件,即使在发生错误时也是如此。这是一种最佳实践,可以有效防止资源泄露和数据损坏 。

  • 文件模式 mode

    • 'w' (write):写入模式。如果文件已存在,它会清空文件内容后重新写入。如果文件不存在,则会创建新文件。

    • 'a' (append):追加模式。它不会清空文件,而是在文件末尾添加新的数据行。这在需要分批次向同一个文件写入数据时非常有用 。

  • 揭开 newline='' 的神秘面纱:这是初学者最容易遇到的陷阱之一。

    • 问题根源 :不同的操作系统使用不同的字符组合来表示一行的结束。例如,Windows 使用回车符和换行符 (\r\n),而 Linux 和 macOS 只使用换行符 (\n) 。

    • 内在冲突 :当 Python 以文本模式 ('w') 写入文件时,它会默认进行一种"通用换行符"翻译,即将代码中的 \n 转换为当前操作系统的标准行尾符 。然而,

      csv 模块为了确保跨平台兼容性,它自己也会处理行尾符,默认写入 \r\n

    • 后果 :在 Windows 系统上,这两套换行机制会同时生效。Python 将 \n 转换成 \r\ncsv 模块再写入自己的 \r\n。这导致了多余的回车符,最终在你的 CSV 文件中每行数据之间都出现一个烦人的空行 。

    • 解决方案 :在 open() 函数中指定 newline=''。这个参数告诉 Python 的文件处理器:"请不要进行任何换行符翻译,将这个任务完全交给 csv 模块,它知道该怎么做。" 这是一个简单而关键的修复,可以确保生成格式正确的 CSV 文件 。

  • encoding='utf-8' 的必要性 :从网络上爬取的数据可能包含各种语言的字符和特殊符号(例如 é, )。utf-8 是一种能够表示世界上几乎所有字符的现代编码标准。在打开文件时明确指定 encoding='utf-8'可以有效防止 UnicodeEncodeError 错误,保证数据的完整性和正确性 。

整合实践:writerow()writerows()

现在,我们将以上知识点整合起来,使用 csv.writer 保存我们的书籍数据。

Python

复制代码
import csv

# 这是我们从第一部分爬取到的数据
all_books_data =

# 定义表头
headers = ['title', 'price', 'rating']

# 将字典列表转换为列表的列表
data_for_writer =
for book in all_books_data:
    data_for_writer.append([book['title'], book['price'], book['rating']])

# 打开文件进行写入,注意 newline='' 和 encoding='utf-8'
with open('books_writer.csv', 'w', newline='', encoding='utf-8') as f:
    # 创建一个 writer 对象
    writer = csv.writer(f)
    
    # 1. 手动写入表头
    writer.writerow(headers)
    
    # 2. 逐行写入数据 (方法一:使用 writerow)
    # for row in data_for_writer:
    #     writer.writerow(row)
        
    # 3. 一次性写入所有数据 (方法二:使用 writerows,更高效)
    writer.writerows(data_for_writer)

print("使用 csv.writer 保存数据成功!")

writerow() 方法接收一个列表,并将其作为一行写入文件。writerows() 则更进一步,它接收一个列表的列表,并一次性将所有行写入文件,通常效率更高 。

更 Pythonic 的方式:csv.DictWriter

当你处理的数据已经是字典列表时(就像我们爬取的结果),csv.DictWriter 是一个更优越、更健壮、可读性更强的选择 。

DictWriter 的主要优势在于:

  • 直接处理字典 :它直接使用我们的 all_books_data,无需进行任何数据格式转换。

  • 明确的列顺序 :通过 fieldnames 参数,你可以精确地控制 CSV 文件中列的顺序。这避免了因字典在旧版 Python 中无序而导致的列顺序混乱问题,并且允许你只选择部分列进行写入 。

  • 自动写入表头 :它拥有一个专门的 writeheader() 方法,可以根据 fieldnames 列表自动写入表头行。这比手动创建和写入表头列表要简洁和安全得多 。

下面是使用 DictWriter 的完整代码示例:

Python

复制代码
import csv

# 我们从第一部分爬取到的数据
all_books_data =

# 定义字段名(即列名),这决定了列的顺序
fieldnames = ['title', 'price', 'rating']

# 打开文件,同样注意 newline='' 和 encoding='utf-8'
with open('books_dictwriter.csv', 'w', newline='', encoding='utf-8') as f:
    # 创建一个 DictWriter 对象,传入文件对象和字段名
    writer = csv.DictWriter(f, fieldnames=fieldnames)
    
    # 写入表头
    writer.writeheader()
    
    # 写入所有数据行
    writer.writerows(all_books_data)

print("使用 csv.DictWriter 保存数据成功!")

通过对比,可以清晰地看到 DictWriter 的代码更简洁、意图更明确,是处理结构化字典数据时的首选。

csv.writercsv.DictWriter 对比

为了帮助你快速选择合适的工具,下表总结了它们的核心区别:

特性 csv.writer csv.DictWriter
输入数据格式 列表的列表/元组 (List of lists/tuples) 字典的列表 (List of dictionaries)
表头处理 必须手动使用 writerow()写入 使用 writeheader() 自动写入
列顺序 由内部列表的元素顺序隐式决定 fieldnames 参数显式控制
最佳适用场景 处理简单的、顺序固定的序列数据 处理自描述的、键值对形式的数据(如网络爬虫或 API 返回的结果),追求代码的健壮性和可读性

Export to Sheets


数据科学家的选择 - 使用 pandas

虽然 csv 模块功能强大且方便,但当你的工作流涉及到数据分析时,pandas 库无疑是更佳的选择。pandas 是 Python 数据分析生态的基石 。如果说

csv 模块是一个文件读写工具,那么 pandas 则是一个完整的车间,涵盖了从读取、清洗、转换、分析到最终保存的整个数据生命周期。将数据保存为 CSV 只是其强大功能的冰山一角。

从原始数据到强大的 DataFrame

pandas 的核心数据结构是 DataFrame,你可以将其想象成一个带有标签(行索引和列名)的二维表格,类似于电子表格或数据库中的表 。

将我们之前爬取的字典列表转换为 DataFrame 非常简单,甚至可以说是"一行魔法":

Python

复制代码
import pandas as pd

# 我们从第一部分爬取到的数据
all_books_data =

# 使用一行代码创建 DataFrame
df = pd.DataFrame(all_books_data)

# 打印 DataFrame 的前几行以查看结果
print(df.head())

pandas 非常智能,它会自动接收字典列表,将字典的键作为 DataFrame 的列名,将值作为对应的数据行 。如果某些字典缺少某个键,

pandas 会在相应的位置自动填充为 NaN(Not a Number),表示缺失值。这个功能对于处理不规整的爬虫数据来说极为方便。

使用 to_csv() 导出 - 一行代码的奇迹

一旦数据被加载到 DataFrame 中,将其保存为 CSV 文件同样只需要一行代码:

Python

复制代码
# 将 DataFrame 保存为 CSV 文件
df.to_csv('books_pandas.csv')

虽然这行代码已经能工作,但 to_csv() 方法提供了许多强大的参数,可以让我们对输出文件进行精细控制。

深入剖析 to_csv() 的核心参数
  • index=False :这是 to_csv() 中最常用也最重要的参数之一。默认情况下,pandas 会将 DataFrame 的行索引(即每行开头的 0, 1, 2...)作为一列写入 CSV 文件。在大多数情况下,这个索引是多余的,我们并不需要它。设置 index=False 可以阻止写入这个索引列,从而得到一个更干净的输出文件 。

  • header=True/False :这个参数控制是否将列名作为表头写入文件。默认值为 True,通常我们都会保留它 。

  • sep=','sep 代表分隔符 (separator)。默认是逗号 ','。如果你的数据本身包含逗号(例如,一段描述性文本),你可能希望使用其他分隔符,如制表符 \t 或竖线 |。例如,df.to_csv('output.tsv', sep='\t')

  • mode='w'/'a' :与 open() 函数中的模式参数完全相同,'w' 用于覆盖写入,'a' 用于在文件末尾追加 。

  • encoding='utf-8':同样,为了确保所有特殊字符都能被正确保存,强烈建议始终明确指定编码 。

专业技巧:为 Excel 用户解决乱码问题 - encoding='utf-8-sig'

这是一个非常常见且令人沮丧的场景:你用 pandas 精心创建了一个包含特殊字符(如中文、é)的 CSV 文件,通过邮件发送给同事。同事用 Microsoft Excel 打开后,看到的却是一堆乱码,比如 中æ--‡Ã©

这个问题根源在于一个叫做字节顺序标记 (Byte Order Mark, BOM) 的东西。BOM 是一个位于文件开头的不可见特殊字符 (\ufeff),它的作用就像一个"签名",用来告诉程序这个文件使用的是哪种 Unicode 编码 。

Excel 的"怪癖":许多程序,尤其是 Windows 平台上的 Microsoft Excel,在打开文件时行为有些"懒惰"。如果文件开头没有这个 BOM 签名,Excel 就不会主动将其识别为 UTF-8 编码,而是会退回到使用一种旧的、本地化的编码(例如中文环境下的 GBK 或欧美环境下的 cp1252)来解析文件,从而导致乱码 。

解决方案 :使用 encoding='utf-8-sig'。这个特殊的编码名称告诉 pandas,在以标准的 UTF-8 格式写入数据之前,先在文件开头加上那个 Excel 需要的 BOM 签名。这样,无论文件内容多么复杂,Excel 都能正确识别并显示它,从而为你和你的同事省去无数麻烦 。

掌握这一点不仅仅是了解一个技术细节,更是学习数据互操作性的重要一课。数据不仅仅是为了脚本本身,更是为了人和其他工具的使用。理解像 Excel 这样流行工具的特性,并知道如何去适应它们,是任何数据从业者都应具备的宝贵实用技能。

Python

复制代码
# 使用 index=False 和 encoding='utf-8-sig' 来创建最兼容的 CSV 文件
df.to_csv('books_pandas_final.csv', index=False, encoding='utf-8-sig')

print("使用 pandas 保存数据成功,并优化了 Excel 兼容性!")

pandas.DataFrame.to_csv() 关键参数速查表

下表为你整理了 to_csv() 最常用的几个参数,方便你快速查阅。

参数 描述 常用设置
path_or_buf 文件路径和名称,例如 'books.csv' 总是需要提供的第一个参数。
index 是否将 DataFrame 的行索引写入文件。 index=False (几乎总是使用)。
header 是否将列名作为表头写入文件。 header=True (默认值,通常保留)。
sep 用作字段分隔的字符。 sep=',' (默认),或 sep='\t' (制表符)。
mode 文件写入模式:'w' 覆盖,'a' 追加。 mode='a' (当向已存在的文件添加数据时)。
encoding 指定文件编码。 encoding='utf-8-sig' (为获得最佳 Excel 兼容性)。

Export to Sheets


解决常见"噩梦" - 错误排查指南

在编程中,遇到错误是常态。错误不是失败的标志,而是指引你走向正确方向的路牌。学会阅读和理解错误信息,是一项能让你事半功倍的"超能力"。本节将把初学者最常遇到的几个令人头疼的错误,转化为宝贵的学习机会。

揭秘 FileNotFoundError: [Errno 2] No such file or directory

  • 症状:程序崩溃,并提示"没有那个文件或目录"。

  • 核心问题:Python 在错误的地方寻找你指定的文件或目录 。

  • 排查清单

    1. 相对路径 vs. 绝对路径 :首先要理解两者的区别。绝对路径 是一个完整的、从根目录开始的路径(例如 C:\Users\YourUser\Documents\data.csv/home/user/data.csv),它清晰明确。而相对路径 (例如 data.csvoutput/data.csv)是相对于"当前工作目录"(Current Working Directory, CWD)的路径 。

    2. 什么是当前工作目录 (CWD)? CWD 是你的 Python 脚本"认为"自己正在运行的位置。这个位置会根据你启动脚本的方式而变化。例如,直接在终端中运行 python my_script.py 和在 VS Code 或 PyCharm 中点击"运行"按钮,CWD 可能会不同 。你可以通过以下代码来检查当前的 CWD:

      Python

      复制代码
      import os
      print(os.getcwd())
    3. 拼写错误和隐藏的扩展名 :文件名或路径中的一个微小拼写错误是常见原因。另外,Windows 系统默认会隐藏已知文件的扩展名,这会导致一个名为 data.csv 的文件实际上可能是 data.csv.txt,从而引发错误 。

    4. 路径分隔符 :Windows 使用反斜杠 \ 作为路径分隔符,而 Linux 和 macOS 使用正斜杠 /。在 Python 字符串中,反斜杠是转义字符(例如 \n 表示换行)。为了避免问题,最佳实践是:

      • 统一使用正斜杠 /,它在所有主流操作系统上都能正常工作 。

      • 或者使用 Python 的 pathlib 模块来构建路径,它会自动处理跨平台的兼容性问题。

解决 PermissionError: [Errno 13] Permission denied

  • 症状:脚本失败,并提示"权限被拒绝"。

  • 最常见的原因(对于初学者,99% 的情况是这个):你试图写入的 CSV 文件此刻正被另一个程序打开,最常见的就是 Microsoft Excel 。当 Excel 打开一个文件时,它会对其施加一个"写锁定",以防止其他程序修改它。Python 会尊重这个锁定,因此无法写入,从而引发权限错误。

  • 简单的解决方案:在其他程序(如 Excel)中关闭该文件,然后重新运行你的 Python 脚本。

  • 其他(较罕见的)原因 :你的脚本试图在一个受保护的系统目录(如 C:\Program Files/root)中创建或写入文件,而当前用户没有这些目录的写入权限 。请确保你的输出路径位于你有权限写入的文件夹中,例如"我的文档"或项目文件夹。

处理 UnicodeEncodeError

  • 症状 :脚本崩溃,并提示类似 'ascii' codec can't encode character '\u20ac' in position... 的错误。

  • 原因 :当你试图写入包含非 ASCII 字符(如欧元符号 、重音字母 é 或中文字符)的数据,但没有在 open() 函数或 to_csv() 方法中指定一个能够处理这些字符的编码时,就会发生此错误。在某些环境下,Python 的默认编码可能是 ascii,这是一个非常有限的编码集,无法表示这些特殊字符 。

  • 解决方案 :这个错误再次提醒了我们前面章节的教训。解决方案很简单:始终 在你的文件操作中明确指定一个功能强大的编码。使用 encoding='utf-8'encoding='utf-8-sig' 即可解决问题。

常见 CSV 错误及其解决方案

这张速查表可以作为你的书签,当你遇到这些常见错误时,可以快速找到原因和解决方案。

错误信息/症状 可能的原因 解决方案
FileNotFoundError 文件路径不正确,或脚本的当前工作目录 (CWD) 不是你预期的目录。 使用绝对路径,或用 os.getcwd() 检查 CWD。仔细检查文件名和路径的拼写。
PermissionError CSV 文件正被另一个程序(通常是 Excel)打开并锁定。 在其他程序中关闭该文件,然后重新运行脚本。
UnicodeEncodeError 试图写入特殊字符(如中文、€),但未指定正确的编码。 to_csv()open() 调用中添加 encoding='utf-8-sig' (推荐) 或 encoding='utf-8'
CSV 文件中出现空行 (使用 csv 模块时) 在 open() 函数中忘记了 newline='' 参数。 始终使用 with open(..., newline='') 来写入 CSV 文件。

Export to Sheets


选择你的路径,开启你的数据冒险

恭喜你!你已经掌握了一项数据处理领域的基础核心技能:将动态、原始的网络数据,转化为静态、规整、随时可用的 CSV 文件。

我们回顾一下所学的两种主要方法:

  • csv 模块 :适用于简单的、无外部依赖的脚本,当你只需要快速将数据"倾倒"到文件中时,它是一个不错的选择。特别是 csv.DictWriter,为处理字典数据提供了清晰的接口。
  • pandas :当你的工作流不仅仅是保存文件,还包括后续的数据清洗、分析或转换时,pandas 是必不可少的选择。它为整个数据科学生命周期提供了强大支持,是通往更广阔数据世界的门户。

对于初学者,一个明确的建议是:了解 csv 模块的存在是件好事,但你的学习精力应该主要投入到 pandas上。虽然 pandas 的初始学习曲线可能稍显陡峭,但它在功能和灵活性上带来的巨大回报是无与伦比的。

现在,你的数据已经整洁地保存在 CSV 文件中,真正的乐趣才刚刚开始!尝试用 pandas.read_csv() 将它重新加载到一个新的 DataFrame 中,然后开始探索、清洗和可视化这些数据吧。你已经为你的下一次数据冒险做好了充分的准备。

相关推荐
经典199232 分钟前
mysql 锁介绍
数据库·mysql
不太可爱的大白33 分钟前
Mysql分片:一致性哈希算法
数据库·mysql·算法·哈希算法
LuckyLay34 分钟前
1.1.2 运算符与表达式——AI教你学Django
python·django
~ 小团子35 分钟前
每日一SQL 【游戏玩法分析 IV】
数据库·sql·游戏
学不会就看35 分钟前
Django--01基本请求与响应流程
后端·python·django
零叹38 分钟前
MySQL——常用程序and主从复制
数据库·mysql
胚芽鞘6815 小时前
关于java项目中maven的理解
java·数据库·maven
nbsaas-boot6 小时前
Java 正则表达式白皮书:语法详解、工程实践与常用表达式库
开发语言·python·mysql
仗剑_走天涯6 小时前
基于pytorch.nn模块实现线性模型
人工智能·pytorch·python·深度学习
chao_7896 小时前
二分查找篇——搜索旋转排序数组【LeetCode】两次二分查找
开发语言·数据结构·python·算法·leetcode