如何编写Windows Service定时运行Python脚本?含定点/周期执行需求
编写Windows Service定时运行Python脚本的实用方案
刚好我之前折腾过类似的需求,给你整理两种靠谱的实现路径,分别适合新手和需要灵活控制的场景,顺便把定时逻辑的实现也讲清楚:
一、快速上手:用NSSM把脚本封装成服务(新手推荐)
如果你不想写太多服务相关的代码,NSSM绝对是首选——它能把任意可执行程序直接包装成Windows服务,步骤超简单:
- 先下载NSSM,解压到一个固定路径(比如
C:\nssm),然后把这个路径加到系统环境变量PATH里,这样命令行就能直接调用它了。 - 先确保你的Python脚本能正常运行,比如打开命令行跑一遍
python C:\scripts\daily_task.py,没问题再往下走。 - 打开管理员权限的命令提示符,输入
nssm install PythonTaskService,会弹出一个图形配置界面:- Path:选你的Python解释器绝对路径,比如
C:\Python39\python.exe - Arguments:填你要运行的脚本绝对路径,比如
C:\scripts\daily_task.py - Working Directory:填脚本所在的文件夹路径,比如
C:\scripts - 切到Details标签,给服务起个好记的名字和描述,方便以后在服务管理器里找
- 切到Log On标签,设置服务运行的账户——如果脚本需要读写特定文件或者访问网络,建议用有对应权限的账户(比如管理员账户)
- Path:选你的Python解释器绝对路径,比如
- 配置完点OK,服务就安装好了。去服务管理器里找到这个服务,把启动类型改成自动,然后启动它就行。
不过要注意,NSSM只是帮你把脚本做成常驻服务,定时执行的逻辑还得在脚本里自己写,或者你也可以直接用Windows任务计划来触发脚本(后面会讲这个更省心的方式)。
二、灵活定制:用pywin32写自定义服务
如果需要更精细的控制(比如随时调整定时规则、记录服务运行日志),可以用pywin32库自己写服务代码,内置定时逻辑:
- 先装pywin32:打开命令行跑
pip install pywin32 - 写服务代码,比如存成
python_service.py,代码我给你写好了,你可以直接改参数:
import win32serviceutil import win32service import win32event import servicemanager import socket import time import subprocess from datetime import datetime, timedelta class PythonTaskService(win32serviceutil.ServiceFramework): # 服务的基本信息,自己改 _svc_name_ = "PythonTaskService" _svc_display_name_ = "Python定时任务服务" _svc_description_ = "每天10点或每2小时执行指定Python脚本" def __init__(self, args): win32serviceutil.ServiceFramework.__init__(self, args) self.hWaitStop = win32event.CreateEvent(None, 0, 0, None) socket.setdefaulttimeout(60) self.is_running = True # 这里配置定时规则,二选一就行 self.schedule_type = "daily" # 可选 "daily"(每天固定时间)或 "interval"(间隔执行) self.daily_time = "10:00" # 每天执行的时间点 self.interval_hours = 2 # 间隔执行的小时数 self.next_run_time = self.calculate_next_run() def calculate_next_run(self): now = datetime.now() if self.schedule_type == "daily": # 计算当天的目标时间,如果已经过了就推到明天 target_time = datetime.strptime(self.daily_time, "%H:%M").replace(year=now.year, month=now.month, day=now.day) if target_time < now: target_time += timedelta(days=1) return target_time elif self.schedule_type == "interval": # 计算下一次执行时间,从现在往后推指定小时数 return now + timedelta(hours=self.interval_hours) return now def SvcStop(self): # 处理服务停止的逻辑 self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) win32event.SetEvent(self.hWaitStop) self.is_running = False def SvcDoRun(self): # 服务启动时执行的逻辑 servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED, (self._svc_name_, '')) self.main() def main(self): # 服务的主循环,负责定时触发脚本 while self.is_running: now = datetime.now() if now >= self.next_run_time: try: # 执行你的Python脚本,记得改路径 subprocess.run(["python", "C:\\scripts\\daily_task.py"], check=True) # 记录成功日志,在事件查看器里能看到 servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, 0, (f"脚本执行成功,时间:{now.strftime('%Y-%m-%d %H:%M:%S')}", '')) except subprocess.CalledProcessError as e: # 记录失败日志 servicemanager.LogMsg(servicemanager.EVENTLOG_ERROR_TYPE, 0, (f"脚本执行失败,错误:{str(e)}", '')) # 计算下一次执行时间 self.next_run_time = self.calculate_next_run() # 每分钟检查一次,别占太多系统资源 time.sleep(60) if __name__ == '__main__': # 处理服务的安装、启动、停止等命令 win32serviceutil.HandleCommandLine(PythonTaskService)
- 安装服务:打开管理员命令提示符,跑
python python_service.py install - 启动服务:要么跑
python python_service.py start,要么去服务管理器里找到“Python定时任务服务”启动 - 要是改了代码,先停服务
python python_service.py stop,再更新服务python python_service.py update就行
三、更省心的定时方案:配合Windows任务计划
其实如果只是需要定时执行脚本,不一定非要写服务——Windows自带的任务计划程序足够好用,而且不用维护常驻服务:
- 打开任务计划程序,点“创建基本任务”
- 跟着向导走:先起个名字,然后设置触发器(选“每天”就设10点,选“按间隔”就设2小时)
- 设置操作:选“启动程序”,程序或脚本填Python解释器的绝对路径,参数填脚本的绝对路径
- 最后勾选“以最高权限运行”(如果脚本需要权限),完成就行
任务计划会自动在指定时间触发脚本,还能设置失败重试、邮件通知,比自己写定时逻辑靠谱多了。
四、踩过的坑要注意
- 权限问题:不管是服务还是任务计划,运行的账户必须有脚本执行的权限——比如读写文件、访问网络,不然脚本会悄悄失败。
- 环境一致性:一定要用Python解释器的绝对路径,别直接写
python,不然服务/任务计划可能找不到你用的Python环境,导致模块找不到。 - 日志记录:建议在脚本里加日志功能,或者用服务的日志(上面的代码里用了servicemanager,日志在事件查看器的“Windows日志->应用程序”里找),出问题了好排查。
内容的提问来源于stack exchange,提问作者skysoft999




