python周期任务调度工具Schedule使用详解


Posted in Python onNovember 23, 2021

如果你想周期性地执行某个 Python 脚本,最出名的选择应该是 Crontab 脚本,但是 Crontab 具有以下缺点:

1.不方便执行秒级任务。

2.当需要执行的定时任务有上百个的时候,Crontab 的管理就会特别不方便。

还有一个选择是 Celery,但是 Celery 的配置比较麻烦,如果你只是需要一个轻量级的调度工具,Celery 不会是一个好选择。

在你想要使用一个轻量级的任务调度工具,而且希望它尽量简单、容易使用、不需要外部依赖,最好能够容纳 Crontab 的所有基本功能,那么 Schedule 模块是你的不二之选。

使用它来调度任务可能只需要几行代码,感受一下:

import schedule
import time
def job():
    print("I'm working...")
schedule.every(10).minutes.do(job)
while True:
    schedule.run_pending()
    time.sleep(1)

上面的代码表示每10分钟执行一次 job 函数,非常简单方便。你只需要引入 schedule 模块,通过调用 scedule.every(时间数).时间类型.do(job) 发布周期任务。

发布后的周期任务需要用 run_pending 函数来检测是否执行,因此需要一个 While 循环不断地轮询这个函数。

下面具体讲讲Schedule模块的安装和初级、进阶使用方法。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上,请选择以下任一种方式输入命令安装依赖:

Windows 环境 打开 Cmd (开始-运行-CMD)。

MacOS 环境 打开 Terminal (command+空格输入Terminal)。

如果你用的是 VSCode编辑器 或 Pycharm,可以直接使用界面下方的Terminal.

pip install schedule

2.基本使用

最基本的使用在文首已经提到过,下面给大家展示更多的调度任务例子:

import schedule
import time
def job():
    print("I'm working...")
# 每十分钟执行任务
schedule.every(10).minutes.do(job)
# 每个小时执行任务
schedule.every().hour.do(job)
# 每天的10:30执行任务
schedule.every().day.at("10:30").do(job)
# 每个月执行任务
schedule.every().monday.do(job)
# 每个星期三的13:15分执行任务
schedule.every().wednesday.at("13:15").do(job)
# 每分钟的第17秒执行任务
schedule.every().minute.at(":17").do(job)
while True:
    schedule.run_pending()
    time.sleep(1)

可以看到,从月到秒的配置,上面的例子都覆盖到了。不过如果你想只运行一次任务的话,可以这么配

import schedule
import time
def job_that_executes_once():
    # 此处编写的任务只会执行一次...
    return schedule.CancelJob
schedule.every().day.at('22:30').do(job_that_executes_once)
while True:
    schedule.run_pending()
    time.sleep(1)

参数传递

如果你有参数需要传递给作业去执行,你只需要这么做:

import schedule
def greet(name):
    print('Hello', name)
# do() 将额外的参数传递给job函数
schedule.every(2).seconds.do(greet, name='Alice')
schedule.every(4).seconds.do(greet, name='Bob')

获取目前所有的作业

如果你想获取目前所有的作业:

import schedule
def hello():
    print('Hello world')
schedule.every().second.do(hello)
all_jobs = schedule.get_jobs()

取消所有作业

如果某些机制触发了,你需要立即清除当前程序的所有作业:

import schedule
def greet(name):
    print('Hello {}'.format(name))
schedule.every().second.do(greet)
schedule.clear()

标签功能

在设置作业的时候,为了后续方便管理作业,你可以给作业打个标签,这样你可以通过标签过滤获取作业或取消作业。

import schedule
def greet(name):
    print('Hello {}'.format(name))
# .tag 打标签
schedule.every().day.do(greet, 'Andrea').tag('daily-tasks', 'friend')
schedule.every().hour.do(greet, 'John').tag('hourly-tasks', 'friend')
schedule.every().hour.do(greet, 'Monica').tag('hourly-tasks', 'customer')
schedule.every().day.do(greet, 'Derek').tag('daily-tasks', 'guest')
# get_jobs(标签):可以获取所有该标签的任务
friends = schedule.get_jobs('friend')
# 取消所有 daily-tasks 标签的任务
schedule.clear('daily-tasks')

设定作业截止时间

如果你需要让某个作业到某个时间截止,你可以通过这个方法:

import schedule
from datetime import datetime, timedelta, time
def job():
    print('Boo')
# 每个小时运行作业,18:30后停止
schedule.every(1).hours.until("18:30").do(job)
# 每个小时运行作业,2030-01-01 18:33 today
schedule.every(1).hours.until("2030-01-01 18:33").do(job)
# 每个小时运行作业,8个小时后停止
schedule.every(1).hours.until(timedelta(hours=8)).do(job)
# 每个小时运行作业,11:32:42后停止
schedule.every(1).hours.until(time(11, 33, 42)).do(job)
# 每个小时运行作业,2020-5-17 11:36:20后停止
schedule.every(1).hours.until(datetime(2020, 5, 17, 11, 36, 20)).do(job)

截止日期之后,该作业将无法运行。

立即运行所有作业,而不管其安排如何

如果某个机制触发了,你需要立即运行所有作业,可以调用 schedule.run_all() :

import schedule
def job_1():
    print('Foo')
def job_2():
    print('Bar')
schedule.every().monday.at("12:40").do(job_1)
schedule.every().tuesday.at("16:40").do(job_2)
schedule.run_all()
# 立即运行所有作业,每次作业间隔10秒
schedule.run_all(delay_seconds=10)

3.高级使用

装饰器安排作业

如果你觉得设定作业这种形式太啰嗦了,也可以使用装饰器模式:

from schedule import every, repeat, run_pending
import time
# 此装饰器效果等同于 schedule.every(10).minutes.do(job)
@repeat(every(10).minutes)
def job():
    print("I am a scheduled job")
while True:
    run_pending()
    time.sleep(1)

并行执行

默认情况下,Schedule 按顺序执行所有作业。其背后的原因是,很难找到让每个人都高兴的并行执行模型。

不过你可以通过多线程的形式来运行每个作业以解决此限制:

import threading
import time
import schedule
def job1():
    print("I'm running on thread %s" % threading.current_thread())
def job2():
    print("I'm running on thread %s" % threading.current_thread())
def job3():
    print("I'm running on thread %s" % threading.current_thread())
def run_threaded(job_func):
    job_thread = threading.Thread(target=job_func)
    job_thread.start()
schedule.every(10).seconds.do(run_threaded, job1)
schedule.every(10).seconds.do(run_threaded, job2)
schedule.every(10).seconds.do(run_threaded, job3)
while True:
    schedule.run_pending()
    time.sleep(1)

日志记录

Schedule 模块同时也支持 logging 日志记录,这么使用:

import schedule
import logging
logging.basicConfig()
schedule_logger = logging.getLogger('schedule')
# 日志级别为DEBUG
schedule_logger.setLevel(level=logging.DEBUG)
def job():
    print("Hello, Logs")
schedule.every().second.do(job)
schedule.run_all()
schedule.clear()

效果如下:

DEBUG:schedule:Running *all* 1 jobs with 0s delay in between
DEBUG:schedule:Running job Job(interval=1, unit=seconds, do=job, args=(), kwargs={})
Hello, Logs
DEBUG:schedule:Deleting *all* jobs

异常处理

Schedule 不会自动捕捉异常,它遇到异常会直接抛出,这会导致一个严重的问题:后续所有的作业都会被中断执行,因此我们需要捕捉到这些异常。

你可以手动捕捉,但是某些你预料不到的情况需要程序进行自动捕获,加一个装饰器就能做到了:

import functools
def catch_exceptions(cancel_on_failure=False):
    def catch_exceptions_decorator(job_func):
        @functools.wraps(job_func)
        def wrapper(*args, **kwargs):
            try:
                return job_func(*args, **kwargs)
            except:
                import traceback
                print(traceback.format_exc())
                if cancel_on_failure:
                    return schedule.CancelJob
        return wrapper
    return catch_exceptions_decorator
@catch_exceptions(cancel_on_failure=True)
def bad_task():
    return 1 / 0
schedule.every(5).minutes.do(bad_task)

这样,bad_task 在执行时遇到的任何错误,都会被 catch_exceptions 捕获,这点在保证调度任务正常运转的时候非常关键。

我们的文章到此就结束啦,如果你喜欢今天的Python 实战教程,请持续关注。

以上就是python周期任务调度工具Schedule使用详解的详细内容,更多关于周期任务调度工具Schedule的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
Python 错误和异常小结
Oct 09 Python
windows系统下Python环境搭建教程
Mar 28 Python
浅谈Tensorflow模型的保存与恢复加载
Apr 26 Python
python opencv实现旋转矩形框裁减功能
Jul 25 Python
Python pymongo模块常用操作分析
Sep 01 Python
Python中查看变量的类型内存地址所占字节的大小
Jun 26 Python
使用Python将字符串转换为格式化的日期时间字符串
Sep 01 Python
Python pandas自定义函数的使用方法示例
Nov 20 Python
Flask和pyecharts实现动态数据可视化
Feb 26 Python
Python列表切片常用操作实例解析
Mar 10 Python
10个顶级Python实用库推荐
Mar 04 Python
python使用shell脚本创建kafka连接器
Apr 29 Python
python百行代码实现汉服圈图片爬取
python可视化大屏库big_screen示例详解
python数据可视化JupyterLab实用扩展程序Mito
python入门学习关于for else的特殊特性讲解
Nov 20 #Python
Python标准库pathlib操作目录和文件
Nov 20 #Python
python数据可视化使用pyfinance分析证券收益示例详解
python编程学习使用管道Pipe编写优化代码
Nov 20 #Python
You might like
PHP中的cookie
2006/11/26 PHP
PHP获取网卡地址的代码
2008/04/09 PHP
PHP执行zip与rar解压缩方法实现代码
2010/12/05 PHP
JavaScript开发时的五个注意事项
2007/12/08 Javascript
CSS JavaScript 实现菜单功能 改进版
2008/12/09 Javascript
javascript 年月日联动实现核心代码
2009/12/21 Javascript
轻轻松松学JS调试(不下载任何工具)
2010/04/14 Javascript
网页禁用右键菜单和鼠标拖动选择方法小结
2015/02/25 Javascript
第一次接触神奇的Bootstrap网格系统
2016/07/27 Javascript
js 定位到某个锚点的方法
2016/11/19 Javascript
js实现选项卡内容切换以及折叠和展开效果【推荐】
2017/01/08 Javascript
jQuery Chosen通用初始化
2017/03/07 Javascript
前端自动化开发之Node.js的环境搭建教程
2017/04/01 Javascript
解决angular2在双向数据绑定时[(ngModel)]无法使用的问题
2018/09/13 Javascript
Vue服务端渲染实践之Web应用首屏耗时最优化方案
2019/03/22 Javascript
layer关闭当前窗口页面以及确认取消按钮的方法
2019/09/09 Javascript
详解JavaScript类型判断的四种方法
2020/10/21 Javascript
将Django使用的数据库从MySQL迁移到PostgreSQL的教程
2015/04/11 Python
python数据分析数据标准化及离散化详解
2018/02/26 Python
Python简单计算文件MD5值的方法示例
2018/04/11 Python
python3调用百度翻译API实现实时翻译
2018/08/16 Python
对Python捕获控制台输出流的方法详解
2019/01/07 Python
Python3 hashlib密码散列算法原理详解
2020/03/30 Python
python使用多线程查询数据库的实现示例
2020/08/17 Python
Django中使用Celery的方法步骤
2020/12/07 Python
pandas数据分组groupby()和统计函数agg()的使用
2021/03/04 Python
24岁生日感言
2014/01/13 职场文书
优秀企业获奖感言
2014/02/01 职场文书
班组长竞聘书
2014/03/31 职场文书
中秋节国旗下演讲稿
2014/09/13 职场文书
秋季运动会广播稿(30篇)
2014/09/13 职场文书
党员教师学习党的群众路线教育实践活动心得体会
2014/10/31 职场文书
2016新教师培训心得体会范文
2016/01/08 职场文书
小学语文新课改心得体会
2016/01/22 职场文书
python 爬取京东指定商品评论并进行情感分析
2021/05/27 Python
Python常用配置文件ini、json、yaml读写总结
2021/07/09 Python