Python日常脚本实战:文件处理、Excel自动化与踩坑指南
1. 为什么是Python日常杂活的首选工具1.1 不是只有写项目才算用Python很多人对Python的印象停留在“做人工智能”“写爬虫”“搞数据分析”似乎得搭个像样的框架、跑通一个完整项目才算真正使用。但我这几年越来越深的体会是Python最值钱的地方恰恰是那些不起眼的“一次性脚本”。举个最典型的例子。有一回我需要把网盘里五百多张照片按拍摄日期归档到“年/月”目录里手动做的话Windows资源管理器里按日期分组新建文件夹全选拖拽一套动作重复十几遍中途还得反复确认有没有放错。我用Python写了不到二十行代码遍历文件读EXIF里的拍摄时间格式化日期shutil.move十秒钟跑完零出错。这类事情不需要架构设计不需要单元测试也不需要部署上线但它们是真正每天都会遇到的“日常杂活”。所以“Python日常记录”这个主题我想写的不是教科书式的知识体系而是我真实消耗过时间的地方安装环境时踩过的坑、写脚本时绕不过去的细节、以及怎么把一段顺手写的代码沉淀成下次还能用的东西。1.2 日常场景里Python凭什么比别的好使如果要给身边的同事推荐处理日常杂活的工具我会先问一个问题你接下来要处理的对象是什么是几十个Excel表格、一堆照片和PDF、结构差不多的网页信息还是服务器和PC之间来回搬运的文件只要主要对象是“文件、表格、网页、文本”中的任意一类Python基本都是备选清单里最靠前的那个。原因不复杂。第一它的语法边界很低读完需求就能开始写不用先建工程文件第二整个生态里针对日常琐事的库非常成熟操作Excel有pandas和openpyxl处理文件路径有pathlib抓网页有requests加BeautifulSoup几乎每件“重复劳动”都能找到对应的轮子第三跨平台体验一致同一份脚本在Windows上归档完照片拿去macOS上处理同事的导出文件改动量通常只是路径分隔符。当然Python不是没有短板。它的运行效率比C、Go差一个数量级但要处理的是五百个文件而不是五亿个瓶颈根本不在语言本身的执行速度而在人手动操作的速度。日常场景里的正确指标从来不是“单次执行多快”而是“从产生需求到拿到结果总共花了多久”。1.3 一个真实案例整理五百张照片的脚本化过程那次的照片归档任务我把它拆成了四步。第一步先确认照片的原始结构发现有的在“微信图片”目录有的在“相机导入”目录还有几张是别人通过聊天软件传过来的混在一起。第二步确定归档规则按拍摄日期归入年/月目录没有EXIF信息的文件按文件修改时间兜底。第三步写脚本时没急着做全量操作先跑一个dry-run版本只打印“哪张照片会移动到哪个目录”核对一轮没有明显问题。第四步才是真正执行移动。那个dry-run的习惯让我躲过一劫。脚本里有个Bug读取EXIF时日期解析失败会返回None如果直接做字符串拼接文件会被扔进一个None/None目录。要是不先预览一遍直接全量跑五百张照片的目录结构就乱套了。从那以后凡是对文件做移动、删除、重命名的脚本我都会保留一句“预览模式”的开关这是用真实教训换来的经验。2. 安装环境这步做好了后面少踩一半坑2.1 版本选择别为了“新”去装最新版每次有朋友刚开始学Python第一个问题往往是“Python现在装哪个版本”。网络上的回答五花八门但我的建议一直很保守如果你的日常工作用到的是第三方库选比最新版本落后一档的稳定版而不是最新的大版本。因为主流第三方库的大版本适配总是滞后于Python官方发版的节奏装上最新版之后最常遇到的报错是“某某包找不到对应版本”或者编译安装时报一堆错。日常使用我推荐直接去官网下载官方安装包。Windows安装时有一个无数人忽略的关键选项安装向导第一页最底部的“Add Python to PATH”复选框一定记得勾上。这个选项默认是不勾的忘掉它的后果是装完了之后在终端敲python完全没有反应然后你得花二十分钟搜索“python不是内部或外部命令”。与其事后折腾环境变量不如装的时候多看一眼。装完以后打开终端输入python --version确认输出正常这一步至少能筛掉八成环境问题。如果你在多个项目里用到的Python版本不一样推荐再装一个pyenv它能做到在项目目录里自动切换版本以后克隆旧项目的时候会省下大量“版本不对”的烦恼。2.2 虚拟环境从第一天就用起来我见过太多人学会Python之后顺手就pip install一切装到全局环境里。短时间内没什么问题直到某天装了一个新包需要升级旧依赖把项目A依赖的版本顶掉项目B立刻罢工你根本想不起来是哪个包动的手脚。这个问题的标准解法是虚拟环境。虚拟环境的原理不复杂就是为每个项目创建一套独立的Python运行目录项目之间互不干扰。日常开发里我已经习惯了任何新项目落地第一件事就是执行下面两行python -m venv venv然后在Windows上激活它venv\Scripts\activate在macOS或Linux上激活它source venv/bin/activate激活之后终端提示符前面会出现一个(venv)前缀这个时候所有pip install装的包都会进当前项目的虚拟环境而不是污染全局。一个容易踩的细节是很多人会忘记激活虚拟环境就直接pip install装完还在纳闷为什么项目里import报错。建议把激活环境这一步养成肌肉记忆形成习惯之后自然就不容易出错了。2.3 换了机器后的一键还原配置搬家换电脑是检验一个Python环境是否“文明”的好时机。有的人换电脑等于重新渡劫到处找以前装过哪些包、哪些版本花一晚上重新装。而我的习惯是每个项目都在requirements.txt里记录依赖清单随手更新换机器的时候只需要一条命令pip install -r requirements.txt生成这份清单就更简单了。在虚拟环境激活的状态下执行pip freeze requirements.txtpip freeze会把当前环境里所有包和精确版本号导出成文本文件这份文件放到代码仓库里就是项目的“依赖配置快照”。不过我后来偏向于只记录直接依赖不锁定传递依赖这意味着我会手动维护一个精简版requirements.txt只写我直接import过的库和版本范围像下面这样requests2.28,3.0 pandas1.5,2.0 openpyxl3.0,4.0这样做的好处是升级时不会被旧版本锁死同时新机器上依旧可以一条命令恢复环境。日常用完随手维护十分钟的事省下的是下次换机器时一整晚的折腾。3. 文件与Office文档批量处理日常最高频的场景3.1 文件重命名与分类归档先预览再动手文件操作是日常琐事里出现频率最高的一类。公司里其他部门的人发过来的报表永远叫“新建文档(17).docx”下载的发票永远是一串数字PDF课程录像的命名乱七八糟诸如此类。写过一次批量重命名脚本之后我每次再遇到这种活儿都会有一种“这也能写脚本”的朴素快乐。下面这段脚本是我处理“文件名标准化”时反复用到的基础模板它的目标是把文件名开头的序号补成两位数方便排序from pathlib import Path import re folder Path(待处理文件) pattern re.compile(r^(\d)\.(.)\.(pdf|docx?|xlsx?)$, re.IGNORECASE) for file in folder.iterdir(): if not file.is_file(): continue m pattern.match(file.name) if not m: continue num, name, ext m.groups() new_name f{int(num):02d}.{name}.{ext} print(f{file.name} - {new_name}) # 确认无误后把下面一行的注释去掉再执行 # file.rename(file.with_name(new_name))先说为什么用pathlib而不是老式的os.path。pathlib把路径封装成对象拼接路径用/运算符优雅得多而且它在Windows和Linux/macOS上会自动处理分隔符差异跨平台少踩坑。上面的脚本默认处于“只打印不改动”的预览模式先跑一遍等打印结果全部符合预期再打开重命名的行执行。这个习惯一旦建立批处理文件时的安全感会提升一大截。3.2 Excel自动整理报表pandas与openpyxl的分工办公场景里Excel几乎是绕不开的。很多财务、运营的同学每天花两三个小时做的事无非是把几个表格里相同工号的数据对齐、算出汇总、生成图表。这类工作交给人做既慢又容易看漏交给pandas处理一小段代码就搞定。一个最常遇到的需求是“多表按关键列合并”。比如两张表一张是员工基本信息一张是上月绩效通过工号关联最终要输出一张完整报表。pandas里只需要一行import pandas as pd info pd.read_excel(员工信息.xlsx) perf pd.read_excel(绩效表.xlsx) result pd.merge(info, perf, on工号, howleft) result.to_excel(合并报表.xlsx, indexFalse)这段代码里值得注意的细节有三处。第一read_excel依赖openpyxl作为引擎所以用pandas处理Excel之前需要确认openpyxl已经装好。第二merge里的on参数指定关联键如果两张表里的列名不一样需要用left_on和right_on分开指定。第三how参数决定了保留哪边的数据日常最常用的是inner和left选错会直接导致结果多行或少行处理前最好先看一眼两张表各自的行数。3.3 Word和PDF改文档也没想象中麻烦处理Word文档日常遇到最多的是“批量替换”。比如一份通知要发给不同部门把“贵部门”换成“销售部”“市场部”。用Word自带的查找替换一个一个文件打开操作也还行但几十上百个文件时还是写脚本痛快。这里推荐用python-docx它被称为“最接近人话”的Word操作库。from docx import Document for file in [通知.docx]: doc Document(file) for paragraph in doc.paragraphs: if 贵部门 in paragraph.text: paragraph.text paragraph.text.replace(贵部门, 销售部) doc.save(通知_已替换.docx)这个示例能跑通基本的段落文本替换但有个隐藏问题如果被替换的文字横跨文档里的多个run对象直接按paragraph.text整体判断之后再赋值会丢失原有的格式。更稳妥的写法是遍历paragraph.runs在每个run内部做替换这样能保留大部分格式。Python日常处理Office文件往往不是“不会写代码”而是“不知道格式细节会这样坑人”所以先从能跑通再逐步完善比一口气追求完美更务实。PDF方面我的日常需求主要两个合并和提取文本。合并我推荐pypdf老项目里常见的PyPDF2已经改名了提取文本则先看PDF是文字型还是扫描型只有文字型才能直接提取。如果是扫描件就得走OCR那是另一个话题了。日常记住一条就够拿到PDF先别急着写代码判断它的类型能够省下大半调试时间。4. 网络请求与信息抓取把重复查询变成脚本4.1 requests库日常接口调试的标配日常工作中经常需要调用内部系统的接口人工开浏览器、登录、点查询看在开发者工具里翻半天的数据效率实在不高。用requests库把这些过程脚本化之后固定几件事的流程将被压缩成一条命令。最基本的一个GET请求长这样import requests resp requests.get(https://api.example.com/data, timeout10) print(resp.status_code) print(resp.json())这段代码看起来简单但有几个日常必踩的细节。timeout参数一定要显式设置不设置的话请求可能因为网络问题挂在那里很久脚本看起来像死机了。其次很多系统都要求带请求头最常见的User-Agent不设置的话部分服务会直接拒绝连接。更正式的做法是维护一个session对象把登录后的Cookie塞进去后面的请求就都带上身份信息了session requests.Session() session.headers.update({User-Agent: Mozilla/5.0}) login_data {username: yourname, password: yourpass} session.post(https://api.example.com/login, datalogin_data) resp session.get(https://api.example.com/report?date2025-01-01)日常重复的“登录然后拿数据”这整套动作最后会收敛成这样一个脚本每次需要报表跑一下就好省得再开浏览器点点点。4.2 解析网页时编码才是最隐蔽的坑拿到网页源码之后的解析很多人第一反应是用requests的resp.text但resp.text返回的字符串是根据响应头里的编码猜测出来的。不少国内站点的响应头没写完整的字符集或者写的是ISO-8859-1这种时候resp.text一打开就是乱码。处理这个问题的标准顺序是先用resp.encoding看看库认为的编码是什么如果明显不对再根据网页meta标签里的charset手动指定resp requests.get(url, timeout10) resp.encoding utf-8如果已经拿到了一堆乱码文本也不要急着崩溃可以试试先用latin-1编码转回去再重新用正确编码解码text resp.content.decode(latin-1, errorsignore) text text.encode(latin-1).decode(utf-8, errorsignore)这类编码问题在中文网页解析中极其常见是“代码看着没问题但输出就是乱码”的头号元凶。日常遇到乱码先怀疑编码别怀疑人生。4.3 抓取信息的边界能跑通不等于可以随便跑这部分我希望说得直白一点。requests加BeautifulSoup确实能让信息抓取变得非常轻松但技术上的“能”不代表规则上的“可以”。在抓取任何网站的数据之前我的习惯是先看两个地方一个是robots.txt另一个是网站的服务条款。前者能告诉你好一些基础抓取规则后者则界定了数据能不能被允许保存使用。日常合规使用的关键点一是控制请求频率不要瞬间发送大量请求避免给对方服务器造成压力也避免自己的IP被临时限制二是不把抓取到的数据用于商业用途除非已经明确获得授权三是不绕过登录、验证码等访问控制机制。简单来说脚本化的目的是替人省去重复劳动而不是给他人服务造成负担。5. 我踩过的坑比报错更隐蔽的问题5.1 时区与日期格式的“隐形杀手”有一回我写了个脚本每天定时拉取业务系统前一天的订单数据一开始跑得好好的后来突然某天全乱了订单日期整体偏移了一天。排查了半天原因不在代码逻辑而在时区。服务器用的是UTC时间而业务数据记录的是本地时间直接拿datetime.now()去算“昨天”在服务器上算出来的其实是本地时间当服务器时区设置不同步时结果自然就偏了。日常处理时间的代码里我给自己立了三条规矩。第一所有业务数据统一用ISO格式字符串或时间戳存储不依赖自然语言的日期格式第二转换时区时使用zoneinfo模块显式指定时区from zoneinfo import ZoneInfo from datetime import datetime local_tz ZoneInfo(Asia/Shanghai) now datetime.now(local_tz)第三别自己手写时差加减比如“UTC8就是加8小时”夏令时区域分分钟教做人。能用标准库做的就别自己写。5.2 路径里那些“看不见”的坑Windows和macOS/Linux的路径分隔符差异是另一个日常高频翻车点。Windows用反斜杠\而macOS和Linux用正斜杠/。如果你在脚本里拼接路径时写死了\代码换个环境就炸。解决方式很简单永远用pathlib它会自动适配当前操作系统。真正隐蔽的坑在细节里。比如Windows路径中常常包含空格比如C:\Users\My Documents\file.txt有些处理函数会把它当成两个参数。再有就是Windows路径大小写不敏感而Linux敏感所以同一个脚本在Windows上正常部署到Linux服务器上突然找不到文件了。日常开发时只要记住“路径问题用pathlib不要手拼字符串”就能躲掉八成路径相关的坑。5.3 乱码问题文件编码的统一与兜底处理CSV和文本文件时编码问题几乎人人都会遇到。用Excel另存为的CSV默认是gbk编码而Python的open函数默认编码在Windows上通常是gbk在macOS和Linux上通常是utf-8两边读同一个文件总有一边会炸。我的习惯是读任何文本文件都显式指定编码并且在不确定的时候用errors参数兜底with open(data.csv, encodingutf-8, errorsreplace) as f: content f.read()如果原本是gbk编码用utf-8去读会得到一个莫名其妙的错或者一串替换字符这时候可以换成encodinggbk重试。后来我处理这类问题总结出一个简单的排查顺序先看文件内容是中文还是英文再尝试主流编码逐个测试最高效的方法是写一个几行的小脚本把常见编码都试一遍能正常解码且没有大量替换符的基本就是正确的。6. 日常工具箱与代码复用习惯6.1 我的高频库清单不少初学者会问“日常到底该学哪些库”。我认为不必贪多把下面这张清单里的库用熟日常绝大多数场景就够用了。每个库都有它最擅长解决的“一类问题”记住这个映射比记住所有API更重要。库名主要用途典型场景pathlib路径处理与文件遍历批量重命名、查找特定类型文件shutil文件复制与移动归档、备份、清理目录requestsHTTP客户端调用接口、下载文件、抓取网页BeautifulSoup4HTML解析从网页中提取结构化数据pandas表格数据处理Excel合并、过滤、统计汇总openpyxlExcel文件读写修改单元格、生成格式化的xlsxpython-docxWord文件读写批量替换、生成通知文档pypdfPDF合并拆分与文本提取合并多个PDF、读取目录这张清单之外还有一堆垂直库比如发邮件用yagmail、操作图片用Pillow、定时任务用系统自带的计划任务或cron都是用到时再查文档就行。6.2 把一次性脚本沉淀成个人工具库以前我写脚本常常是“用一次就扔”后来发现自己反复在写同样的功能。真正让日常记录变成资产的做法是把常用的小函数抽出来整理成一个myutils.py放进所有项目都能引用的地方。我的个人工具库里最常用到的是这两个函数。一个是“安全重命名”它保证目标文件名如果已经存在就自动追加序号另一个是“带预览的文件移动”它会在真正执行前把将要发生的所有操作打印出来。这些函数就几十行但它们承载了我过往所有踩坑经验。整理个人工具库时我给自己的一个原则是每个函数只做一件事参数尽量简单并且必须写清楚输入输出的格式。因为这类函数往往在多个项目里复用今天不写注释三个月后自己都看不懂。6.3 定时任务让日常脚本自己跑起来很多脚本固化下来之后可以交给系统调度彻底解放双手。Windows上可以用“任务计划程序”macOS和Linux上可以用crontab。比如每天早上九点自动拉取数据生成报表晚上七点自动备份某个目录都可以配置成定时任务。配置crontab的日常示例# 每天早上9点执行报表脚本 0 9 * * * cd /path/to/project /usr/bin/python3 daily_report.py这里有一个容易踩的坑crontab执行时的PATH和你在终端登录时的PATH不一样所以脚本里如果调用了某些命令最好在脚本开头用绝对路径或者在crontab里设置PATH环境变量。另外脚本的输出默认不会显示建议在脚本里用logging把关键信息写入日志文件方便出问题时查看。定时任务真正跑起来之后Python日常记录的形态就完全变了。从“我手动去执行命令”变成“每天到点自动执行、日志自动记录、有问题才需要人介入”。那种“你只管写代码定期检查其余交给机器”的成就感是这套体系给我最大的回报。我自己这几年的体会是Python日常记录不在于代码写得多花哨而在于一个问题一旦出现过一次就应该被沉淀成工具或文档下次再遇到同样的事情五分钟就能解决。从环境安装的细枝末节到文件批处理、表格合并、定时跑数这些单独拎出来都不起眼的小事聚在一起就是每天实实在在省下来的两三个小时。写下来就是积累。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →