Python中Schedule模块使用详解 周期任务神器


Posted in Python onApril 19, 2022

如果你想在Linux服务器上周期性地执行某个 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.准备

请选择以下任一种方式输入命令安装依赖:

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

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

3. 如果你用的是 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周期任务神器之Schedule模块使用详解的文章就介绍到这了!

Python 相关文章推荐
python追加元素到列表的方法
Jul 28 Python
深入解析Python中的descriptor描述器的作用及用法
Jun 27 Python
python框架django基础指南
Sep 08 Python
Python编程之event对象的用法实例分析
Mar 23 Python
使用python编写简单的小程序编译成exe跑在win10上
Jan 15 Python
Python 3.x 安装opencv+opencv_contrib的操作方法
Apr 02 Python
python中的变量如何开辟内存
Jun 26 Python
python中字符串内置函数的用法总结
Sep 13 Python
flask session组件的使用示例
Dec 25 Python
python的内存管理和垃圾回收机制详解
May 18 Python
python读写Excel表格的实例代码(简单实用)
Dec 19 Python
Python中socket网络通信是干嘛的
May 27 Python
python中urllib包的网络请求教程
Apr 19 #Python
python APScheduler执行定时任务介绍
Apr 19 #Python
Python数据可视化之Seaborn的安装及使用
python 闭包函数详细介绍
Apr 19 #Python
Python  lambda匿名函数和三元运算符
Apr 19 #Python
Python使用mitmproxy工具监控手机 下载手机小视频
使用Python通过企业微信应用给企业成员发消息
You might like
PHP 清空varnish 缓存的详解(包括指定站点下的)
2013/06/20 PHP
PHP实现JS中escape与unescape的方法
2016/07/11 PHP
Laravel中validation验证 返回中文提示 全局设置的方法
2019/09/29 PHP
window.open()弹出居中的窗口
2007/02/01 Javascript
JS 打印功能代码可实现打印预览、打印设置等
2014/10/31 Javascript
jQuery实现友好的轮播图片特效
2015/01/12 Javascript
使用HTML+CSS+JS制作简单的网页菜单界面
2015/07/27 Javascript
js控制多图左右滚动切换效果代码分享
2015/08/26 Javascript
node.js中 stream使用教程
2016/08/28 Javascript
十大热门的JavaScript框架和库
2017/03/21 Javascript
3分钟掌握常用的JS操作JSON方法总结
2017/04/25 Javascript
详谈js中标准for循环与foreach(for in)的区别
2017/11/02 Javascript
jQuery常见的遍历DOM操作详解
2018/09/05 jQuery
jQuery easyui datagird编辑行删除行功能的实现代码
2018/09/20 jQuery
小程序获取当前位置加搜索附近热门小区及商区的方法
2019/04/08 Javascript
vue实现分环境打包步骤(给不同的环境配置相对应的打包命令)
2019/06/04 Javascript
Vue在chrome44偶现点击子元素事件无法冒泡的解决方法
2019/12/15 Javascript
整理 node-sass 安装失败的原因及解决办法(小结)
2020/02/19 Javascript
基于JavaScript实现简单的轮播图
2021/03/03 Javascript
[46:55]Ti4 冒泡赛第二轮 LGD vs C9
2014/07/14 DOTA
python导入模块交叉引用的方法
2019/01/19 Python
Java Spring项目国际化(i18n)详细方法与实例
2020/03/20 Python
Pytorch 使用不同版本的cuda的方法步骤
2020/04/02 Python
CK美国官网:Calvin Klein
2016/08/26 全球购物
印度化妆品购物网站:Nykaa
2018/07/22 全球购物
精彩的推荐信范文
2013/11/26 职场文书
八年级历史教学反思
2014/01/10 职场文书
财务部总监岗位职责
2014/03/12 职场文书
2014年村委会工作总结
2014/11/24 职场文书
大学生操行评语大全
2014/12/31 职场文书
三八妇女节主持词
2015/07/04 职场文书
春节慰问简报
2015/07/21 职场文书
婚礼答谢词范文
2015/09/29 职场文书
2016年“六一儿童节”校园广播稿
2015/12/17 职场文书
2016学校先进党组织事迹材料
2016/02/29 职场文书
Python爬取英雄联盟MSI直播间弹幕并生成词云图
2021/06/01 Python