python定时启动程序方法

第一种办法是最简单又最暴力。那就是在一个死循环中,使用线程睡眠函数 sleep()。

缺点:占CPU内存,死循环 + 阻塞线程

def doSth():
    # 把爬虫程序放在这个类里
    print(u'这个程序要开始疯狂的运转啦')


# 一般网站都是1:00点更新数据,所以每天凌晨一点启动

def main(h=1, m=0):
    while True:
        now = datetime.datetime.now()
        print(now.hour, now.minute)
        if now.hour == h and now.minute == m:
            break
        # 每隔60秒检测一次
        time.sleep(60)
    doSth()

Python 标准库 threading 中有个 Timer 类。它会新启动一个线程来执行定时任务,所以它是非阻塞函式。

每2秒循环调用
from threading import Timer


def printHello():
    print('TimeNow:%s' % (datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
    t = Timer(2, printHello)
    t.start()

if __name__ == "__main__":
    printHello()
2秒后调用一次结束
from threading import Timer


def task():
    print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
    
def printHello():
    t = Timer(2, task)
    t.start()


if __name__ == "__main__":
    printHello()

通过 scheduler 类来调度事件,从而达到定时执行任务的效果。

首先安装包: pip install schedule
每个事件都在同一线程中运行,所以如果一个事件需要更长的时间如(函数加上time.sleep(3)),延迟事件将会有重叠。为了不丢失事件,延迟事件将会在之前事件运行完再被执行,但一些延迟事件可能会晚于原本计划的事件。

代码
import schedule


def job1():
    print('Job1:每隔10秒执行一次的任务,每次执行2秒')
    print('Job1-startTime:%s' % (datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
    time.sleep(2)
    print('Job1-endTime:%s' % (datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
    print('------------------------------------------------------------------------')


def job2():
    print('Job2:每隔30秒执行一次,每次执行5秒')
    print('Job2-startTime:%s' % (datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
    time.sleep(5)
    print('Job2-endTime:%s' % (datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
    print('------------------------------------------------------------------------')


def job3():
    print('Job3:每隔1分钟执行一次,每次执行10秒')
    print('Job3-startTime:%s' % (datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
    time.sleep(10)
    print('Job3-endTime:%s' % (datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
    print('------------------------------------------------------------------------')


def job4():
    print('Job4:每天下午17:49执行一次,每次执行20秒')
    print('Job4-startTime:%s' % (datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
    time.sleep(20)
    print('Job4-endTime:%s' % (datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
    print('------------------------------------------------------------------------')


def job5():
    print('Job5:每隔5秒到10秒执行一次,每次执行3秒')
    print('Job5-startTime:%s' % (datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
    time.sleep(3)
    print('Job5-endTime:%s' % (datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
    print('------------------------------------------------------------------------')


def job6():
    exit()


if __name__ == '__main__':
    # schedule.every(10).seconds.do(job1)
    # schedule.every(30).seconds.do(job2)
    # schedule.every(1).minutes.do(job3)
    # schedule.every().day.at('09:29').do(job4)
    schedule.every().day.at('16:54').do(job6)
    schedule.every(5).to(10).seconds.do(job5)
    while True:
        schedule.run_pending()
结果
------------------------------------------------------------------------
Job1:每隔10秒执行一次的任务,每次执行2秒
Job1-startTime:2019-11-12 14:36:01
Job1-endTime:2019-11-12 14:36:03
------------------------------------------------------------------------
Job2:每隔30秒执行一次,每次执行5秒
Job2-startTime:2019-11-12 14:36:09
Job2-endTime:2019-11-12 14:36:14
------------------------------------------------------------------------
Job1:每隔10秒执行一次的任务,每次执行2秒
Job1-startTime:2019-11-12 14:36:14
Job1-endTime:2019-11-12 14:36:16
------------------------------------------------------------------------

不加上时间阻碍相当于多线程执行

代码
import time

import schedule


def job():
    print("I'm working")
    worker_main()

def worker_main():
    print('Job5-startTime:%s' % (time.time()))
# datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')


schedule.every(10).seconds.do(job)
schedule.every(10).seconds.do(job)
schedule.every(10).seconds.do(job)
schedule.every(10).seconds.do(job)
schedule.every(10).seconds.do(job)


while 1:
    schedule.run_pending()
结果
I'm working
Job5-startTime:1573541137.3658931
I'm working
Job5-startTime:1573541137.3658931
I'm working
Job5-startTime:1573541137.3658931
I'm working
Job5-startTime:1573541137.3658931
I'm working
Job5-startTime:1573541137.3658931

sched——通用时间调度器

sched模块实现了一个通用事件调度器,在调度器类使用一个延迟函数等待特定的时间,执行任务。同时支持多线程应用程序,在每个任务执行后会立刻调用延时函数,以确保其他线程也能执行。

代码
import sched
import time

#生成调度器
scheduler = sched.scheduler(time.time, time.sleep)


def print_event(name):
    print ('EVENT:', time.time(), name)

print ('START:', time.time())

#分别设置在执行后2秒、3秒之后执行调用函数
scheduler.enter(2, 1, print_event, ('first',))
scheduler.enter(3, 1, print_event, ('second',))

#运行调度器
scheduler.run()

+++++++++++++++++++++++++++++++++++++++++++++++

以上就是时间定时器,但是都无法做到循环执行定时任务。因此,需要一个能够担当此重任的库。它就是APScheduler

APScheduler的全称是Advanced Python Scheduler。它是一个轻量级的 Python 定时任务调度框架。APScheduler 调度器(scheduler)任务控制器:通过配置executor、jobstore、trigger,使用线程池(ThreadPoolExecutor默认值20)或进程池(ProcessPoolExecutor 默认值5)并且默认最多3个(max_instances)任务实例同时运行,实现对job的增删改查等调度控制

你需要选择合适的调度器,这取决于你的应用环境和你使用APScheduler的目的。通常最常用的两个:

BlockingScheduler:当调度器是你应用中唯一要运行的东西时使用。 

BackgroundScheduler:当你不运行任何其他框架时使用,并希望调度器在你应用的后台执行。

作业存储

支持4中作业存储,分别是:MemoryJobStore(存储在内存中)、sqlalchemy(关系型数据库)、mongodb(文档数据库)、redis(内存型键值对数据库)

触发方式

date:固定日期触发器:任务只运行一次,运行完毕自动清除;若错过指定运行时间,任务不会被创建
interval:时间间隔触发器,每个一定时间间隔执行一次。
cron:cron风格的任务触发。

简单实用:

代码:
def tick(s):
    print("Tick! The time is: %s" % datetime.now(), s)


scheduler = BlockingScheduler()
# ----------------------------------interval间隔:触发任务运行的时间间隔-------------------------
# 触发器为 interval,每隔 3 秒执行一次
# jitter振动参数,给每次触发添加一个随机浮动秒数,一般适用于多服务器,避免同时运行造成服务拥堵。
scheduler.add_job(tick, 'interval', seconds=3, jitter=10, id="test", args=['text'])
# 在 2019-04-15 17:00:00 ~ 2019-12-31 24:00:00 之间, 每隔两分钟执行一次 job_func 方法
a = scheduler.add_job(tick, 'interval', seconds=3, id="test1", start_date='2019-11-12 11:13:00',
                      end_date='2019-11-12 11:14:00')
# 每2小时触发
scheduler.add_job(tick, 'interval', hours=2)
jitter振动参数,给每次触发添加一个随机浮动秒数,一般适用于多服务器,避免同时运行造成服务拥堵。
# 每小时(上下浮动120秒区间内)运行`job_function`
sched.add_job(job_function, 'interval', hours=1, jitter=120)
# -----------------------------------cron周期:触发任务运行的周期--------------------------------

# hour =19 ,minute =23 这里表示每天的19:23 分执行任务
scheduler.add_job(tick, 'cron', hour=11, minute=50)
# 任务会在6月、7月、8月、11月和12月的第三个周五,00:00、01:00、02:00和03:00触发
scheduler.add_job(tick, 'cron', month='6-8,11-12', day='3rd fri', hour='0-3')
# 在2014-05-30 00:00:00前,每周一到每周五 5:30运行
scheduler.add_job(tick, 'cron', day_of_week='mon-fri', hour=5, minute=30, end_date='2014-05-30')

# ---------------------------------date日期:触发任务运行的具体日期------------------------------
# 其中run_date参数可以是date类型、datetime类型或文本类型,只在2010 - 2 - 25 19: 05:06执行一次,args传递一个text参数。
scheduler.add_job(tick, 'date', run_date=datetime(2019, 11, 12, 13, 56, 25), args=['text1'], id='job1')


scheduler.start()

另一种是以装饰器形式

代码:
from datetime import datetime

from apscheduler.schedulers.blocking import BlockingScheduler

scheduler = BlockingScheduler()


@scheduler.scheduled_job('cron', id='my_job_id', day='last sun')
def some_decorated_task():
    print("I am printed at 00:00:00 on the last Sunday of every month!")
    
# 每天每天凌晨1点30分50秒执行一次scheduled_job()装饰器实现
@scheduler.scheduled_job('cron', day_of_week='*', hour=1, minute='30', second='00')
def tick():
    print("Tick! The time is: %s" % datetime.now())

try:
    scheduler.start()
    print('定时任务成功执行')
except Exception as e:
    scheduler.shutdown()
    print('定时任务执行失败')
finally:
    exit()
移除任务
  1. 调用remove_job(),参数为:任务ID,任务储存器名称
  2. 在通过add_job()创建的任务实例上调用remove()方法
    第二种方式更方便,但前提必须在创建任务实例时,实例被保存在变量中。对于通过scheduled_job()创建的任务,只能选择第一种方式。
    当任务调度结束时(比如,某个任务的触发器不再产生下次运行的时间),任务就会自动移除
job = scheduler.add_job(myfunc, 'interval', minutes=2)
job.remove()
# 根据id
scheduler.add_job(myfunc, 'interval', minutes=2, id='my_job_id')
scheduler.remove_job('my_job_id')
暂停和恢复任务

通过任务实例或调度器,就能暂停和恢复任务。如果一个任务被暂停了,那么该任务的下一次运行时间就会被移除。在恢复任务前,运行次数计数也不会被统计。
暂停任务,有以下两个方法:
apscheduler.job.Job.pause()
apscheduler.schedulers.base.BaseScheduler.pause_job()
恢复任务:
apscheduler.job.Job.resume()
apscheduler.schedulers.base.BaseScheduler.resume_job()

关闭调度器

关闭方法如下:

scheduler.shutdown()
# 默认情况下,调度器会先把正在执行的任务处理完,再关闭任务储存器和执行器。但是,如果你就直接关闭,你可以添加参数:
scheduler.shutdown(wait=False)

暂停、恢复任务进程
调度器可以暂停正在执行的任务:

scheduler.pause()

也可以恢复任务:

scheduler.resume()

同时,也可以在调度器启动时,默认所有任务设为暂停状态

scheduler.start(paused=True)
夏令时问题

有些timezone时区可能会有夏令时的问题。这个可能导致令时切换时,任务不执行或任务执行两次。避免这个问题,可以使用UTC时间,或提前预知并规划好执行的问题。

# 在Europe/Helsinki时区, 在三月最后一个周一就不会触发;在十月最后一个周一会触发两次
sched.add_job(job_function, 'cron', hour=3, minute=30)
加日志,当程序执行完才会显示
from apscheduler.events import EVENT_JOB_EXECUTED, EVENT_JOB_ERROR
import logging

logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S',
                    filename='log1.log',
                    filemode='a',
                    )
logging.debug("调试信息")
logging.info("普通信息")
logging.warning("警告信息")
logging.error("错误信息")
logging.critical("严重错误信息")


def aps_test(x):
    print(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), x)
    print(1 / 0)


def date_test(x):
    print(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), x)


def my_listener(event):
    if event.exception:
        a = '任务出错了'
        print(a)


    else:
        a = '任务照常运行'
        print(a)



scheduler = BlockingScheduler()
scheduler.add_job(func=aps_test, args=('一次性任务,会出错',), next_run_time=datetime.now() + timedelta(seconds=13),
                  id='date_task')
scheduler.add_job(func=date_test, args=('循环任务',), trigger='interval', seconds=5, id='interval_task')
scheduler.add_listener(my_listener, EVENT_JOB_EXECUTED | EVENT_JOB_ERROR)
scheduler._logger = logging

scheduler.start()

谢谢阅览!!!

  • 12
    点赞
  • 94
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值