用python解放右手(五) 定时任务-让代码比你先上班
定时任务:让代码比你先上班
本文基于 Python 3.9+,涉及库:
schedule、APScheduler。阅读时间约 10 分钟。安装依赖:
pip install schedule apscheduler
阿明的"早间仪式"
每天早上 8:30,阿明到公司后的第一件事,是打开三个系统、导出昨日数据、跑几个查询、把结果截图发群里。
等搞完这些,已经 9:30 了。咖啡凉了,正经活儿还没开始干。
周三早上,阿明又在做这套"早间仪式",老张端着咖啡路过,瞥了一眼:“你这套操作,每天固定做?”
“对啊,雷打不动。”
“几点开始做?”
“8:30 到公司,9:30 左右做完。”
老张摇头:“你让代码 8 点就跑,你到公司直接看结果不香吗?”
阿明愣住:“……还能这样?”
"这叫定时任务。"老张拉过椅子坐下,“让代码比你先上班。”
第一步:认识schedule——简单定时,像设闹钟
“阿明,你手机设过闹钟吧?”
“当然。”
“schedule库就是给 Python 设闹钟的——简单、直观,适合本机跑的小脚本。”
pipinstallschedule最简示例:每天说早安
importscheduleimporttimedefjob():print("⏰ 该干活了!")# 每天 9:00 执行schedule.every().day.at("09:00").do(job)# 保持运行,检查是否有任务到期whileTrue:schedule.run_pending()time.sleep(1)# 每秒检查一次“看到没?schedule.every().day.at("09:00").do(job)就是’每天早上 9 点,执行 job 函数’。”
“while True循环是让脚本一直跑着,每秒检查一下’到点了吗?到点了吗?'”
阿明点头:“确实像闹钟……但这脚本要一直开着?”
“对,schedule适合本机长期运行的脚本。如果你想更专业的调度,后面讲APScheduler。”
第二步:各种定时模式
“schedule支持的定时模式很多,我给你列几个常用的:”
importscheduleimporttimedefjob():print("执行任务...")# 每 10 分钟执行一次schedule.every(10).minutes.do(job)# 每小时执行一次schedule.every().hour.do(job)# 每天 9:30 执行schedule.every().day.at("09:30").do(job)# 每周一 9:00 执行schedule.every().monday.at("09:00").do(job)# 每周五 17:00 执行(发周报!)schedule.every().friday.at("17:00").do(job)# 每 2 小时的第 15 分钟执行schedule.every(2).hours.at(":15").do(job)whileTrue:schedule.run_pending()time.sleep(1)阿明眼睛亮了:“每周五 17:00 发周报!这不就是我上周学的邮件自动化吗?”
“对,把它们串起来就行。”
第三步:实战——阿明的"早间仪式"自动化
“来,把你每天早上那套操作,用定时任务串起来。”
importscheduleimporttimefromdatetimeimportdatetime# 假设这些是你之前写的函数frommy_scriptsimportexport_data,generate_report,send_summary_emaildefmorning_routine():"""早间仪式:每天 8:00 自动执行"""print(f"\n🌅 早间仪式开始!时间:{datetime.now()}")# 1. 导出昨日数据print("📊 正在导出数据...")export_data()# 2. 生成报表print("📈 正在生成报表...")generate_report()# 3. 发送邮件给团队print("📧 正在发送邮件...")send_summary_email()print(f"✅ 早间仪式完成!时间:{datetime.now()}\n")# 每天早上 8:00 执行schedule.every().day.at("08:00").do(morning_routine)print("🤖 定时任务已启动,等待执行...")whileTrue:schedule.run_pending()time.sleep(60)# 每分钟检查一次就够了“这段代码干了啥?”
“每天早上 8 点,自动执行三个步骤:导出数据 → 生成报表 → 发邮件。你 8:30 到公司,打开邮箱,结果已经在等你了。”
“time.sleep(60)改成每分钟检查一次,不用每秒那么频繁。”
阿明感叹:“这比我手动操作快多了,还不会漏步骤……”
第四步:APScheduler——高级调度,像请了个管家
"schedule够简单,但功能有限。如果你需要更复杂的调度,比如:
- 同时管理多个任务
- 任务执行历史记录
- 错过时间后补执行
- 分布式部署
“那就得请APScheduler出场了——它像个专业管家,功能全面,但配置稍复杂。”
pipinstallapscheduler最简示例
fromapscheduler.schedulers.blockingimportBlockingSchedulerfromdatetimeimportdatetimedefjob():print(f"⏰ 任务执行:{datetime.now()}")# 创建调度器scheduler=BlockingScheduler()# 添加任务:每 5 秒执行一次scheduler.add_job(job,"interval",seconds=5)# 添加任务:每天 9:00 执行scheduler.add_job(job,"cron",hour=9,minute=0)# 启动print("🤖 调度器已启动...")scheduler.start()“BlockingScheduler会阻塞主线程,适合独立运行的脚本。”
三种触发器
“APScheduler有三种触发方式,对应不同场景:”
fromapscheduler.schedulers.backgroundimportBackgroundSchedulerimporttime scheduler=BackgroundScheduler()# ========== 1. date:特定时间执行一次 ==========# 2024-02-01 10:00:00 执行一次scheduler.add_job(my_job,"date",run_date="2024-02-01 10:00:00")# ========== 2. interval:间隔执行 ==========# 每 30 分钟执行一次scheduler.add_job(my_job,"interval",minutes=30,start_date="2024-01-20 09:00:00",end_date="2024-01-20 18:00:00")# ========== 3. cron:像 Linux 的 crontab ==========# 每周一到周五的 9:00 和 18:00 执行scheduler.add_job(my_job,"cron",day_of_week="mon-fri",hour="9,18",minute=0)scheduler.start()# 主程序继续干别的whileTrue:time.sleep(1)"cron触发器最强大,语法和 Linux 的 crontab 一样:
day_of_week="mon-fri":周一到周五hour="9,18":9 点和 18 点minute=0:整点
“你想多复杂的时间规则,都能配出来。”
第五步:系统级定时——让脚本随系统启动
“阿明,schedule和APScheduler都需要 Python 脚本一直跑着。如果电脑重启了,脚本就停了。”
“那怎么办?”
“用系统级的定时任务,操作系统帮你盯着。”
Windows:任务计划程序
"Windows 自带的’任务计划程序’,可以设置:
- 每天 8:00 执行某个 Python 脚本
- 开机自动启动
- 即使没登录也能跑
"配置步骤:
- 搜索打开【任务计划程序】
- 创建基本任务 → 填名称
- 触发器:每天 8:00
- 操作:启动程序
- 程序:
python.exe的路径(比如C:\Python311\python.exe) - 参数:你的脚本路径(比如
D:\scripts\morning_routine.py)
“这样就算重启电脑,到点也会自动执行。”
Linux/Mac:crontab
“Linux 和 Mac 用crontab,更简单:”
# 编辑定时任务crontab-e# 添加一行:每天 8:00 执行脚本08* * * /usr/bin/python3 /home/aming/morning_routine.py>>/home/aming/cron.log2>&1“0 8 * * *就是’每天 8 点 0 分’。后面的>> cron.log是把输出记录到日志文件。”
第六步:日志与监控——让定时任务"可追溯"
“定时任务最大的问题是——它默默跑着,出错了你都不知道。”
“所以必须加日志和异常处理。”
importscheduleimporttimeimportloggingfromdatetimeimportdatetime# 配置日志logging.basicConfig(filename="task.log",# 日志文件level=logging.INFO,# 记录 INFO 级别及以上format="%(asctime)s - %(levelname)s - %(message)s")defmorning_routine():try:logging.info("🌅 早间仪式开始")# 模拟任务logging.info("📊 导出数据...")# export_data()logging.info("📈 生成报表...")# generate_report()logging.info("📧 发送邮件...")# send_summary_email()logging.info("✅ 早间仪式完成")exceptExceptionase:logging.error(f"❌ 任务执行失败:{e}")# 出错时发告警(下篇教)# send_alert(f"早间仪式失败:{e}")schedule.every().day.at("08:00").do(morning_routine)logging.info("🤖 定时任务已启动")whileTrue:schedule.run_pending()time.sleep(60)“logging是 Python 内置的日志模块,不用额外安装。”
“日志文件长这样:”
2024-01-22 08:00:00 - INFO - 🌅 早间仪式开始 2024-01-22 08:00:02 - INFO - 📊 导出数据... 2024-01-22 08:00:05 - INFO - 📈 生成报表... 2024-01-22 08:00:08 - INFO - 📧 发送邮件... 2024-01-22 08:00:10 - INFO - ✅ 早间仪式完成“出问题了,打开task.log一看就知道哪步挂了。”
踩坑提醒:定时任务的坑
老张喝了口咖啡,表情认真:
坑 1:任务执行时间重叠
“如果你的任务要跑 5 分钟,但你设了每 3 分钟执行一次,就会重叠执行——两个同样的任务同时在跑,可能互相冲突。”
"解决办法:
- 拉长间隔时间
- 加锁机制(文件锁、数据库锁)
- 用
APScheduler的max_instances=1限制同时只能跑一个实例"
坑 2:时区问题
“服务器可能在美国,你在中国,时区不一致导致任务跑错时间。”
“解决办法:”
fromapscheduler.schedulers.backgroundimportBackgroundSchedulerfrompytzimporttimezone scheduler=BackgroundScheduler(timezone=timezone("Asia/Shanghai"))坑 3:环境变量缺失
“系统级定时任务(如 crontab)运行时,环境变量和用户登录时不一样,可能导致 Python 找不到库。”
“解决办法:在脚本开头指定 Python 路径,或者用虚拟环境的完整路径。”
坑 4:任务跑完进程退出
“schedule需要while True循环保持运行。如果你用系统定时任务直接调用脚本,脚本跑完就退出了,不会持续调度。”
"解决办法:
- 系统定时任务直接调用脚本(一次性的)
- 或者用一个常驻进程跑
schedule/APScheduler"
一句话总结
阿明把定时任务脚本部署到了公司服务器上,设置了每天早上 8:00 自动执行。
第二天早上 8:30,他到公司,打开邮箱——报表已经躺在收件箱里了。群里也有机器人发的昨日数据摘要。
阿明靠在椅背上,喝了口热咖啡。
老张路过,笑了:“感觉怎么样?”
阿明竖起大拇指:“代码比我先上班,我到了直接看结果。”
老张留下一句话:
“定时任务就像你家的扫地机器人——你设定好’每天下午 3 点扫’,它到点就干活,你回家地面就是干净的。你要做的只是偶尔倒个尘盒(看看日志)。”
扩展思考
"定时任务的应用场景远不止早间仪式:
- 数据库备份:每天凌晨 3 点自动备份
- 日志清理:每周清理一次过期日志,防止磁盘爆满
- 数据同步:每小时从 A 系统拉数据,写到 B 系统
- 定时爬虫:每天抓取竞品价格,存到 Excel
- 健康检查:每 5 分钟检查网站是否挂掉,挂了发告警
“核心逻辑都一样:定时触发 → 执行任务 → 记录日志 → 异常告警。”
下集预告
下一篇,阿明被财务部求助——100 份合同扫描件要合并成一个 PDF,还要加水印。老张教他PyPDF2和pdfplumber,PDF 操作不再求人。
记住:定时任务的关键不是’能定时’,而是’出错可追溯’。日志和异常处理,比定时本身更重要。
你们有什么工作是每天/每周固定时间要做的?如果能自动化,你想先自动化哪个?欢迎在评论区聊聊。
