简体   繁体   English

安排 Python 脚本每小时准确运行

[英]Scheduling Python Script to run every hour accurately

Before I ask, Cron Jobs and Task Scheduler will be my last options, this script will be used across Windows and Linux and I'd prefer to have a coded out method of doing this than leaving this to the end user to complete.在我问之前, Cron Jobs 和 Task Scheduler将是我最后的选择,这个脚本将在 Windows 和 Linux 上使用,我宁愿有一个编码出来的方法来完成这个而不是留给最终用户来完成。

Is there a library for Python that I can use to schedule tasks?是否有可用于安排任务的 Python 库? I will need to run a function once every hour, however, over time if I run a script once every hour and use.sleep, "once every hour" will run at a different part of the hour from the previous day due to the delay inherent to executing/running the script and/or function.我将需要每小时运行一次函数,但是,随着时间的推移,如果我每小时运行一次脚本并使用 .sleep,由于延迟,“每小时一次”将在与前一天不同的时间运行执行/运行脚本和/或函数所固有的。

What is the best way to schedule a function to run at a specific time of day (more than once) without using a Cron Job or scheduling it with Task Scheduler?使用 Cron Job 或使用 Task Scheduler 安排功能的情况下,安排功能在一天中的特定时间(多次)运行的最佳方法是什么?

Or if this is not possible, I would like your input as well.或者,如果这不可能,我也希望您能提供意见。

AP Scheduler fit my needs exactly. AP Scheduler 完全符合我的需求。

Version < 3.0版本 < 3.0

import datetime
import time
from apscheduler.scheduler import Scheduler

# Start the scheduler
sched = Scheduler()
sched.daemonic = False
sched.start()

def job_function():
    print("Hello World")
    print(datetime.datetime.now())
    time.sleep(20)

# Schedules job_function to be run once each minute
sched.add_cron_job(job_function,  minute='0-59')

out:出去:

>Hello World
>2014-03-28 09:44:00.016.492
>Hello World
>2014-03-28 09:45:00.0.14110

Version > 3.0版本 > 3.0

(From Animesh Pandey's answer below) (来自下面的 Animesh Pandey 的回答)

from apscheduler.schedulers.blocking import BlockingScheduler

sched = BlockingScheduler()

@sched.scheduled_job('interval', seconds=10)
def timed_job():
    print('This job is run every 10 seconds.')

@sched.scheduled_job('cron', day_of_week='mon-fri', hour=10)
def scheduled_job():
    print('This job is run every weekday at 10am.')

sched.configure(options_from_ini_file)
sched.start()

Maybe this can help: Advanced Python Scheduler也许这会有所帮助: Advanced Python Scheduler

Here's a small piece of code from their documentation:这是他们文档中的一小段代码:

from apscheduler.schedulers.blocking import BlockingScheduler

def some_job():
    print "Decorated job"

scheduler = BlockingScheduler()
scheduler.add_job(some_job, 'interval', hours=1)
scheduler.start()

To run something every 10 minutes past the hour.每小时每 10 分钟运行一次。

from datetime import datetime, timedelta

while 1:
    print 'Run something..'

    dt = datetime.now() + timedelta(hours=1)
    dt = dt.replace(minute=10)

    while datetime.now() < dt:
        time.sleep(1)

For apscheduler < 3.0, see Unknown's answer .对于apscheduler < 3.0,请参阅Unknown's answer

For apscheduler > 3.0对于apscheduler > 3.0

from apscheduler.schedulers.blocking import BlockingScheduler

sched = BlockingScheduler()

@sched.scheduled_job('interval', seconds=10)
def timed_job():
    print('This job is run every 10 seconds.')

@sched.scheduled_job('cron', day_of_week='mon-fri', hour=10)
def scheduled_job():
    print('This job is run every weekday at 10am.')

sched.configure(options_from_ini_file)
sched.start()

Update:更新:

apscheduler documentation . apscheduler 文档

This for apscheduler-3.3.1 on Python 3.6.2 .这适用于Python 3.6.2上的apscheduler-3.3.1

"""
Following configurations are set for the scheduler:

 - a MongoDBJobStore named “mongo”
 - an SQLAlchemyJobStore named “default” (using SQLite)
 - a ThreadPoolExecutor named “default”, with a worker count of 20
 - a ProcessPoolExecutor named “processpool”, with a worker count of 5
 - UTC as the scheduler’s timezone
 - coalescing turned off for new jobs by default
 - a default maximum instance limit of 3 for new jobs
"""

from pytz import utc
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from apscheduler.executors.pool import ProcessPoolExecutor

"""
Method 1:
"""
jobstores = {
    'mongo': {'type': 'mongodb'},
    'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')
}
executors = {
    'default': {'type': 'threadpool', 'max_workers': 20},
    'processpool': ProcessPoolExecutor(max_workers=5)
}
job_defaults = {
    'coalesce': False,
    'max_instances': 3
}

"""
Method 2 (ini format):
"""
gconfig = {
    'apscheduler.jobstores.mongo': {
        'type': 'mongodb'
    },
    'apscheduler.jobstores.default': {
        'type': 'sqlalchemy',
        'url': 'sqlite:///jobs.sqlite'
    },
    'apscheduler.executors.default': {
        'class': 'apscheduler.executors.pool:ThreadPoolExecutor',
        'max_workers': '20'
    },
    'apscheduler.executors.processpool': {
        'type': 'processpool',
        'max_workers': '5'
    },
    'apscheduler.job_defaults.coalesce': 'false',
    'apscheduler.job_defaults.max_instances': '3',
    'apscheduler.timezone': 'UTC',
}

sched_method1 = BlockingScheduler() # uses overrides from Method1
sched_method2 = BlockingScheduler() # uses same overrides from Method2 but in an ini format


@sched_method1.scheduled_job('interval', seconds=10)
def timed_job():
    print('This job is run every 10 seconds.')


@sched_method2.scheduled_job('cron', day_of_week='mon-fri', hour=10)
def scheduled_job():
    print('This job is run every weekday at 10am.')


sched_method1.configure(jobstores=jobstores, executors=executors, job_defaults=job_defaults, timezone=utc)
sched_method1.start()

sched_method2.configure(gconfig=gconfig)
sched_method2.start()

the simplest option I can suggest is using the schedule library.我可以建议的最简单的选择是使用计划库。

In your question, you said "I will need to run a function once every hour" the code to do this is very simple:在您的问题中,您说“我需要每小时运行一次函数”,执行此操作的代码非常简单:

    import schedule

    def thing_you_wanna_do():
        ...
        ...
        return


    schedule.every().hour.do(thing_you_wanna_do)

    while True:
        schedule.run_pending()

you also asked how to do something at a certain time of the day some examples of how to do this are:您还询问了如何在一天中的某个时间做某事,一些示例如下:

    import schedule


    def thing_you_wanna_do():
        ...
        ...
        return


    schedule.every().day.at("10:30").do(thing_you_wanna_do)
    schedule.every().monday.do(thing_you_wanna_do)
    schedule.every().wednesday.at("13:15").do(thing_you_wanna_do)
    # If you would like some randomness / variation you could also do something like this
    schedule.every(1).to(2).hours.do(thing_you_wanna_do)

    while True:
        schedule.run_pending()

90% of the code used is the example code of the schedule library.使用的代码90%是调度库的示例代码。 Happy scheduling!安排愉快!

   #For scheduling task execution
import schedule
import time

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

schedule.every(1).minutes.do(job)
#schedule.every().hour.do(job)
#schedule.every().day.at("10:30").do(job)
#schedule.every(5).to(10).minutes.do(job)
#schedule.every().monday.do(job)
#schedule.every().wednesday.at("13:15").do(job)
#schedule.every().minute.at(":17").do(job)

while True:
    schedule.run_pending()
    time.sleep(1)

Run the script every 15 minutes of the hour.每小时每 15 分钟运行一次脚本。 For example, you want to receive 15 minute stock price quotes, which are updated every 15 minutes.例如,您希望接收 15 分钟的股票报价,每 15 分钟更新一次。

while True:
    print("Update data:", datetime.now())
    sleep = 15 - datetime.now().minute % 15
    if sleep == 15:
        run_strategy()
        time.sleep(sleep * 60)
    else:
        time.sleep(sleep * 60)

On the version posted by sunshinekitty called "Version < 3.0" , you may need to specify apscheduler 2.1.2 .在 sunkitty 发布的名为 "Version < 3.0" 的版本上,您可能需要指定 apscheduler 2.1.2 。 I accidentally had version 3 on my 2.7 install, so I went:我不小心在我的 2.7 安装中安装了版本 3,所以我去了:

pip uninstall apscheduler
pip install apscheduler==2.1.2

It worked correctly after that.之后它工作正常。 Hope that helps.希望有帮助。

The Python standard library does provide sched andthreading for this task. Python 标准库确实为此任务提供了调度线程 But this means your scheduler script will have be running all the time instead of leaving its execution to the OS, which may or may not be what you want.但这意味着您的调度程序脚本将一直运行,而不是将其执行留给操作系统,这可能是您想要的,也可能不是您想要的。

Probably you got the solution already @lukik, but if you wanna remove a scheduling, you should use:可能你已经得到了@lukik 的解决方案,但是如果你想删除一个调度,你应该使用:

job = scheduler.add_job(myfunc, 'interval', minutes=2)
job.remove()

or或者

scheduler.add_job(myfunc, 'interval', minutes=2, id='my_job_id')
scheduler.remove_job('my_job_id')

if you need to use a explicit job ID如果您需要使用明确的作业 ID

For more information, you should check: https://apscheduler.readthedocs.io/en/stable/userguide.html#removing-jobs有关更多信息,您应该查看: https ://apscheduler.readthedocs.io/en/stable/userguide.html#removing-jobs

I found that scheduler needs to run the program every second.我发现调度程序需要每秒运行一次程序。 If using a online server it would be costly.如果使用在线服务器,成本会很高。 So I have following:所以我有以下几点:

It run at each minute at the 5th second, and you can change it to hours days by recalculating waiting period in seconds它在第 5 秒的每分钟运行一次,您可以通过重新计算等待时间(以秒为单位)将其更改为小时天

import time
import datetime
Initiating = True
print(datetime.datetime.now())
while True:
    if Initiating == True:
        print("Initiate")
        print( datetime.datetime.now())
        time.sleep(60 - time.time() % 60+5)
        Initiating = False
    else:
        time.sleep(60)
        print("working")
        print(datetime.datetime.now())

This method worked for me using relativedelta and datetime and a modulo boolean check for every hour.这种方法对我有用,它使用 relativedelta 和 datetime 以及每小时的模布尔检查。 It runs every hour from the time you start it.从您启动它开始,它每小时运行一次。

import time
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta

 #Track next run outside loop and update the next run time within the loop   
    nxt_run=datetime.now()

#because while loops evaluate at microseconds we basically need to use a boolean evaluation to track when it should run next
    while True:
        cnow = datetime.now() #track the current time
        time.sleep(1) #good to have so cpu doesn't spike
        if (cnow.hour % 1 == 0 and cnow >= nxt_run):
           print(f"start @{cnow}: next run @{nxt_run}")
           nxt_run=cnow+relativedelta(hours=1) #add an hour to the next run
        else:
           print(f"next run @{nxt_run}")

clock.py时钟.py

from apscheduler.schedulers.blocking import BlockingScheduler
import pytz

sched = BlockingScheduler(timezone=pytz.timezone('Africa/Lagos'))

@sched.scheduled_job('cron', day_of_week='mon-sun', hour=22)
def scheduled_job():
    print('This job is run every week at 10pm.')
    #your job here


sched.start()

Procfile档案

clock: python clock.py

requirements.txt要求.txt

APScheduler==3.0.0

After deployment, the final step is to scale up the clock process.部署后,最后一步是扩展时钟进程。 This is a singleton process, meaning you'll never need to scale up more than 1 of these processes.这是一个单例流程,这意味着您永远不需要扩大超过 1 个这些流程。 If you run two, the work will be duplicated.如果你运行两个,工作将被重复。

$ heroku ps:scale clock=1

Source: https://devcenter.heroku.com/articles/clock-processes-python来源: https ://devcenter.heroku.com/articles/clock-processes-python

Perhaps Rocketry suits your needs.也许Rocketry适合您的需求。 It's a powerful scheduler that is very easy to use, has a lot of built-in scheduling options and it is easy to extend:它是一个功能强大的调度器,非常易于使用,具有很多内置的调度选项,并且易于扩展:

from rocketry import Rocketry
from rocketry.conds import daily, every, after_success

app = Rocketry()

@app.task(every("1 hour 30 minutes"))
def do_things():
    ...    

@app.task(daily.between("12:00", "17:00"))
def do_daily_afternoon():
    ...

@app.task(daily & after_success(do_things))
def do_daily_after_task():
    ...

if __name__ == "__main__":
    app.run()

It has much more though:它还有更多:

  • String based scheduling syntax基于字符串的调度语法
  • Logical statements (AND, OR, NOT)逻辑语句(AND、OR、NOT)
  • A lot of built-in scheduling options很多内置的调度选项
  • Easy to customize (custom conditions, parameters etc.)易于定制(定制条件、参数等)
  • Parallelization (run on separate thread or process)并行化(在单独的线程或进程上运行)
  • Paramatrization (execution order and input-output)参数化(执行顺序和输入输出)
  • Persistence: put the logs anywhere you like持久性:将日志放在您喜欢的任何地方
  • Modify scheduler on runtime (ie. build API on top of it)在运行时修改调度程序(即在其之上构建 API)

Links:链接:

Disclaimer: I'm the author免责声明:我是作者

One option is to write a C/C++ wrapper that executes the python script on a regular basis.一种选择是编写一个定期执行 python 脚本的 C/C++ 包装器。 Your end-user would run the C/C++ executable, which would remain running in the background, and periodically execute the python script.您的最终用户将运行 C/C++ 可执行文件,该可执行文件将继续在后台运行,并定期执行 python 脚本。 This may not be the best solution, and may not work if you don't know C/C++ or want to keep this 100% python.这可能不是最好的解决方案,如果您不了解 C/C++ 或想保留这个 100% python,则可能无法正常工作。 But it does seem like the most user-friendly approach, since people are used to clicking on executables.但这似乎是最用户友好的方法,因为人们习惯于单击可执行文件。 All of this assumes that python is installed on your end user's computer.所有这些都假设 python 安装在您的最终用户的计算机上。

Another option is to use cron job/Task Scheduler but to put it in the installer as a script so your end user doesn't have to do it.另一种选择是使用 cron 作业/任务计划程序,但将其作为脚本放入安装程序中,这样您的最终用户就不必这样做了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM