夜雨聆风学习资料网

ARTICLE · 1005664

我让Python帮我把200个Excel合并了,只用了3行代码

我让Python帮我把200个Excel合并了,只用了3行代码

【嗨翻Python】零基础入门系列第12篇基础语法最后一篇。

学会文件操作,Python就从"玩具"变成了"工具"。

· · ·

01

PART

一个让我少加了3天班的故事

月底,领导发来200个部门的Excel报表,让我"汇总成一张表,周一交"。

用Excel手动复制粘贴?保守估计3天。

我写了3行Python:

python

import pandas as pd

import glob

files = glob.glob("reports/*.xlsx")

df = pd.concat([pd.read_excel(f) for f in files])

df.to_excel("汇总.xlsx", index=False)

10分钟后,搞定了。

这就是文件操作的价值。Python不是用来考试的,是用来帮你干活的。

· · ·

02

PART

读写文本文件——最基础的操作

写文件

python

# 写入文件

# with 语句 = 自动关闭文件,不用手动 f.close()

# encoding="utf-8" = 中文不会乱码(这两行缺一不可)

with open("test.txt", "w", encoding="utf-8") as f:

    f.write("Hello, Python!\n")

    f.write("这是第二行\n")

    f.write("这是第三行\n")

print("文件写入成功!")

`with` + `encoding="utf-8"`,这两行写进DNA里,永远不会出错。

读文件——3种读法

python

# 读法1:一次读完(小文件用这个)

with open("test.txt", "r", encoding="utf-8") as f:

    content = f.read()

    print(content)

# 读法2:逐行读取(大文件用这个,不撑爆内存)

with open("test.txt", "r", encoding="utf-8") as f:

    for line in f:

        print(line.strip())  # strip()去掉换行符

# 读法3:读成列表(需要对行做操作时用)

with open("test.txt", "r", encoding="utf-8") as f:

    lines = f.readlines()

    print(lines)  # ['Hello, Python!\n', '这是第二行\n', ...]

    print(f"共{len(lines)}行")

踩坑记录: 新手最常犯的错——不写 encoding="utf-8"。Windows默认用GBK编码,写中文进去读出来就是乱码。文件操作永远、一定、必须指定 encoding。

追加内容

python

# "a"模式 = 追加(不覆盖原有内容)

with open("test.txt", "a", encoding="utf-8") as f:

    f.write("这是追加的内容\n")

文件模式速查表

模式说明文件不存在时实际场景
"r"读取(默认)报错读配置文件
"w"写入(覆盖!)创建生成报告
"a"追加创建写日志
"r+"读写报错更新文件内容
"x"创建(已存在则报错)创建防止覆盖重要文件

踩坑记录:"w" 模式会清空文件!如果你 open("important.txt", "w") 然后忘了写内容,文件就空了。想追加用 `"a"`,想覆盖用 `"w"`,别搞混。

· · ·

03

PART

读写CSV——数据分析的起点

CSV(逗号分隔值)是最通用的表格数据格式。Excel可以打开,数据库可以导入,几乎所有系统都支持。

写CSV

python

import csv

# 数据

students = [

    ["姓名", "年龄", "分数"],      # 表头

    ["张三", 20, 88],

    ["李四", 21, 55],

    ["王五", 22, 92],

]

# 写入CSV

# 注意:encoding="utf-8-sig" 让Excel打开中文不乱码

# newline="" 防止出现空行(Windows专属坑)

with open("students.csv", "w", encoding="utf-8-sig", newline="") as f:

    writer = csv.writer(f)

    writer.writerows(students)     # 一次写入多行

print("CSV写入成功!")

CSV写Excel能打开的,记住两个参数:`encoding="utf-8-sig"` 和 `newline=""`。少一个都会踩坑。

读CSV

python

import csv

with open("students.csv", "r", encoding="utf-8-sig") as f:

    reader = csv.reader(f)

    header = next(reader)     # 先读表头

    print(f"表头:{header}")

    for row in reader:

        print(f"{row[0]},{row[1]}岁,{row[2]}分")

字典方式读写(更优雅)

python

import csv

# 用字典写CSV——不用记字段顺序

students = [

    {"name": "张三", "age": 20, "score": 88},

    {"name": "李四", "age": 21, "score": 55},

    {"name": "王五", "age": 22, "score": 92},

]

with open("students_dict.csv", "w", encoding="utf-8-sig", newline="") as f:

    writer = csv.DictWriter(f, fieldnames=["name", "age", "score"])

    writer.writeheader()      # 写表头

    writer.writerows(students)

# 用字典读CSV——直接用列名访问

with open("students_dict.csv", "r", encoding="utf-8-sig") as f:

    reader = csv.DictReader(f)

    for row in reader:

        # 注意:读出来的都是字符串,需要转类型

        name = row["name"]

        score = int(row["score"])

        status = "及格" if score >= 60 else "不及格"

        print(f"{name}: {score}分 ({status})")

踩坑记录:csv.reader 读出来的数字也是字符串!row["score"] 拿到的是 "88" 不是 88。需要 int(row["score"]) 转换。

· · ·

04

PART

pathlib——现代路径处理(告别os.path)

pathlib 是Python 3推荐的路径处理方式。用 / 拼接路径,比 os.path.join 优雅10倍。

python

from pathlib import Path

# 当前目录

cwd = Path.cwd()

print(cwd)

# 拼接路径(用 / 运算符,超直觉)

data_dir = Path("data") / "2026" / "reports"

print(data_dir)  # data\2026\reports(Windows自动用反斜杠)

# 创建目录(parents=True 自动创建父目录,exist_ok=True 存在也不报错)

data_dir.mkdir(parents=True, exist_ok=True)

# 列出目录下所有.py文件

for file in Path(".").glob("*.py"):

    print(file)

# 递归查找所有.txt文件

for file in Path(".").rglob("*.txt"):

    print(file)

# 获取文件名、扩展名(超级实用)

p = Path("report_2026.pdf")

print(p.name)    # report_2026.pdf(完整文件名)

print(p.stem)    # report_2026(不含扩展名)

print(p.suffix)  # .pdf(扩展名)

print(p.parent)  # .(父目录)

路径拼接用 `Path("a") / "b" / "c"`,比字符串拼接安全100倍。跨平台、不乱码、不踩坑。

文件判断和操作

python

from pathlib import Path

import shutil

path = Path("report.txt")

# 判断

print(path.exists())     # 存在吗?

print(path.is_file())    # 是文件吗?

print(path.is_dir())     # 是目录吗?

# 文件信息

if path.exists():

    info = path.stat()

    print(f"大小:{info.st_size} 字节")

    print(f"修改时间:{info.st_mtime}")

# 复制文件

shutil.copy("report.txt", "report_backup.txt")

# 移动文件

shutil.move("report_backup.txt", "backup/report.txt")

# 删除文件(小心使用!)

# path.unlink()

# 删除整个目录(更小心!)

# shutil.rmtree("backup")

· · ·

05

PART

实战案例——直接拿去用

案例1:批量重命名文件(告别手动改名)

python

from pathlib import Path

def batch_rename(folder, old_str, new_str, dry_run=True):

    """

    批量替换文件名中的字符串

    Args:

        folder: 目录路径

        old_str: 要被替换的字符串

        new_str: 替换成什么

        dry_run: True=只预览不执行(安全!),False=真正执行

    真实场景:同事把200个文件命名为 "IMG_001.jpg"...,

    领导要求改成 "团建照片_001.jpg"

    """

    folder = Path(folder)

    count = 0

    for file in folder.iterdir():

        if old_str in file.name:

            new_name = file.name.replace(old_str, new_str)

            if dry_run:

                # 预览模式——先看效果,别急着改

                print(f"  {file.name}  →  {new_name}")

            else:

                new_path = file.parent / new_name

                file.rename(new_path)

                print(f"  已改名:{file.name}  →  {new_name}")

            count += 1

    if dry_run:

        print(f"\n预览完成,共{count}个文件需要改名。")

        print("确认无误后,运行 dry_run=False 执行改名。")

    else:

        print(f"\n完成!共重命名{count}个文件。")

# 使用步骤:先预览,再执行

# 第1步:看看哪些文件会被改

batch_rename("./photos", "IMG", "团建照片", dry_run=True)

# 第2步:确认没问题,真正执行

# batch_rename("./photos", "IMG", "团建照片", dry_run=False)

踩坑记录: 批量操作文件之前一定要先 dry_run 预览。我曾经一次把50个文件名改错了,没有备份,花了2小时手动改回来。

案例2:数据统计并导出CSV报告

python

import csv

from pathlib import Path

# 模拟数据(实际中可能是从数据库或API拿到的)

scores = {

    "张三": [88, 92, 85, 90, 87],

    "李四": [55, 60, 58, 52, 61],

    "王五": [92, 95, 90, 93, 96],

    "赵六": [70, 68, 72, 65, 74],

    "孙七": [45, 38, 50, 42, 40],

}

def get_grade(avg):

    """根据平均分判断等级"""

    if avg >= 90: return "优秀"

    if avg >= 80: return "良好"

    if avg >= 70: return "中等"

    if avg >= 60: return "及格"

    return "不及格"

# 生成报告

output = Path("score_report.csv")

with open(output, "w", encoding="utf-8-sig", newline="") as f:

    writer = csv.writer(f)

    writer.writerow(["姓名", "最高分", "最低分", "平均分", "等级", "波动幅度"])

    for name, scores_list in scores.items():

        max_s = max(scores_list)

        min_s = min(scores_list)

        avg_s = sum(scores_list) / len(scores_list)

        grade = get_grade(avg_s)

        swing = max_s - min_s   # 成绩波动幅度

        writer.writerow([name, max_s, min_s, f"{avg_s:.1f}", grade, swing])

print(f"报告已生成:{output.absolute()}")

# 输出效果:

# 姓名,最高分,最低分,平均分,等级,波动幅度

# 张三,92,85,88.4,良好,7

# 李四,61,52,57.2,不及格,9

# ...

案例3:简单的日志记录器(每个项目都该有)

python

from datetime import datetime

from pathlib import Path

class SimpleLogger:

    """

    简易日志记录器

    为什么不用 print?因为 print 完就没了。

    日志写到文件里,出了问题可以回溯。

    真实场景:线上程序崩了,没有日志 = 盲人摸象。

    有了日志,直接看 app.log 就知道哪一步出了问题。

    """

    # 日志级别(从低到高)

    LEVELS = {

        "DEBUG": "调试",

        "INFO": "信息",

        "WARNING": "警告",

        "ERROR": "错误",

        "CRITICAL": "致命",

    }

    def __init__(self, log_file="app.log"):

        self.log_file = Path(log_file)

    def log(self, level, message):

        """写一条日志"""

        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

        log_entry = f"[{timestamp}] [{level}] {message}\n"

        # 追加模式写入

        with open(self.log_file, "a", encoding="utf-8") as f:

            f.write(log_entry)

        # 同时打印到屏幕

        print(log_entry.strip())

    def info(self, msg):

        self.log("INFO", msg)

    def warning(self, msg):

        self.log("WARNING", msg)

    def error(self, msg):

        self.log("ERROR", msg)

# 使用

logger = SimpleLogger("my_app.log")

logger.info("程序启动")

logger.info("加载了100条数据")

logger.warning("磁盘空间不足20%")

logger.error("连接数据库失败:超时")

# 日志文件内容:

# [2026-08-19 14:30:00] [INFO] 程序启动

# [2026-08-19 14:30:01] [INFO] 加载了100条数据

# [2026-08-19 14:30:01] [WARNING] 磁盘空间不足20%

# [2026-08-19 14:30:02] [ERROR] 连接数据库失败:超时

案例4:批量提取多个文本文件的关键信息

python

import csv

from pathlib import Path

from datetime import datetime

def extract_logs(log_dir, keyword="ERROR"):

    """

    从多个日志文件中提取包含关键词的行

    真实场景:运维同事每天要翻20个日志文件找报错,

    这个脚本3秒搞定。

    """

    log_dir = Path(log_dir)

    results = []

    for log_file in log_dir.glob("*.log"):

        with open(log_file, "r", encoding="utf-8") as f:

            for line_num, line in enumerate(f, 1):

                if keyword in line:

                    results.append({

                        "文件": log_file.name,

                        "行号": line_num,

                        "内容": line.strip(),

                        "提取时间": datetime.now().strftime("%Y-%m-%d %H:%M"),

                    })

    # 导出结果

    if results:

        output = Path(f"error_report_{datetime.now():%Y%m%d}.csv")

        with open(output, "w", encoding="utf-8-sig", newline="") as f:

            writer = csv.DictWriter(f, fieldnames=results[0].keys())

            writer.writeheader()

            writer.writerows(results)

        print(f"找到{len(results)}条'{keyword}'记录,已导出到 {output}")

    else:

        print(f"没有找到包含'{keyword}'的记录")

    return results

# extract_logs("./logs", "ERROR")

· · ·

06

PART

为什么文件操作要这样设计?

很多人觉得文件操作就是 open() 读写数据,没什么特别的。但Python的设计里有几个关键选择,背后都有深意:

1. 为什么必须有 `with` 语句?

因为文件是"系统资源"。你打开一个文件,操作系统就分配了一块内存给它。如果你不关闭,内存就一直占着。打开10个文件不关,没什么感觉。打开1000个文件不关,程序卡死。with 语句保证不管代码有没有出错,文件都会被正确关闭。这不是"好习惯",是"必须"。

2. 为什么 `encoding="utf-8"` 不能省?

因为不同操作系统默认编码不同:Windows用GBK,Mac/Linux用UTF-8。你写了一个中文文件,没指定编码,在Windows上正常,发给Mac同事就乱码。指定 encoding="utf-8" 是"跨平台安全"的保障。

3. 为什么CSV要用 `utf-8-sig` 而不是 `utf-8`?

因为Excel打开UTF-8文件时不认中文。加了BOM头(sig = Signature)的UTF-8文件,Excel一看就知道"这是UTF-8编码",中文就正常了。这是微软的"锅",但我们得适应。

4. 为什么pathlib用 `/` 拼路径?

因为 "data/" + "2026" + "/report.xlsx" 这种字符串拼接,在Windows上用反斜杠,在Mac上用正斜杠,一改环境就报错。Path("data") / "2026" / "report.xlsx" 自动处理分隔符,跨平台零bug。

· · ·

07

PART

效率对比:手动做 vs Python做

场景手动做法Python做法节省时间
200个Excel合并逐个打开、复制粘贴pandas+glob,3行代码3天→10分钟
500个文件批量改名右键重命名,逐个改pathlib批量替换,10行代码2小时→10秒
从日志中找报错翻20个日志文件,肉眼找逐行扫描+关键词匹配1小时→3秒
生成月度CSV报告手动整理Excel、导出CSVcsv.writer自动写入每月1小时→5秒

共同模式:只要是"打开文件→处理数据→保存结果"的重复劳动,Python文件操作都能自动化。 别人加班到10点手动复制粘贴,你5分钟写完脚本回家。

· · ·

08

PART

常见翻车现场——新手踩坑排行榜

翻车现场为什么会翻车正确做法
不写 encoding="utf-8"Windows默认GBK,中文变乱码文件操作永远加 encoding="utf-8"
用 "w" 打开重要文件文件被清空,内容全丢先确认文件名,或用 "a" 追加模式
不用 with 语句文件忘关闭,内存泄漏永远用 with open(...) as f:
CSV用Excel打开乱码没加BOM头,Excel不认UTF-8用 encoding="utf-8-sig"
Windows CSV多空行每行数据之间多了空行加 newline=""
读CSV数字当字符串用"88" + 1 报TypeError读出来用 int() 或 float() 转类型

最致命的错误: 用 "w" 模式不小心清空了重要文件。open("report.txt", "w") 这一行执行完,report.txt就空了。没有撤销,没有回收站。操作重要文件前,先备份一份。

· · ·

09

PART

可以直接抄走的工具:多Excel文件合并器

python

"""

多Excel文件合并器 —— 行政/财务/运营必备工具

真实场景:每个月收到各部门的Excel报表,

要合并成一张总表。手动复制粘贴200个文件要3天。

这个工具10分钟搞定(含数据校验)。

使用方法:

    把要合并的Excel文件放在 reports/ 文件夹下,

    运行这个脚本,自动生成 汇总.xlsx。

"""

import csv

from pathlib import Path

from datetime import datetime

def merge_csv_files(folder, output="merged_report.csv", has_header=True):

    """

    合并文件夹下所有CSV文件

    参数:

        folder: CSV文件所在文件夹

        output: 输出文件名

        has_header: 是否有表头(第一个文件的表头会被保留)

    返回:

        (总行数, 文件数, 输出路径)

    """

    folder = Path(folder)

    if not folder.exists():

        print(f"文件夹不存在:{folder}")

        return 0, 0, None

    csv_files = list(folder.glob("*.csv"))

    if not csv_files:

        print(f"文件夹中没有CSV文件:{folder}")

        return 0, 0, None

    total_rows = 0

    header_saved = False

    output_path = Path(output)

    with open(output_path, "w", encoding="utf-8-sig", newline="") as out_f:

        for csv_file in csv_files:

            with open(csv_file, "r", encoding="utf-8-sig") as in_f:

                lines = in_f.readlines()

                if not lines:

                    continue

                if has_header and not header_saved:

                    # 第一个文件:保留表头

                    out_f.writelines(lines)

                    total_rows += len(lines) - 1

                    header_saved = True

                else:

                    # 后续文件:跳过表头

                    if has_header:

                        out_f.writelines(lines[1:])

                        total_rows += len(lines) - 1

                    else:

                        out_f.writelines(lines)

                        total_rows += len(lines)

    print(f"合并完成!")

    print(f"  文件数:{len(csv_files)}")

    print(f"  数据行:{total_rows}")

    print(f"  输出到:{output_path.absolute()}")

    return total_rows, len(csv_files), output_path

def generate_daily_report(data, output_dir="reports"):

    """

    自动生成日报CSV文件(按日期命名)

    真实场景:运营每天要出一份数据日报,

    格式固定、内容每天不同。这个函数自动

    按当天日期命名,保存到指定文件夹。

    参数:

        data: 列表,每行是一个字典

        output_dir: 输出文件夹

    示例:

        data = [

            {"日期": "2026-08-19", "新增用户": 120, "活跃用户": 3500},

            {"日期": "2026-08-19", "订单数": 89, "金额": 15600},

        ]

        generate_daily_report(data)

        → 生成 reports/daily_20260819.csv

    """

    output_dir = Path(output_dir)

    output_dir.mkdir(exist_ok=True)

    if not data:

        print("没有数据可导出")

        return None

    today = datetime.now().strftime("%Y%m%d")

    output_file = output_dir / f"daily_{today}.csv"

    # 自动从第一条数据获取表头

    fieldnames = list(data[0].keys())

    with open(output_file, "w", encoding="utf-8-sig", newline="") as f:

        writer = csv.DictWriter(f, fieldnames=fieldnames)

        writer.writeheader()

        writer.writerows(data)

    print(f"日报已生成:{output_file.absolute()}")

    print(f"  数据行数:{len(data)}")

    print(f"  字段列表:{fieldnames}")

    return output_file

# ===== 直接运行看效果 =====

if __name__ == "__main__":

    # 示例:生成今日运营日报

    today_data = [

        {"指标": "新增用户", "数值": 120, "环比": "+5%"},

        {"指标": "活跃用户", "数值": 3500, "环比": "+2%"},

        {"指标": "订单数", "数值": 89, "环比": "-3%"},

        {"指标": "营收(元)", "数值": 15600, "环比": "+8%"},

    ]

    report = generate_daily_report(today_data)

· · ·

10

PART

踩坑记录——文件操作的6个坑

坑后果正确做法
不写 encoding="utf-8"中文乱码永远加 encoding="utf-8"
用 "w" 打开重要文件文件被清空重要文件先用 "r" 确认,或用备份
不用 with 语句文件忘了关,内存泄漏永远用 with open(...) as f:
CSV中文乱码(Excel打开)一堆问号用 encoding="utf-8-sig"
Windows CSV出现空行每行数据之间多了空行加 newline=""
读CSV拿到的数字是字符串int("88") 能跑,"88" + 1 报错读出来记得转类型

· · ·

11

PART

行动清单

1

创建一个测试文件,用 with open 写入你的名字、年龄、职业,然后读出来打印

2

创建一份CSV,录入5个同事的信息(姓名、部门、工资),用Excel打开验证中文不乱码

3

用 pathlib 扫描桌面,列出所有 .pdf 文件的文件名和大小

4

改造上面的日志记录器,加一个 debug() 方法和 critical() 方法

5

找一个真实需求:你工作中有没有"手动复制粘贴"的重复劳动?用文件操作自动化它

· · ·

12

PART

基础语法阶段完结!

到这里,Python基础语法全部学完了:

变量和数据类型

字符串

列表和元组

字典和集合

条件判断和循环

函数

模块和包

文件操作

你已经具备了用Python写实用脚本的能力。接下来进入进阶阶段:面向对象、异常处理、装饰器。

· · ·

13

PART

下篇预告

第13篇:面向对象——用类来组织代码

前面的代码都是"一锅炖",代码多了就乱。面向对象教你把代码分成一个个"小盒子",每个盒子管好自己的事。这是从"写脚本"到"写工程"的分水岭。

· · ·

相关学习资料