Python技术迷

clevercsv,一个有趣的 Python 库!

有些 CSV 文件,长得像 CSV,读起来根本不像。

你拿 csv.DictReader 一把梭,跑得好好的。第二天换了个供应商导出的文件,分隔符从逗号变成分号,字段里还夹着一堆没转义干净的引号,结果脚本不是读错列,就是整行糊掉。更烦的是,这种文件你肉眼看着“也还行”,程序一吃就炸。

这类脏活我一般不先怪业务代码,先怀疑文件本身。尤其是 CSV 这种格式,表面最朴素,实际最容易藏坑。clevercsv 这个库,就挺适合干这个事。它不是什么大而全的数据处理框架,干的事情很专一:尽量把那些“不太标准但现实里经常遇到”的 CSV 文件,给你猜对、读出来。 这种切入和落法,和我平时写技术文时强调的“先落现场、再落判断”的手感是一致的。

先看个很常见的现场文件:

sample = """order_id;user_name;remark
1001;alice;"paid, wait deliver"
1002;bob;"need ""red"" invoice"
"""

这玩意如果你默认按逗号读,第二列第三列就直接串了。标准库当然也能处理,但前提是你得先知道分隔符、quotechar、escape 这些细节。问题就在这儿:线上接文件时,你往往并不知道。

clevercsv 的用法不复杂,先让它猜 dialect,再按猜出来的规则读。

import clevercsv

with open("orders.csv", "r", encoding="utf-8") as f:
    data = f.read()

dialect = clevercsv.Sniffer().sniff(data)

print(dialect.delimiter)   # 可能是 ; 或 ,
print(dialect.quotechar)   # 可能是 " 或其他

rows = clevercsv.read_table("orders.csv")
for row in rows[:3]:
    print(row)

这地方我第一眼就比较信它的点,不是“自动识别”这四个字,而是它专门在 CSV dialect 检测这件事上做了不少针对性处理。现实里的问题从来不是“不会读 CSV”,而是“文件格式不老实”。clevercsv 干的就是这个脏活。

平时如果你只接自己系统导出的文件,其实没必要上它。规规矩矩的逗号分隔、双引号转义、UTF-8 编码,标准库够了:

import csv

with open("orders.csv", newline="", encoding="utf-8") as f:
    reader = csv.DictReader(f)
for row in reader:
        print(row["order_id"], row["user_name"])

但只要文件来源一杂,事情马上就不是这么回事了。比如财务导出来的文件爱用分号,欧洲区系统导出来的小数点和分隔符还经常互相打架,某些老系统甚至会把一列里的换行也混进去。你这时候再指望人工先打开文件看一眼,再改脚本,基本等于放弃自动化。

我之前写过一个批量导入脚本,目录里每天会丢进来几十个 CSV,来源不统一。最开始偷懒,直接 pandas.read_csv(),参数靠试。后来发现错一批数据,比直接报错还难受。因为报错至少知道停了,读错列是真会往库里灌脏数据。

后来我就把入口换成了“先识别,再校验”这一套:

import clevercsv
from pathlib import Path

defload_csv_safely(file_path: str):
    text = Path(file_path).read_text(encoding="utf-8", errors="replace")
    dialect = clevercsv.Sniffer().sniff(text)

    rows = clevercsv.read_table(file_path)

    header = rows[0] if rows else []
if"order_id"notin header:
raise ValueError(f"{file_path} 表头不对, dialect={dialect}")

return rows

files = Path("./incoming").glob("*.csv")
for f in files:
try:
        rows = load_csv_safely(str(f))
        print(f"{f.name} OK, {len(rows)} rows")
except Exception as e:
        print(f"{f.name} FAIL: {e}")

这段代码不花哨,但很像线上会留着的东西:先把文件吃进来,识别规则,再做最基本的表头兜底。因为很多时候,能读出来不代表读对了。这一点比库本身更重要。

再说个容易忽略的点:clevercsv 解决的是 CSV 结构识别,不是万能数据清洗。比如编码乱掉、列名写错、字段缺失、金额列混进中文备注,这些事它不管。别把工具用大了。它适合放在“导入链路的前半段”,帮你把文件先按比较靠谱的方式切开,后面再接自己的业务校验。

比如金额字段,你还是得自己兜:

defnormalize_amount(raw: str) -> float:
    value = raw.strip().replace(",", "")
return float(value) if value else0.0

很多人一提这种库,就容易写成“提高效率、增强兼容性、适用于多场景”。这种话不能说错,但没什么用。真正有感觉的地方是:你碰到一批野路子 CSV,不想每次手改参数,不想把导入脚本写成猜谜游戏,这时候 clevercsv 能帮你少踩不少坑。

安装也很直接:

pip install clevercsv

然后记住一个判断就够了:

你控制文件格式,用标准库; 你不控制文件来源,先试 clevercsv。

它不算什么重量级神器,但挺有工程味。不是为了炫技,是为了少被脏数据折腾。很多库有趣,不是因为概念新,而是因为它刚好补在那个你反复骂人的位置上。

clevercsv 就属于这一类。