ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

用Python实现自动更新日记:定时任务与文件写入实战

用Python实现自动更新日记:定时任务与文件写入实战 遇到“自动更新的死亡日记”这类设定可能很多人第一反应是灵异故事里的桥段一本日记放在桌上第二天翻开发现里面自己多出了几行字写的却是还没发生的事。不过把恐怖元素剥掉这类“自动更新”的能力放在真实开发里其实就是定时任务、文件写入和内容生成这三个技术点的组合。本文就用 Python 还原一个“自动更新日记”的程序再从网约车行程记录这个场景出发聊聊位置数据和定时任务的工程化实现。1. 背景与核心概念1.1 “死亡日记”中的技术底色恐怖故事里最吓人的设定往往不是怪物本身而是那些看起来不合理却又符合生活经验的细节。比如“日记会在第二天自动更新”这个设定之所以让人背脊发凉是因为它违背了普通人对纸制品的认知纸上的字一旦写上去就不会自己变化。可如果换成程序视角自动更新内容再正常不过。一个定时任务可以每天在固定时间把新内容追加到指定文件也可以从一个模板中随机抽取一句话写进去甚至可以按照外部接口返回的数据生成一段“未来预测”。从技术上说写一个这样的程序不需要复杂算法只需要把时间处理、文件操作和调度机制串起来。1.2 自动更新、定时任务与数据持久化“自动更新”在程序里不是一个单一功能而是一组能力的组合。首先是调度能力也就是在指定时间自动触发某个函数其次是数据持久化把生成的内容写入磁盘文件或数据库确保程序重启后数据还在最后是内容生成可以基于时间、随机数、模板或外部数据动态构造文本。对于初学者来说最容易忽略的是调度循环的常驻运行方式。单纯写一个函数并不会自动执行需要有一个 while 循环不断检查当前时间是否到达了预设的触发点这正是 schedule 这类库存在的原因。1.3 应用场景从个人日记到网约车行程记录自动更新日记这个例子虽然带有故事色彩但背后的技术组合在实际项目中非常常见。比如运维场景中的巡检报告每天定时生成金融场景中的交易流水归档每隔一段时间自动落盘再比如网约车平台中的行程记录乘客上车后自动记录起点、终点和每个时间点的位置轨迹本质上也是“定时采集数据 写入存储”的流程。文章标题里提到的“网约车后座的神秘人”放在工程语境下可以理解成一次不被用户感知的行程数据采集行为。弄清楚它的实现原理也就明白了为什么这类功能必须遵循隐私授权和数据最小化原则。2. 环境准备与基础概念2.1 运行环境与 Python 版本本文的示例程序不依赖特定操作系统Windows、macOS、Linux 都可以运行。Python 版本建议使用 3.8 及以上因为后面会用到 f-string 和一些现代类型标注虽然 3.6 也能跑但低版本在新特性兼容上会有麻烦。如果你不确定自己的 Python 版本可以在命令行里先执行python --version如果输出类似Python 3.10.12说明环境满足要求。在 Windows 上如果提示找不到 python可以尝试python3 --version或者检查安装时是否勾选了添加到 PATH。2.2 依赖库与安装命令主程序只需要三个依赖标准库里的datetime做时间处理random生成随机内容第三方库schedule负责定时调度。真正需要手动安装的只有schedule执行下面的命令pip install schedule安装完成后可以用pip show schedule查看版本信息。这里多说一句尽量不要在全局环境里安装太多第三方包推荐用虚拟环境隔离项目依赖可以用python -m venv venv创建虚拟环境再激活后安装依赖。2.3 项目目录设计为了让示例更接近真实项目我们把程序拆成清晰的文件结构diary_app/ ├── main.py # 主程序负责定时任务和日记写入 ├── diary/ # 存放生成的日记文件 └── logs/ # 存放运行日志main.py 放在项目根目录diary 和 logs 两个文件夹可以先创建出来也可以让程序在第一次运行时自动创建。自动创建的好处是部署新环境时少一步手工操作后面我会在代码里演示这个逻辑。3. 核心实现让日记每天“自动更新”3.1 获取当前时间并生成日期标题日记最重要的特征是时间。每一天的日记应该对应一个独立的文件或者一个独立的小节标题。先来看最基础的时间处理from datetime import datetime now datetime.now() date_str now.strftime(%Y-%m-%d) time_str now.strftime(%H:%M:%S) print(date_str, time_str)strftime 是 datetime 对象提供的格式化方法%Y表示四位年份%m表示两位月份%d表示两位日期%H、%M、%S分别对应小时、分钟、秒。运行后输出类似2025-06-01 08:30:00的结果。这个日期字符串既可以用作文件名也可以写进文件内容的第一行。3.2 追加写入与文件自动创建生成内容之后需要写入文件。这里要区分两种写入方式覆盖写入和追加写入。如果使用w模式每次运行都会把原来的内容清空重写如果使用a模式新内容会追加到文件末尾这正符合日记“一天天累积”的语义。from pathlib import Path def append_diary(text: str, directory: Path): directory.mkdir(parentsTrue, exist_okTrue) today datetime.now().strftime(%Y-%m-%d) file_path directory / f{today}.md with open(file_path, a, encodingutf-8) as f: f.write(text \n)注意两个细节第一directory.mkdir(parentsTrue, exist_okTrue)会递归创建目录目录已经存在时也不会报错第二打开文件时指定encodingutf-8避免在 Windows 上因为默认编码不是 UTF-8 导致中文乱码。文件命名为2025-06-01.md每天的日记都落在同一个文件里查看时按时间顺序往下读即可。3.3 定时调度使用 schedule 实现每日更新有了时间处理和文件写入剩下的问题是怎么让程序每天自动执行。schedule 库提供的 API 非常直观import schedule import time def job(): print(执行日记更新任务) schedule.every().day.at(08:00).do(job) while True: schedule.run_pending() time.sleep(1)schedule.every().day.at(08:00)表示每天 08:00 触发一次.do(job)绑定要执行的任务函数。后面的 while 循环是调度器保持运行的关键run_pending()会检查当前时间是否有到期任务time.sleep(1)避免空转占用 CPU。如果你不想让程序常驻也可以把任务触发逻辑放进系统自带的任务计划程序但 schedule 更适合做轻量级本地工具。3.4 模拟“死亡日记”的内容生成为了让程序有点故事感可以增加一个随机内容池每次更新时从里面抽取几句话。这样生成的日记既不是空白的也不是一成不变的看起来就像“日记自己在写内容”。随机内容池可以根据需求自由扩展这里只是演示思路。import random sentence_pool [ 今天似乎有人翻看过这本书。, 窗外下着雨路灯光把影子拉得很长。, 后座的门开着但没有人下车。, 凌晨三点的街道比想象中安静。, ] def build_content(): now datetime.now() lines [ f# {now.strftime(%Y-%m-%d)}, f更新时间{now.strftime(%H:%M:%S)}, , random.choice(sentence_pool), , ] return \n.join(lines)实际项目里可以把 sentence_pool 替换成外部输入比如天气接口、新闻摘要、当日待办甚至是一个语言模型生成的文本。内容生成的逻辑和调度逻辑分离后续扩展会更方便。4. 完整实战自动更新日记程序4.1 创建项目结构与依赖按照 2.3 节的目录规划在本地创建如下文件diary_app/ ├── main.py ├── diary/ └── logs/在项目目录下创建虚拟环境并安装依赖cd diary_app python -m venv venv # Windows venv\Scripts\activate # macOS / Linux source venv/bin/activate pip install schedule如果你的环境没有安装虚拟环境工具也可以直接pip install schedule但建议还是用虚拟环境隔离。4.2 编写主程序下面是一个完整的 main.py把前面几节的内容整合起来。为了演示方便触发时间设置为“每小时执行一次”这样你不需要等一整天就能看到效果想改成每天固定时间把代码中的schedule.every().hour换成schedule.every().day.at(08:00)即可。import logging import random import time from datetime import datetime from pathlib import Path import schedule BASE_DIR Path(__file__).resolve().parent DIARY_DIR BASE_DIR / diary LOG_DIR BASE_DIR / logs logging.basicConfig( levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s, handlers[ logging.FileHandler(LOG_DIR / diary_app.log, encodingutf-8), logging.StreamHandler(), ], ) sentence_pool [ 今天似乎有人翻看过这本书。, 窗外下着雨路灯光把影子拉得很长。, 后座的门开着但没有人下车。, 凌晨三点的街道比想象中安静。, 笔记本的某一页留着陌生的折痕。, ] def build_content() - str: now datetime.now() lines [ f# {now.strftime(%Y-%m-%d)}, f更新时间{now.strftime(%H:%M:%S)}, , random.choice(sentence_pool), , ] return \n.join(lines) def append_diary() - None: DIARY_DIR.mkdir(parentsTrue, exist_okTrue) today datetime.now().strftime(%Y-%m-%d) file_path DIARY_DIR / f{today}.md content build_content() try: with open(file_path, a, encodingutf-8) as f: f.write(content \n) logging.info(f日记已更新{file_path}) except OSError as e: logging.error(f写入日记失败{e}) def main() - None: logging.info(自动更新日记程序已启动) # 每小时触发一次便于快速验证 schedule.every().hour.do(append_diary) # 固定时间触发示例 # schedule.every().day.at(08:00).do(append_diary) while True: schedule.run_pending() time.sleep(1) if __name__ __main__: main()这段代码有几个值得说明的地方。日志同时输出到控制台和logs/diary_app.log文件便于观察运行状态append_diary中先创建目录再写入文件避免目录不存在时报错把内容构建和文件写入拆成两个函数逻辑更清晰。程序启动后会一直运行直到你按 CtrlC 手动终止。4.3 运行与验证在项目目录下运行python main.py正常情况下控制台会输出类似下面的日志2025-06-01 10:00:00 - INFO - 自动更新日记程序已启动 2025-06-01 11:00:00 - INFO - 日记已更新diary\2025-06-01.md由于设置的是每小时执行一次你不需要等太久就能看到新文件生成。如果想立刻验证可以手动调用一次写入函数python -c from main import append_diary; append_diary()这条命令会立即执行一次日记写入用来确认文件内容和格式是否符合预期。4.4 结果说明程序运行一段时间后diary 目录下会出现以日期命名的 Markdown 文件。打开2025-06-01.md内容大致如下# 2025-06-01 更新时间11:00:00 后座的门开着但没有人下车。每天多次触发时内容会不断追加文件里会形成多条记录。如果你希望每个时间段独立成文可以把文件名改成包含小时分钟的形式例如2025-06-01_1100.md如果你希望所有内容汇总在一个文件里直接沿用当前实现即可。这里的核心是理解“文件名决定粒度”的思路。5. 扩展实战网约车行程记录与位置追踪原理5.1 行程记录的功能设计标题中的“网约车后座的神秘人”看起来像故事但网约车行程记录本身是一个很典型的工程需求。乘客从上车到下车平台需要记录订单状态、经纬度轨迹和关键时间点。从实现上看这个过程可以抽象为三步周期性采集位置数据把数据转换为结构化文本再写入存储。和日记程序相比区别在于数据来源不同日记的内容来自随机池而行程记录的内容来自 GPS 模块或导航 SDK。理解这个抽象过程对后续开发会有很大帮助。5.2 模拟定位与轨迹写入这里用一个模拟定位的示例来演示轨迹记录的核心逻辑。真实项目里定位数据来自手机 GPS 或车辆终端但写入和调度的思路是相同的。import random import time from datetime import datetime from pathlib import Path def get_location(): # 模拟 GPS 坐标真实场景中应调用定位 SDK lng round(116.397 random.uniform(-0.01, 0.01), 6) lat round(39.909 random.uniform(-0.01, 0.01), 6) return lng, lat def write_track(file_path: Path, interval_seconds: int 5, total_times: int 10): file_path.parent.mkdir(parentsTrue, exist_okTrue) for i in range(total_times): lng, lat get_location() now datetime.now().strftime(%Y-%m-%d %H:%M:%S) with open(file_path, a, encodingutf-8) as f: f.write(f{now},{lng},{lat}\n) time.sleep(interval_seconds) if __name__ __main__: write_track(Path(track/test_ride.csv))运行后track/test_ride.csv中会生成类似下面的记录2025-06-01 10:05:01,116.398123,39.908765 2025-06-01 10:05:06,116.397654,39.909321每一行代表一个采集时间点上的经纬度这就是轨迹数据的原始形态。真实系统中这些数据会经过加密、压缩后上传到服务端再由后端做地图匹配、里程计算和费用结算。5.3 隐私与授权边界必须强调一点位置数据属于个人敏感信息。无论是做网约车行程记录还是在个人项目中模拟定位采集都需要遵守几条基本边界。第一只能采集自己拥有或经过明确授权的设备数据不能偷偷记录别人的位置第二数据存储要加密避免明文文件泄露第三遵循最小化原则只采集业务真正需要的字段不额外收集无关信息。在 Linux 服务器上做定时任务时要保证任务以最小权限账号运行在 Windows 计划任务里同样要避免使用管理员权限来跑普通脚本。6. 常见问题与排查思路6.1 高频报错对照表自动更新类程序在开发中最常遇到的问题可以整理成下表问题现象常见原因解决思路任务不触发schedule 循环没有运行或进程提前退出检查 while 和 sleep 是否在代码中用日志确认进程存活中文乱码打开文件时没有指定 encodingutf-8给 open 指定 encoding 参数时间不对系统时区不是本地时区检查系统时区必要时在项目里手动设置时区重复写入程序被重复启动多个进程同时执行任务使用单实例锁或通过 PID 文件防止重复启动日志文件不生成logs 目录不存在在代码中先调用 mkdir 创建目录文件被占用其他程序打开了同名文件检查编辑器或同步盘是否锁定了文件6.2 排查思路演示如果你遇到“任务不触发”的问题可以按下面的顺序排查。第一步确认程序是否在运行最简单的方法是在 while 循环里加一行日志输出第二步检查 schedule 的触发时间表达式是否写成了every().day.at(08:00)但不小心把任务绑定到了错误函数第三步单次调用任务函数验证逻辑是否正确用上一节提到的python -c from main import append_diary; append_diary()方式第四步查看logs/diary_app.log中有没有异常堆栈。这种从外到内、从现象到代码的排查顺序能帮你节省大量时间。7. 最佳实践与工程建议7.1 异常处理与日志记录把个人脚本升级成可靠工具第一步是做好异常处理和日志。示例中已经用 try-except 包住了文件写入但实际项目还需要考虑更多异常场景比如磁盘满了、权限不足、文件被锁。日志方面不要用 print 替代 logginglogging 可以记录时间级别和调用位置并且支持同时输出到控制台和文件。对于定时任务每次执行都应该有明确的日志输出这样即使程序在凌晨 3 点出错也能次日通过日志定位问题。7.2 数据备份与文件安全日记或行程记录这类数据一旦丢失很难找回备份策略应该提前考虑。最简单的方式是定期把整个数据目录压缩备份到另一个磁盘或对象存储如果数据需要长期保存还可以引入数据库写入。对于包含敏感信息的文件建议加密存储至少也要避免把文件放在公共目录或提交到公开仓库。程序生成的日记如果包含个人想法不要轻易上传到未加密的云盘。7.3 从脚本到服务上生产前要考虑的事如果你想把这个定时任务部署到服务器长期运行建议不要直接用终端挂起一个 while 循环。Linux 环境可以用 systemd 服务管理进程让任务在开机后自动启动并在异常退出时自动重启Windows 环境可以使用任务计划程序在系统启动时运行脚本。如果使用 Docker 部署则要把时区、日志目录和挂载卷都显式配置好。无论采用哪种方式都要设置进程的运行用户为专用低权限账号避免脚本被入侵后扩大损失。8. 总结与延伸这篇教程从一个略带恐怖色彩的故事设定切入实际完成了一个可运行的自动更新日记程序。通过拆解可以发现所谓“死亡日记”的自动更新能力本质上就是时间获取、文件追加、定时调度和内容生成四个模块的组合。代码量不大但把调度、持久化、日志和异常处理都涉及到了很适合作为 Python 自动化工具的练手项目。后续可以往几个方向继续深入。第一把日记存储从文件换成 SQLite 或 MySQL让数据检索更方便第二用 FastAPI 或 Flask 给程序加一个 Web 界面在浏览器里查看历史日记第三接入真实定位 API把网约车行程记录的模拟采集替换成真实数据同时认真设计权限和加密方案。如果有兴趣还可以把这篇内容里的流程改造成一个通用的定时任务管理脚本让不同的任务函数共用同一套调度框架。如果你在运行过程中遇到代码报错或功能不符合预期可以对照常见问题部分的排查思路逐项检查。如果这篇文章对你有帮助可以收藏备用也欢迎在评论区分享你的运行结果。下一篇会继续选择贴近生活的场景做技术拆解把故事感转化为可落地的代码。
返回列表