任务需求:在flask中每天自动同步大众点评数据到数据库,这里选择了Flask-APScheduler
APScheduler基于Quartz的一个Python定时任务框架,实现了Quartz的所有功能,使用起来十分方便。提供了基于日期、固定时间间隔以及crontab类型的任务,并且可以持久化任务。基于这些功能,我们可以很方便的实现一个python定时任务系统。
APScheduler有四种组成部分:
APScheduler提供了多种调度器,可以根据具体需求来选择合适的调度器,常用的调度器有:
BlockingScheduler:适合于只在进程中运行单个任务的情况,通常在调度器是你唯一要运行的东西时使用。
BackgroundScheduler: 适合于要求任何在程序后台运行的情况,当希望调度器在应用后台执行时使用。
AsyncIOScheduler:适合于使用asyncio框架的情况
GeventScheduler: 适合于使用gevent框架的情况
TornadoScheduler: 适合于使用Tornado框架的应用
TwistedScheduler: 适合使用Twisted框架的应用
QtScheduler: 适合使用QT的情况
pip install apscheduler
pip install Flask-APScheduler
import time
from apscheduler.schedulers.blocking import BlockingScheduler
sched = BlockingScheduler()
def work1():
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))
@sched.scheduled_job('interval', seconds=2)
def work2():
print('work2222')
sched.add_job(work1, 'interval', seconds=5)
sched.start()
id
# id代表该job唯一标识,不可重复,之后可以用id查找job
sched.add_job(work1, 'interval', seconds=5, id='my_work1')
trigger
它管理着作业的调度方式。它可以为date, interval或者cron。对于不同的trigger,对应的参数也不同。
后面可以写的参数:
#表示每隔3天17时19分07秒执行一次任务
sched.add_job(my_job, 'interval',days=3,hours=17,minutes=19,seconds=7)
# The job will be executed on November 6th, 2009
sched.add_job(func=work1, trigger='date', run_date=date(2009, 11, 6), args=['text'])
# The job will be executed on November 6th, 2009 at 16:30:05
sched.add_job(func=work1, trigger='date', run_date=datetime(2009, 11, 6, 16, 30, 5), args=['text'])
后面参数和interval大致相同
# 表示2017年3月22日17时19分07秒执行该程序
sched.add_job(work1, 'cron', year=2017, month=3, day=22, hour=17, minute=19, second=7)
# 表示任务在6,7,8,11,12月份的第三个星期五的00:00,01:00,02:00,03:00 执行该程序
sched.add_job(work1, 'cron', month='6-8,11-12', day='3rd fri', hour='0-3')
# 表示从星期一到星期五5:30(AM)直到2014-05-30 00:00:00
sched.add_job(work1, 'cron', day_of_week='mon-fri', hour=5, minute=30, end_date='2014-05-30')
# 表示每5秒执行该程序一次,相当于interval 间隔调度中seconds = 5
sched.add_job(work1, 'cron', second='*/5')
args 参数,要可迭代对象
# 移除所有,移除要放在start之前才有效
sched.remove_all_jobs()
# 根据id移除job
sched.remove_job('my_work1')
sched.pause_job('my_work1') # 暂停
sched.resume_job('my_work1') # 恢复
sched.get_job('my_work1') # 获取
sched.get_jobs() # 获取所有job列表
# 默认情况是调度器等所有job完成后关闭,设为False时直接关闭
sched.shutdown(wait=False)
flask的项目结构是util目录下有个dianpingUtil.py文件,文件里有个函数dianPing()是要执行的定时任务
import hashlib
import time
import json
from http import client
from urllib.parse import urlencode
from model.ZLHModel import ZlhUserFinanceDianping
def dianPing(current_app):
data_type = 1
today_date = time.strftime("%Y-%m-%d", time.localtime())
_where = (ZlhUserFinanceDianping.ufd_date == today_date) & (ZlhUserFinanceDianping.ufd_data_type == data_type)
# 如果已存在则不访问
user_finance_dianping_count = ZlhUserFinanceDianping.select().where(_where).count()
if not user_finance_dianping_count:
host = 'openapi.dianping.com'
appsecret = current_app.config['DIANPING_APPSECRET']
time_stamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
params = {'app_key': current_app.config['DIANPING_APPKEY'], 'timestamp': time_stamp,
'session': current_app.config['DIANPING_SESSION'], 'format': "json", 'v': "1",
'sign_method': "MD5",
'open_shop_uuid': current_app.config['DIANPING_OPEN_SHOP_UUID'], 'date_type': str(data_type)}
signstr = sign(params, appsecret, 'MD5')
params['sign'] = signstr
encode_params = urlencode(params)
request_url = "https://openapi.dianping.com/router/merchant/data/consumption"
header = {"Content-type": "application/x-www-form-urlencoded ; charset=UTF-8"}
conn = client.HTTPConnection(host)
conn.request(method="POST", url=request_url, headers=header, body=encode_params)
response = conn.getresponse()
res = response.read()
result = json.loads(res)
if result.get('code') == 200:
for data in result.get('data'):
ZlhUserFinanceDianping.create(ufd_tg_consume_amount=data.get('tg_consume_amount') * 100,
ufd_tg_consume_count=data.get('tg_consume_count'),
ufd_mopay_consume_amount=data.get('mopay_consume_amount') * 100,
ufd_mopay_consume_count=data.get('mopay_consume_count'),
ufd_reservation_consume_amount=data.get(
'reservation_consume_amount') * 100,
ufd_reservation_consume_count=data.get('reservation_consume_count'),
ufd_platform=data.get('platform'),
ufd_date=today_date, ufd_data_type=data_type,
)
current_app.logger.info('大众点评完成同步')
else:
current_app.logger.info('大众点评返回结果错误' + result.get('msg'))
else:
current_app.logger.info('点评当日已保存')
def sign(param, appsecret, signmethod):
if signmethod !="MD5":
return ''
lists = []
param_str = appsecret
for item in param:
lists.append(item)
lists.sort()
for key in lists:
param_str = param_str + key + param[key]
param_str += appsecret
param_str = param_str.strip()
return genMd5(param_str)
def genMd5(str):
md5 = hashlib.md5()
md5.update(str.encode("utf8"))
md5.hexdigest()
return md5.hexdigest()
SCHEDULER_API_ENABLED = True
# 添加需要的配置
# 大众点评
DIANPING_APPSECRET = '***'
DIANPING_APPKEY = '***'
DIANPING_OPEN_SHOP_UUID = '***'
DIANPING_SESSION = '***'
DIANPING_REFRESH_SESSION = '**'
这里的重点是定时任务需要用到flask app上下文,所以直接将app作为参数传过去即可
from flask import Flask
from flask_apscheduler import APScheduler
... # 省略导入配置文件
app = Flask(__name__, template_folder='templates', static_url_path='/static')
# 注册APScheduler,添加任务
scheduler = APScheduler()
scheduler.init_app(app)
# 表示一天后开始执行,然后每天执行一次
scheduler.add_job(id='dianping', func='util.DianpingUtil:dianPing',
trigger='interval', day=1, args=[app, ])
scheduler.start()
app.config.from_object(app_config[‘dev’]) # 配置文件
# 省略
...