CSV、Excel 与数据库数据读取实践

摘要

数据分析的第一步通常是读取数据。CSV、Excel 和数据库是最常见的数据来源,但真正落地时会遇到编码、分隔符、表头、日期、数据类型、缺失值、大文件、SQL 查询和连接管理等问题。这些问题看似琐碎,却往往决定了后续清洗和建模的成败------一个被误读为整数的订单号、一个因编码错误而乱码的中文列名,都可能在分析链路中埋下隐患。

本文围绕 pandas 的 read_csv、read_excel 和 read_sql,介绍常见数据读取场景、参数配置、类型控制、分块读取和读取后的校验流程。我们会从核心概念出发,逐步拆解每个参数的作用与适用场景,再通过 11 个实战示例演示如何应对真实数据中的典型问题,最后给出常见问题的排查思路和进阶实践建议,帮助建立稳定可复用的数据入口。

无论你是刚接触 pandas 的初学者,还是在维护数据管道的工程师,都可以从本文中找到可直接落地的读取方案,让数据入口更可靠、更可控。

一、背景与问题

读取数据看起来简单:

python 复制代码
import pandas as pd

df = pd.read_csv("data.csv")

但真实数据往往并不理想:

  • CSV 编码不统一,中文乱码。
  • 分隔符不是逗号,而是制表符或竖线。
  • 前几行是说明文字,不是表头。
  • 日期字段格式混乱。
  • 金额字段包含逗号、空字符串或单位。
  • Excel 有多个工作表,表头位置不固定。
  • 数据库字段类型和 pandas 推断类型不一致。
  • 文件过大,一次性读取导致内存不足。

稳定的数据读取流程应该明确数据来源、字段类型、缺失值规则和读取后的校验步骤。

二、核心概念

1. 数据源类型

常见来源如下:

来源 特点
CSV 简单通用,适合导出和交换
Excel 业务人员常用,格式灵活但不够规范
数据库 适合结构化查询和增量读取
JSON 适合嵌套结构和接口数据
Parquet 列式存储,适合大数据分析

本文重点覆盖 CSV、Excel 和数据库。

2. 编码

编码决定字节如何解释为字符。常见编码包括:

  • utf-8
  • utf-8-sig
  • gbk
  • gb18030

中文 Windows 环境导出的 CSV 可能使用 gbk,Excel 更容易识别带 BOM 的 utf-8-sig。

3. 分隔符

CSV 并不一定用逗号分隔。常见分隔符包括:

分隔符 参数
逗号 sep=","
制表符 sep="\t"
竖线 `sep="
分号 sep=";"

分隔符错误时,pandas 可能把整行读成一列。

4. 类型推断

pandas 会根据数据内容推断类型,但推断不一定符合业务:

  • 订单号可能被推断为整数。
  • 日期可能被推断为字符串。
  • 混合数字和空字符串可能变成浮点。
  • 大数字 ID 可能出现精度风险。

重要字段应通过 dtype 和后续转换显式控制。

5. 分块读取

大文件无法一次性读入内存时,可以使用 chunksize 分块读取。分块处理适合统计、过滤、清洗和导入数据库。

三、工作原理

1. 通用读取流程

text 复制代码
确认数据源
  → 明确编码、分隔符和表头
  → 指定关键字段类型
  → 读取样本
  → 校验列名、行数和缺失值
  → 正式读取
  → 保存读取日志

不要直接对未知文件执行完整读取。先读取少量行,确认结构正确。

2. 读取后的校验

每次读取后至少检查:

  • 行数是否符合预期。
  • 列名是否完整。
  • 主键是否缺失或重复。
  • 关键字段类型是否正确。
  • 日期和数值转换是否产生新增缺失值。

读取成功不等于数据可用。

3. 数据库读取边界

从数据库读取数据时,应避免:

  • SELECT * 读取不需要的字段。
  • 一次性读取全表。
  • 在 Notebook 中写死生产数据库密码。
  • 缺少时间范围或分页条件。
  • 对线上库发起高成本查询。

数据分析查询也需要遵守数据库性能和权限边界。

四、实战示例

1. 读取标准 CSV

python 复制代码
import pandas as pd

df = pd.read_csv(
    "data/orders.csv",
    encoding="utf-8-sig",
)

print(df.head())
print(df.info())

如果出现乱码,可以尝试 gbk 或 gb18030,但最终应推动数据导出端统一编码。

2. 指定字段类型

python 复制代码
df = pd.read_csv(
    "data/orders.csv",
    dtype={
        "order_id": "string",
        "customer_id": "string",
        "region": "string",
    },
    encoding="utf-8-sig",
)

ID 类字段优先按字符串读取,避免前导零和超长数字问题。

3. 解析日期

python 复制代码
df = pd.read_csv(
    "data/orders.csv",
    encoding="utf-8-sig",
)

df["order_date"] = pd.to_datetime(
    df["order_date"],
    errors="coerce",
)

bad_dates = df.loc[df["order_date"].isna(), ["order_id", "order_date"]]
print(bad_dates)

先读取再转换,通常比一次性依赖自动解析更容易排查异常。

4. 处理非标准分隔符

python 复制代码
df = pd.read_csv(
    "data/orders.tsv",
    sep="\t",
    encoding="utf-8",
)

读取后先检查列数。如果只读出一列,优先检查分隔符是否正确。

5. 跳过说明行和指定表头

python 复制代码
df = pd.read_csv(
    "data/report.csv",
    skiprows=2,
    header=0,
    encoding="utf-8-sig",
)

业务系统导出的文件常常包含标题、导出时间和说明行,需要跳过后再读取表格主体。

6. 读取部分列

python 复制代码
df = pd.read_csv(
    "data/orders.csv",
    usecols=["order_id", "order_date", "region", "amount"],
    encoding="utf-8-sig",
)

只读取需要的列,可以降低内存占用和处理成本。

7. 分块读取大文件

python 复制代码
import pandas as pd

total_amount = 0.0
total_rows = 0

for chunk in pd.read_csv(
    "data/large_orders.csv",
    chunksize=100_000,
    encoding="utf-8-sig",
):
    chunk["amount"] = pd.to_numeric(chunk["amount"], errors="coerce")
    total_amount += chunk["amount"].sum(skipna=True)
    total_rows += len(chunk)

print("rows:", total_rows)
print("total amount:", total_amount)

分块处理适合累计统计和过滤导出。如果后续逻辑需要全量排序或复杂关联,则需要更合适的存储和计算方案。

8. 读取 Excel

python 复制代码
df = pd.read_excel(
    "data/orders.xlsx",
    sheet_name="订单明细",
    dtype={
        "订单号": "string",
        "客户ID": "string",
    },
    engine="openpyxl",
)

print(df.head())

Excel 适合业务人员维护小规模数据,但不适合作为大规模自动化数据管道的主要格式。

9. 读取多个工作表

python 复制代码
sheets = pd.read_excel(
    "data/monthly_report.xlsx",
    sheet_name=None,
    engine="openpyxl",
)

for sheet_name, frame in sheets.items():
    print(sheet_name, frame.shape)

sheet_name=None 会读取所有工作表并返回字典。需要谨慎处理大型 Excel 文件。

10. 从数据库读取

python 复制代码
import pandas as pd
from sqlalchemy import create_engine, text

engine = create_engine(
    "postgresql+psycopg://user:password@localhost:5432/app"
)

query = text(
    """
    select order_id, order_date, region, amount
    from orders
    where order_date >= :start_date
      and order_date < :end_date
    """
)

df = pd.read_sql(
    query,
    con=engine,
    params={
        "start_date": "2026-09-01",
        "end_date": "2026-10-01",
    },
)

真实项目中,数据库连接信息应来自环境变量或配置系统,不要写死在代码里。

11. 读取后统一校验

python 复制代码
def validate_orders(frame: pd.DataFrame) -> None:
    required_columns = {"order_id", "order_date", "region", "amount"}
    missing_columns = required_columns - set(frame.columns)
    if missing_columns:
        raise ValueError(f"missing columns: {missing_columns}")

    if frame["order_id"].isna().any():
        raise ValueError("order_id contains missing values")

    if frame["order_id"].duplicated().any():
        raise ValueError("order_id contains duplicated values")

    if len(frame) == 0:
        raise ValueError("orders is empty")

把校验放在读取之后、清洗之前,可以更早发现数据源异常。

五、常见问题与实践建议

1. 中文乱码怎么办?

先确认源文件编码。可以尝试 utf-8-sig、gbk、gb18030。长期方案是统一导出编码,并把编码约定写入数据接口文档。

2. 为什么订单号前面的 0 没了?

因为字段被当成数字读取了。读取时指定:

python 复制代码
pd.read_csv("orders.csv", dtype={"order_id": "string"})

ID、手机号、证件号和编码字段通常都应该按字符串处理。

3. Excel 公式读取到的是公式还是结果?

取决于文件保存状态和读取引擎。pandas 读取 Excel 时通常读取单元格缓存结果,但如果文件没有被 Excel 重新计算保存,结果可能不是最新。自动化流程中不建议依赖复杂 Excel 公式。

4. 大文件应该怎么读?

优先考虑:

  • 只读取需要的列。
  • 明确 dtype 降低内存。
  • 使用 chunksize 分块。
  • 转换为 Parquet。
  • 将数据放入数据库或数据仓库。

如果每次都要全量扫描超大 CSV,应重新考虑数据存储方式。

5. 数据库读取是否可以直接 SELECT *?

不建议。只选择需要的字段,增加时间范围和过滤条件,并避免在业务高峰期执行重查询。分析任务也可能影响线上数据库。

六、进阶思考

1. 数据读取层抽象

可以把读取逻辑封装为函数:

python 复制代码
from pathlib import Path

import pandas as pd


def read_orders_csv(path: str | Path) -> pd.DataFrame:
    frame = pd.read_csv(
        path,
        dtype={"order_id": "string", "customer_id": "string"},
        encoding="utf-8-sig",
    )
    frame["order_date"] = pd.to_datetime(
        frame["order_date"],
        errors="coerce",
    )
    frame["amount"] = pd.to_numeric(
        frame["amount"],
        errors="coerce",
    )
    validate_orders(frame)
    return frame

这样 Notebook、脚本和定时任务都可以复用同一套读取规则。

2. 读取日志

每次读取可以记录:

  • 文件路径或数据源。
  • 文件大小。
  • 读取时间。
  • 行数和列数。
  • 关键字段缺失数量。
  • 数据时间范围。
  • 读取程序版本。

当数据结果异常时,这些信息有助于定位是源数据变化还是处理逻辑变化。

3. 从 CSV 迁移到 Parquet

Parquet 支持列式存储、类型信息和压缩,适合较大规模的分析任务。稳定数据管道中,可以把外部 CSV 读取后转换为 Parquet,后续分析直接读取 Parquet。

4. 安全与权限

数据库连接字符串、账号密码和访问 Token 不应写进 Notebook。建议使用环境变量、密钥管理工具或只读分析账号,并限制可访问的数据范围。

结论

数据读取是数据分析流程的入口,入口不稳定,后续清洗和统计都会受影响。CSV 要关注编码、分隔符和类型;Excel 要关注工作表、表头和公式;数据库要关注查询范围、连接管理和权限。

下一篇将进入数据清洗,重点处理缺失值、重复值和异常值,并把读取后的原始数据变成更可靠的分析数据集。

参考资料

  1. pandas read_csv 文档:https://pandas.pydata.org/docs/reference/api/pandas.read_csv.html
  2. pandas read_excel 文档:https://pandas.pydata.org/docs/reference/api/pandas.read_excel.html
  3. pandas read_sql 文档:https://pandas.pydata.org/docs/reference/api/pandas.read_sql.html
  4. SQLAlchemy 官方文档:https://docs.sqlalchemy.org/
相关推荐
GEO实战经验分享1 小时前
GEO技术与工具全景:从监测仪表盘到Schema的实战指南
数据库
一个天蝎座的程序猿1 小时前
IoTDB集群扩容:从慌乱到从容的经验分享
数据库
小小龙学IT2 小时前
Python SQLAlchemy 2.0 深度解析:从 Core 到 ORM 的数据访问双引擎
开发语言·数据库·python
DongQiShanRen2 小时前
裁决台账双向互校(上):名册与实物的第一道对账
java·linux·运维·数据库·人工智能·自然语言处理·数据挖掘
ShineWinsu2 小时前
对于Redis:AOF持久化的解析
linux·数据库·redis·缓存·面试·持久化·aof
仍然.3 小时前
Redis---集群
数据库·redis·缓存
Allstar_433 小时前
DV/PV 验证管理:为量产放行提供完整证据链——全星研发项目管理 APQP 软件系统汽车电子行业专业级研发项目管理平台
数据库·汽车
小范的技术工坊3 小时前
向量数据库解决了什么问题?
数据库
小蒜学长3 小时前
在线保险服务与管理平台的设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端·服务平台·在线保险