ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Python Excel合并工具开发day2:环境坑与数据清洗实战日志

Python Excel合并工具开发day2:环境坑与数据清洗实战日志 如果你恰好处于某个项目的第二天这个时间节点其实比第一天更值得认真对待。很多项目和技术尝试不是死在难度上而是死在第二天的混乱里第一天的热情还在想象中的进展却变成了和报错信息死磕于是第三天很多人悄悄停下来了。我最近给自己定了一个30天独立开发计划目标是用Python做一个自动合并清洗Excel的小工具这篇day2日志记录的就是这个项目第二天的完整经历包括环境问题排查、核心功能实现、时间消耗记录以及从这一天里总结出来的几条经验。你要是正在做自动化脚本、写工具类程序或者任何需要连续推进的个人项目这篇日志里的内容应该都能直接用上。1. 为什么第二天值得单独写一页日志1.1 项目背景与第一天留下的摊子先交代清楚我在做什么。这个30天计划的目标非常朴素用Python给办公室写一个小工具把一堆格式乱七八糟的Excel分表自动合并成一张总表同时完成去重、日期格式统一和缺失字段填充。使用场景是财务和行政口的同事每个月要把十几个分表手工合成总表再花两三个小时清理重复行、调整格式。我想把这个过程压缩到十几秒。第一天我只做了三件事确认需求、技术选型、搭环境。需求是从同事那边一点点问出来的选型上也很保守直接用了Python 3.11配合pandas和openpyxl没有引入任何重型框架。第一天晚上我以为环境全都装好了第二天打开电脑才发现事情远没这么简单。这篇日志的价值恰恰就在于第二天是整个项目从规划走向落地的第一个分水岭第一天的计划再漂亮第二天一动手就会被真实世界的丑陋细节击碎。1.2 第二天给自己定的三个目标给第二天定目标时我特意没有写完成全部功能这种大话。无法度量的目标不仅没有指导意义还会制造焦虑。我最终定了三个可以逐项检查的小目标修复环境兼容性问题让代码可以从头到尾完整跑起来实现最基本的合并加去重功能产出一个最小可用版本MVP拿一份真实数据跑通流程并把输出结果和手工处理的结果做交叉验证这三个目标有一个共同点全部指向让流水线先转起来。我不允许自己在第二天去优化性能、补充完整测试或者设计什么好看的界面这些都排在后面。MVP的逻辑很简单哪怕最终交付物需要很多功能当前阶段最重要的事情是保证已经有的一部分功能真正能用而不是对着想象中的完整方案空转。1.3 写日志本身就是第二天的一件正事我还给自己下了一个硬性要求第二天结束时必须写满一页日志里面要记录发生了什么、每个环节花了多长时间、遇到的具体报错和排查线索。这个要求不是养成好习惯之类的虚话而是非常实际的原因——第三天回看时如果没有当天记下来的排查路径你可能会重新花一个小时去定位同一个问题。我见过太多人第三天放弃并不是能力不够而是第二天踩过的坑没有留下任何文字第三天重蹈覆辙之后心态崩了。日志在这里起到的作用就是给第二天的混乱做一个快照让第三天不用从零开始回忆。2. 上午三小时环境问题才是第一只拦路虎2.1 第一天埋下的雷解释器路径和包安装位置不一致第二天早上我做的第一件事不是写功能代码而是打开终端把第一天写的几行测试代码重新跑一遍。结果报错信息非常直接ModuleNotFoundError: No module named pandas。这就让人有点摸不着头脑了因为第一天我明明用pip装过。后来查了python和pip各自指向的位置才发现这台电脑上装了不止一个Python系统自带一个3.8版本第一天我在终端里用它执行了pip install包都装进了3.8对应的目录而我在VS Code里选择的解释器却是另一个路径下的3.11。两边各跑各的自然是什么都调不通。这个坑在Python开发里太典型了。一旦电脑上有多个Python环境比如系统默认的、Anaconda的、pyenv管理的、IDE自己带的依赖装到A环境却在B环境运行的情况几乎必然会发生。排查时核心动作只有两个在终端分别执行which python和which pip确认当前使用的是哪个路径再看看VS Code右下角显示的解释器是不是同一个。只要把这两条路径对齐一大半问题就消失了。2.2 我的解决方案conda加独立虚拟环境我最终没有继续在系统Python上折腾而是新建了一个conda虚拟环境名字叫excel_tool之后所有脚本都让这个环境的解释器来跑。选conda而不是Python自带的venv主要原因是后面要用到的pandas、openpyxl、xlrd这些包很多都携带本地编译的二进制文件。conda在管理这一类带二进制的依赖上要比原生的pip省心太多尤其是Windows环境下经常会冒出来的编译报错conda基本可以绕开。具体命令没什么稀奇就是四行conda create -n excel_tool python3.11 conda activate excel_tool pip install pandas openpyxl xlrd装完依赖之后重点其实是最后一步在VS Code里手动把Python解释器切到excel_tool对应的路径。这一步我在第一天忽略了因为那时候图省事直接用默认解释器结果第二天全部翻车。切换完之后我把第一天的测试代码重新跑了一遍确认没有缺包、没有路径问题这才敢正式进入功能开发。注意不要迷信第一天装好的环境。所有环境相关的东西第二天重新冷启动一遍才是真正的验证。第一天的装好了很可能只是在交互式终端里能import并不等于代码可以通过脚本正常运行。2.3 中文路径和编码问题环境问题刚消停我又碰上了中文乱码。同事发来的原始Excel文件放在一个带中文名的文件夹里用pandas去读取的时候控制台直接抛了编码异常。排查下来的根因是当前系统语言环境下默认编码和文件实际编码不一致。这种情况的标准处理方式是在读取文件时显式指定编码类型同时在脚本启动入口重新配置标准输出编码避免在print中文日志时再次触发UnicodeEncodeError。这一小节值得单独强调是因为处理办公类数据时中文路径、中文列名、中文文件名都是完全正常的日常状态千万不要习惯性地假设所有环境都默认UTF-8。脱离真实场景写代码第二天基本都会在真实数据上翻车。3. 下午四个小时把最小可用版本真正跑通3.1 核心功能的三步拆解环境理顺之后下午的重头戏是写核心逻辑。我把功能拆成了三段每一段都很粗糙但有一个硬性要求每一段都能单独用一小份样本数据运行不需要等整个程序写完才能看到结果。这个习惯也是我从之前的项目里学到的教训——如果你连续写两天代码才跑一次程序那这两天的产出大多数是盲写盲写出来的东西大概率要推翻一半。这三段功能分别是第一遍历指定文件夹里的所有xlsx文件读取每个工作表统一列名后纵向拼接第二按指定字段对拼接后的总表去重保留最新的一行第三对日期列做格式化统一输出成yyyy-mm-dd格式同时把空缺单元格标记出来。每一步的验证方式都非常简单拿三五个文件跑一下人工扫一眼输出立刻就知道对不对。3.2 表头映射同事的命名自由才是最现实的挑战第一个真正卡住我的问题来自不同分表的表头不一致。有的表叫客户名称有的叫客户名还有的竟然叫客户/单位名称。没有遇到这种真实数据之前我很难想象一个内部工具面临的难点不是算法而是同事们各自的命名习惯。处理方式简单直接在代码里维护一张列名映射表把各种叫法映射到统一的标准字段上。映射表的所有内容都来自和同事的沟通不是我拍脑袋定的。为什么强调这一点因为工具面向的是真实使用者技术上的最优解远不如贴合业务流程重要。你花三个小时做一个自动识别各种列名的语义算法不如花十分钟问清楚同事们各自习惯用什么名称。我在这一小节得到的经验是能用配置表解决的灵活性问题就不要动用复杂的代码逻辑。配置表是透明的任何人打开都能看懂而一段花哨的规则代码调试起来只会更头疼。3.3 日期解析的边界情况日期字段是当天另一个耗时点。合并后的数据里同一个日期列同时出现了2024/1/5、20240105和05-01-2024三种写法。pandas的to_datetime虽然能自动识别一部分格式但遇到05-01-2024这种日/月/年顺序不明确的写法时解析结果很可能出现方向性错误把5月1日变成1月5日。我采用的方案是先用正则把规则类的写法统一替换成ISO格式再交给to_datetime转换仍然无法解析的值标记成待人工确认而不是直接让程序崩溃。数据处理脚本有一条很核心的原则对异常数据要有兜底不能遇到一条脏数据就整段崩溃。面向真实办公场景的工具数据脏是常态程序对脏数据的容忍度决定了这个工具能不能被实际使用。3.4 回归验证手工结果对账功能跑通之后我没有因为它能跑了就收工。我拿了一份真实的小样本数据手工做了合并和去重得到一份预期结果表再让脚本跑同一份数据把两张表逐行逐字段做对比。抽验的意义在于确认程序的正确性不是形式上的而是业务意义上的。一个程序输出出来的表格格式上完全合法但去重规则理解错了结果就是错的只是错得非常工整。手工对账的过程很枯燥但它是MVP能够交付的底线没有对账过的代码默认不可信。4. 第二天的时间账与记录方法4.1 当天的时间流水我把当天的时间流水贴在这里不是为了展示自己有多努力而是想说明一个现实第二天真正花在写核心代码上的时间只有三个小时左右剩下的大块时间全消耗在环境和边界情况上。具体流水是这样的09:30-09:50 回顾第一天代码整理项目目录结构09:50-10:40 排查环境问题切换conda虚拟环境10:40-10:50 休息10:50-11:40 实现文件遍历与基础合并逻辑14:00-15:20 处理表头映射与日期解析边界情况15:20-16:00 用样本数据做手工交叉验证16:00-16:40 提交代码并撰写当天日志如果这一天没有记时间账我到了晚上对第二天的印象只会是写了一整天代码。但实际上真正写代码的时间连一半都不到。这种时间记录对后面的工期预估特别有用第三天的计划里我很自然地知道要给环境问题预留30分钟buffer给数据异常情况预留一小时buffer而不是天真地认为所有时间都能花在功能开发上。4.2 日志模板第二天记录什么才有用把日志写成流水账当然最容易但最没用。我用的模板只包含四类信息做了什么、花了多久、遇到什么问题、怎么解决的。其中怎么解决的最容易偷懒不写但这恰恰是最重要的部分。我会强迫自己把排查路径记录下来——先试了什么报了什么错然后从哪里找到了线索最终用了什么方案。这样写的最大好处是第三天如果再碰到类似问题我只需要搜一下日志文本就能复用整套排查路径不需要每次重新走一遍。我的日志模板拆开来看是这样的项目名和日期用来定位是哪一天的记录目标列表写当天开工前定下了哪几个可以检查的目标时间流水记录每个时段的实际行为和耗时遇到的问题和解决过程这是整个日志的核心明天的第一条优先任务给第三天的自己留一句明确的指令4.3 精力分配的逻辑时间流水的另一个作用是帮助我观察自己的精力曲线。我可以明显看到上午状态最好的时段都用来做紧锣密鼓的排查性工作了下午精力相对平稳的时候就做规则编码晚上不做任何动脑任务只做收尾提交和写日志。这个安排是刻意的环境问题需要高精力状态下才能保持冷静排查不然很容易越搞越乱而写常规逻辑时就算状态一般也可以通过查阅文档来推进工作。把认知负荷最高的任务放在精力最好的时段是独立开发里性价比最高的一个安排。5. 第二天最容易踩的五个坑5.1 环境依赖装错位置这个坑我上午已经踩过了。解决的关键在于统一解释器路径和pip路径。最简单的办法就是使用独立虚拟环境不要把任何依赖直接装进系统Python。折中来说哪怕环境再乱只要强制自己每次都在同一个环境里安装和运行问题就不会产生。症状常见原因解决思路import模块报No module named依赖装进了另一个环境确认解释器路径和pip路径一致pip install出现编译错误包需要本地编译环境改用conda安装或换预编译wheel中文路径或文件名乱码默认编码与文件实际编码不一致读取时显式指定编码输出前统一转码5.2 只调代码不提交版本第二天下午我给自己立了一条铁律每跑通一个功能点就提交一次git。不能等全部写完再统筹提交。我见过太多人写了七天代码才想起来用版本控制然后对着已经被自己改得面目全非的文件发呆。第二天的代码量不大提交成本几乎为零但收益会随着时间累积。每次提交信息保持简洁一句话说清楚这次做了什么改动几天之后回看时整个项目的演化轨迹会非常清晰。5.3 完美主义导致的迟迟不动手第二天的完美主义坑杀的不是代码质量是交付节奏。很多人第二天脑子里还在纠结要不要换一个更优雅的架构、要不要把界面做得漂亮一点、要不要先设计一套完整的测试用例。我处理这个问题的方法很直接把这些念头都压到MVP跑通之后再讨论。第二天的唯一目标是有东西能跑并且这个能跑是经过验证的。界面的美观、架构的优雅、测试的完整全部排在可用性之后。5.4 过于信任自己写的程序这里要单独强调一条自己写的程序在交叉验证之前默认是不可信的。不能因为脚本输出了一张整齐的表就默认这张表是正确的。我见过不少数据处理工具会变成错误报表的批量生成器根本原因就是从来没有拿输出和手工结果做过对账。哪怕只是抽几行样例数据人工核对一下也比完全不核对强得多。对账这个动作看似笨拙却是防止工具在错误方向上一路狂奔的唯一防线。5.5 没有给当天留下明确的退出动作第二天结束时我给自己的收尾动作固定成了一个模板提交代码、更新日志、写下一句明天优先要做的一件事。这句话不要小看。第三天早上打开电脑时面对的不再是一堆记忆模糊的代码而是一个清晰可执行的指令。很多项目的死亡就差在第二天结束的时候没有留下任何指引给第三天。有了这个指引哪怕第三天状态很差你至少知道该从哪里开始。结语写到这里这份day2日志就算完整了。我对第二天最大的体会是这一天不是用来证明自己会写代码的而是用来证明自己知道问题在哪里并且能把问题清清楚楚地记录下来。环境坑、数据坑、时间黑洞这些严格来说都不是能力问题而是管理问题。一份有结构的日志能把第二天的混乱有序化第三天的推进就会顺畅很多。这个第二天记录的习惯我在好几轮独立项目里一直在用每次都有效。如果你此刻也正卡在某个项目的第二天我建议你顺手写下一份自己的日志——不用很长但把问题和路径记下来第三天它会回报你的。
返回列表