ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Python自动化脚本:日报汇总、日志巡检与文件整理实战

Python自动化脚本:日报汇总、日志巡检与文件整理实战 一天只有24小时把重复劳动压下去就等于给自己加薪。这套我用 Python 写的自动化脚本做法很朴素把日报汇总、日志巡检、文件归档这三件最占时间的琐事交给代码去跑每天差不多能省出3小时。事情本身并不高深难的是你愿不愿意先花一个下午把逻辑理清楚。这篇内容适合谁一类是每天跟 Excel、日志、散乱文件打交道的运营和开发另一类是刚学 Python 但不知道能拿它干嘛的入门者。三个脚本都给出了完整代码和解释你可以直接复制改路径用也可以只读思路然后顺手把你的某个重复劳动也脚本化。文中还包含我实际踩过的坑这些坑在教程里基本不会写。1. 先聊整体思路为什么是这3个脚本1.1 选脚本的判断标准动手写自动化之前先别急着打开编辑器。我给自己定过三条判断标准同时满足才会写脚本第一这件事每周至少重复两次以上。只发生一次的事手动做掉反而更快。第二流程是固定的输入和输出都可预期。比如“读取表格—生成汇总—发邮件”每一步都不会突然变成“先开个会讨论需求”。第三机器能明确判断对错不需要人来临时做价值判断。比如“今天的异常数超过10条就告警”这个规则是明确的。日报汇总、日志巡检、文件整理正好卡在这三个标准上。它们不涉及复杂的业务判断只是量大、费时、出错率高。人做这些事眼睛很快就会疲劳一旦漏掉一行数据或者看错一个 ERROR后面还要花更多时间返工脚本做这些事稳定性反而更高。1.2 技术方案选型为什么用 Python这三个脚本语言选择其实不少Shell 也能做但我最终全用 Python原因有几个。一是生态成熟。读 Excel 有 pandas发邮件有标准库 smtplib算文件哈希有 hashlib几乎不需要自己造轮子。二是语法直观出了 Bug 也好排查。写 Shell 的字符串处理稍微复杂一点就全是转义符和管道符我看着头疼。三是跨平台。同一个脚本稍微改改路径在家里的 Windows 上能跑在公司 Linux 服务器上也能跑。库的选型我也说一下取舍。读 Excel 我会优先用 pandas 的read_excel因为它自动处理表头和数据类型遇到空单元格也不会直接崩。如果你环境里装不了 pandas退一步用openpyxl也能读但代码会啰嗦很多。发邮件则直接用smtplib这是标准库干净利落没必要为了发封邮件引入一整个邮件框架。日志解析优先用正则表达式加Counter因为你需要的不是全文检索而是“统计、分组、命中关键词”这三件事。文件操作优先用pathlib它比老的os.path更直观路径拼接、目录遍历都顺手。1.3 设计原则可配置、可重跑、不销毁数据写这几类脚本最忌讳的是把代码写死。发送人、接收人、文件夹路径、告警阈值这些都应该放在脚本顶部常量区或者单独一个配置文件里。后面要改也只是改一行不用翻遍整段代码去替换字符串。另一个原则是可重跑。同一个脚本运行两次结果应该是一致的至少不能把数据弄脏。比如日报脚本如果今天已经生成过报告重复运行不应该生成两份内容文件整理脚本如果目标位置已经有同名文件要自动改名而不是直接覆盖。这个原则直接决定了脚本能不能长期用。最后是不销毁数据。自动化的目的是节省时间不是制造风险。我的文件整理脚本里哪怕发现了重复文件也只是把重复副本移到一个专门的文件夹并不会直接删除这样万一误判还有后悔药吃。2. 脚本一日报自动汇总与邮件推送2.1 这个脚本解决的是什么我见过太多人每天上班第一件事就是打开几个渠道后台把订单量、成交金额从表格里复制出来再填进公司日报模板最后复制到邮件或者企业微信发出去。这个流程看着不复杂但一天天重复做非常消耗耐心。更麻烦的是手动复制粘贴容易串行明明该填A渠道的数字一不留神填成了B渠道。所以我决定把这套流程自动化。这个脚本做什么呢自动读取一个目录下所有 Excel 文件把它们按照统一规则汇总成一张总表然后算总订单数和总成交金额生成一段 HTML 格式的日报正文最后通过 SMTP 发送到指定的邮箱列表。整个过程一条命令完成耗时不超过10秒而手动操作至少20分钟。2.2 完整代码与执行流程先直接看代码。这是一个最小可运行的版本我把功能分成三个函数加载数据、生成报表内容、发送邮件。import smtplib import pandas as pd from email.mime.text import MIMEText from email.header import Header from pathlib import Path DATA_DIR Path(rD:\daily_data) # 放各渠道导出的 Excel SENDER botexample.com PASSWORD 你的SMTP授权码 # SMTP 授权码不是登录密码 RECEIVERS [bossexample.com, meexample.com] SMTP_SERVER smtp.example.com SMTP_PORT 465 def load_today_data(): dfs [] for f in DATA_DIR.glob(*.xlsx): df pd.read_excel(f) dfs.append(df) if not dfs: return pd.DataFrame() return pd.concat(dfs, ignore_indexTrue) def build_html(df): if df.empty: return p今日暂无数据/p total_orders int(df[订单数].sum()) total_amount float(df[金额].sum()) html fh3今日汇总/h3p订单数: {total_orders}/pp成交金额: {total_amount}/p return html def send_mail(content): msg MIMEText(content, html, utf-8) msg[Subject] Header(每日日报, utf-8) msg[From] SENDER msg[To] , .join(RECEIVERS) with smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT) as server: server.login(SENDER, PASSWORD) server.sendmail(SENDER, RECEIVERS, msg.as_string()) if __name__ __main__: df load_today_data() html build_html(df) send_mail(html)执行流程是这样的第一步load_today_data遍历DATA_DIR目录把所有.xlsx文件读进来合并成一个 DataFrame。这里假设每个 Excel 文件的列名是一致的都有“订单数”和“金额”这两列。如果你的文件格式不统一可以先在外面用 Excel 把模板统一一下或者在这个函数里做列名映射。第二步build_html把汇总数字拼成一段 HTML这样邮件正文里可以显示出简单的标题和加粗效果。第三步send_mail走 SMTP 把邮件发出去。2.3 关键细节与注意点这段代码最容易被忽略的是 SMTP 授权码。很多邮箱必须开启 SMTP 服务并且用独立的授权码登录而不是直接用邮箱登录密码。我一开始没注意这个一直用密码登录结果每次都是Authentication failed。这个问题卡了很久才发现所以提醒你先去邮箱设置里把 SMTP 服务打开。如果邮件内容是纯文字可以把MIMEText的第二个参数从html改成plain。但既然日报里通常有点结构用 HTML 会更舒服。如果你的汇总逻辑更复杂比如不同渠道有不同的指标、需要按渠道分组展示那只要把build_html改成遍历df.groupby(渠道)生成一段表格就行。这属于扩展需求核心骨架不需要大改。还有一点发送完邮件之后建议在脚本里把当天已经发送过的标记记下来最简单的方式是检查当天的数据文件是否已经处理过。否则哪天不小心重复运行了这个脚本可能会发给同事两封一模一样的邮件。3. 脚本二日志巡检与异常告警3.1 巡检脚本的价值第二个脚本解决的是“守着日志看异常”的问题。以前我维护一个后端服务的时候每天早上到公司的第一件事就是连上服务器翻昨天的日志文件搜索ERROR、Timeout、login failed这些关键词数一数异常次数。日志文件动辄几百兆关键词又多用编辑器打开经常卡半天。更难受的是人眼搜日志容易漏。日志是滚动写入的上午那波异常可能已经被后来大量的正常日志冲走了不仔细看根本发现不了。这个脚本的价值就是半夜或者清晨自动把日志扫一遍统计出各种异常关键词的命中次数再按小时维度看异常集中出现在哪个时段如果异常总量超过阈值就立刻发邮件告警。3.2 核心实现正则匹配统计阈值告警直接看核心代码。整个脚本分两块解析日志以及按需发告警。import re from collections import Counter from pathlib import Path import smtplib from email.mime.text import MIMEText LOG_FILE Path(r/var/log/app.log) # 换成你的日志路径 KEYWORDS [ERROR, Timeout, login failed] THRESHOLD 20 # 异常行数超过 20 才告警 MAIL_CONFIG { sender: alertexample.com, password: 授权码, receivers: [opsexample.com], smtp_host: smtp.example.com, smtp_port: 465, } def parse_log(path): errors Counter() hourly Counter() with open(path, r, encodingutf-8, errorsignore) as f: for line in f: for kw in KEYWORDS: if kw in line: errors[kw] 1 m re.search(r\d{4}-\d{2}-\d{2} \d{2}:, line) if m: hourly[m.group(0)] 1 return errors, hourly def notify(title, content): msg MIMEText(content, plain, utf-8) msg[Subject] title msg[From] MAIL_CONFIG[sender] msg[To] , .join(MAIL_CONFIG[receivers]) with smtplib.SMTP_SSL(MAIL_CONFIG[smtp_host], MAIL_CONFIG[smtp_port]) as server: server.login(MAIL_CONFIG[sender], MAIL_CONFIG[password]) server.sendmail(MAIL_CONFIG[sender], MAIL_CONFIG[receivers], msg.as_string()) if __name__ __main__: errors, hourly parse_log(LOG_FILE) total sum(errors.values()) body f总异常数: {total}\n分布: {dict(errors)}\n if total THRESHOLD: notify(日志异常告警, body f高峰时段: {hourly.most_common(3)}) else: print(body)这段代码最核心的是parse_log里的这个正则\d{4}-\d{2}-\d{2} \d{2}:。它匹配的是“2024-01-15 08:”这种日志前缀用来把日志行归类到小时维度找出异常到底集中在哪个时段。比如匹配结果是2024-01-15 08:那这行日志就计入早上8点这个时段。用hourly.most_common(3)可以快速知道三个异常最密集的小时这个信息在定位问题的时候非常有用。errors是关键词命中的分布情况比如“ERROR”出现了多少次、“Timeout”出现了多少次。这样你能知道当天的异常是系统错误为主还是超时为主而不是只看到一个总数字。3.3 阈值计算与告警降噪发告警大家都会但发多了就变成噪声最后谁都不看。这里有一个简单的阈值计算思路先手动观察一周的异常数量取一个正常波动范围把阈值设在这个范围之上。比如这周每天的 ERROR 一般在5到15条之间那阈值设为20就合适。更讲究一点可以做“连续告警抑制”。意思是如果某个错误关键词连续几天出现而且数量基本持平那就没必要天天发邮件只发一次就行。最简单的实现方式是在脚本旁边放一个状态文件记录上一次告警的关键词和数量本次如果关键词一致且数量没有明显变化就不重复骚扰自己。当然如果异常数突然翻倍那一定要告警。实际使用中还有一个细节日志文件在滚动写入时可能同时有多个进程在写直接打开读取一般没问题但用errorsignore很关键。因为日志文件大概率会有非 UTF-8 的乱码行遇到编码错误直接跳过总比整个脚本崩溃要好。需要注意关键词匹配越精确误报越少。我后来把ERROR改成了 - ERROR 加了个空格减少命中ERROR_CODE_XXX这种无关内容的概率。4. 脚本三文件批量整理与去重4.1 定时整理下载目录第三个脚本的场景不是服务器而是你自己的电脑。不知道你有没有这种经历下载目录、桌面、微信接收文件目录三个月不整理就会堆满几百个文件名千奇百怪的文档、图片、压缩包。想找某个文件的时候系统自带的搜索又慢又不准只能一个个翻。我自己的下载目录曾经乱到我自己都不愿意打开。后来我写了这个整理脚本挂在计划任务里每周跑一次效果很好。它会按文件后缀把文件分到images、docs、videos、archives、others这几个子目录里同时用 MD5 找出重复文件把重复副本移到专门文件夹不做物理删除。这个脚本运行完下载目录瞬间清爽。而且因为只是移动文件即使分类不合理也很容易再拖回去没有破坏性风险。4.2 去重原理 MD5 及脚本代码去重的原理很简单计算每个文件的 MD5 哈希值哈希值相同的文件内容就是完全一样的。MD5 虽然理论上存在碰撞但对于普通的文档、图片、压缩包去重场景碰撞概率可以忽略不计。import hashlib import shutil from pathlib import Path SOURCE Path.home() / Downloads # 要整理的目录 DEST Path.home() / AutoSort # 整理后的根目录 DUPLICATE_DIR DEST / _重复文件 TYPE_MAP { images: [.jpg, .jpeg, .png, .gif, .webp], docs: [.pdf, .docx, .xlsx, .pptx, .txt, .md], videos: [.mp4, .mov, .avi], archives: [.zip, .rar, .7z, .tar, .gz], } def file_md5(path, chunk_size8192): h hashlib.md5() with open(path, rb) as f: while chunk : f.read(chunk_size): h.update(chunk) return h.hexdigest() def main(): DEST.mkdir(parentsTrue, exist_okTrue) DUPLICATE_DIR.mkdir(parentsTrue, exist_okTrue) seen {} for item in SOURCE.iterdir(): if item.is_dir(): continue ext item.suffix.lower() target_dir None for t, exts in TYPE_MAP.items(): if ext in exts: target_dir DEST / t break if target_dir is None: target_dir DEST / others target_dir.mkdir(exist_okTrue) md5 file_md5(item) if md5 in seen: shutil.move(str(item), DUPLICATE_DIR / item.name) print(f重复移动: {item.name}) else: seen[md5] item dest target_dir / item.name if dest.exists(): dest target_dir / f{item.stem}_{md5[:6]}{ext} shutil.move(str(item), dest) print(f归档: {item.name} - {target_dir.name}/) if __name__ __main__: main()关键点有四个。iterdir()可以列出目录下所有文件和子目录比os.listdir更自然。计算 MD5 时如果文件很大一定要分块读取是一次性read()整个文件会占用大内存分块每次读 8KB既能保证性能也能处理任何尺寸的文件。TYPE_MAP这种字典结构很好扩展你想加一类文件只需要在这个字典里加一个列表。dest.exists()的存在性检查很重要避免移动时覆盖同名文件。4.3 安全设计先预览再执行移动不删除这类整理脚本最大的敌人不是代码写不出来而是误操作。我见过有人写文件整理脚本时直接把重复文件os.remove()删了结果删完才发现有个同名但内容不同的文件才是自己要留的版本。所以我的原则是整理脚本只移动不删除。实际操作中第一次在新目录上跑脚本我会加一个previewTrue参数只打印“这个文件将被移动到那个目录”不实际移动。确认无误之后再把preview改成False。这个习惯让我避免了好几次灾难。# 演示 preview 模式的核心逻辑 def main(previewFalse): # ... if preview: print(f[预览] {item.name} - {target_dir.name}/) continue # 真正移动文件如果你希望按时间归档而不是按类型归档也非常好改把TYPE_MAP换成按月归档比如DEST / item.stat().st_mtime.strftime(%Y-%m)文件就能按月放进2024-01、2024-02这样带年月名的文件夹。这个变量方案很实用尤其是视频素材、摄影原片这类按时间找更顺手的文件。5. 环境准备与定时任务配置5.1 Python 环境与依赖安装三个脚本运行起来对 Python 版本没有特别的要求Python 3.8 以上都行。你只需要在本机装好 Python然后安装 pandas 这个第三方库。安装命令很简单pip install pandas如果没装 pandas也可以把脚本一改成只读 CSV 文件用标准库csv模块处理这样连第三方依赖都不需要了。但日常操作没有 pandas 确实不方便建议还是装上。顺便提醒一个很多人遇到的问题在 Windows 上输python,结果提示无法将“python”项识别为 cmdlet、函数、脚本文件。这通常是因为安装 Python 时没有勾选“Add Python to PATH”。解决办法有两个要么重装一遍 Python在第一步勾选 Add Python to PATH要么手动把 Python 的安装目录和 Scripts 目录加进系统环境变量。这里多说一句安装完之后一定要新开一个终端窗口环境变量才能生效。5.2 Windows 任务计划程序定时跑脚本写好了不能老是手动执行要挂到系统定时任务里。Windows 用户可以直接用“任务计划程序”。步骤是这样的打开任务计划程序点击“创建基本任务”填任务名称比如“每日日报生成”。触发器选择“每天”设置运行时间比如早上 8 点半。操作选择“启动程序”在“程序或脚本”里填 Python 的完整路径比如C:\Python311\python.exe在“添加参数”里填脚本的完整路径比如D:\scripts\daily_report.py。最后完成创建。为什么“程序或脚本”要填完整路径因为计划任务运行的环境和你手动打开终端的环境不一样它未必会加载你自己的 PATH 环境变量。如果只填python任务计划程序可能找不到 Python。同样脚本里的文件路径也建议全部用绝对路径尤其是脚本一里的DATA_DIR如果你填的是相对路径./daily_data计划任务的工作目录可能和你预期的不一样导致读取不到文件。5.3 Linux Crontab 定时跑Linux 上更简单直接使用 crontab。先用crontab -e打开当前用户的定时任务表在文件末尾加一行30 8 * * * /usr/bin/python3 /opt/scripts/log_check.py /tmp/autoscript.log 21这行的含义是每天 8 点 30 分执行一次日志巡检脚本并把标准输出和错误输出都追加到/tmp/autoscript.log。后面这个 log 21非常重要。如果不重定向输出定时任务一旦报错你根本看不到任何日志信息有了这个文件脚本出了什么问题直接cat /tmp/autoscript.log就能排查。Linux 环境还有一个需要注意的点脚本里使用的 Python 解释器必须填绝对路径。你用which python3就能查到完整路径比如/usr/bin/python3。如果你用的是虚拟环境就把路径换成虚拟环境里的 Python因为虚拟环境里的 Python 才能找到你已经装好的 pandas 和第三方依赖。我在这里踩过一次坑crontab 一直报 pandas 导入失败后来才发现它调用的是系统自带的 Python根本没装 pandas。6. 常见问题与排查技巧6.1 常见报错速查我把实际运行过程中最容易遇到的问题整理成了一张表方便你对照排查。现象可能原因解决办法ModuleNotFoundError: No module named pandas当前 Python 环境没装 pandas或用了系统自带的 Pythonpip install pandas用虚拟环境时确认路径正确FileNotFoundError路径写的是相对路径或者目录不存在改用绝对路径运行前先创建目录SMTP 发送失败Authentication failed邮箱没开启 SMTP 服务或密码不是授权码去邮箱设置里开启 SMTP使用独立授权码中文内容在邮件里显示乱码邮件正文或标题编码不对设置MIMEText(content, html, utf-8)标题用Header定时任务没有执行计划任务配置的 Python 路径不对或程序没有被引用到确认python.exe完整路径用日志输出排查看执行记录日志文件读取时乱码、报 UnicodeDecodeError日志文件混有非 UTF-8 编码open时加errorsignore文件整理时重复文件被覆盖同名不同内容移动时未做存在性检查使用dest.exists()判断后重命名6.2 自动化脚本的维护与扩展脚本写出来只是开始维护才是决定它能用多久的关键。我给自己的规则是所有自动化脚本放在同一个目录下目录里维护一个README.md记录这个脚本是干什么的、依赖什么第三方库、定时任务怎么配置、上次改过什么。别高估自己的记忆三个月后你再看自己写的脚本没有文档的话大概率要花半小时重新读代码才能想起来逻辑。脚本的版本管理也值得做哪怕只是用 Git 管理一个本地仓库。改坏了一行代码可以直接回退到上一个可用版本不用靠 CtrlZ 碰运气。这些脚本的扩展方向其实很多。日报脚本可以加上数据分析自动生成柱状图发到邮件日志巡检可以对接企业微信或者钉钉机器人把告警发到群里面文件整理可以顺手对图片做压缩或者添加水印。每多接一个环节每天能省的时间就更多一点。6.3 一点个人体会写这类自动化脚本我最大的体会是“克制比炫技更重要”。刚开始接触自动化的时候很容易陷入一种冲动觉得什么事情都能脚本化。但实际做下来有的流程两个月一变脚本刚写完就要改有的流程本身就没什么稳定的规则硬自动化反而增加维护成本。所以我现在的判断标准很简单这个重复劳动未来三个月内是否依然存在且流程不变如果答案是肯定的就值得花一个下午把脚本写出来。如果答案是否定的那还不如继续手动做把写脚本的时间花在更重要的事上。这套 Python 自动化脚本本质上不是炫技而是把精力还给自己我知道今天的日志正常、日报已发、文件已经归档就可以安心去做那些真正需要人类判断力的事了。
返回列表