一、概览
在本文中,我们在原代码的基础上进行了一定的模块拆分,并处理了以上两个问题
原文链接:
二、模块重新分区
1、 将 my_job.py 文件添加到单独的任务模块中
之前的定时任务是由apscheduler库完成的,而任务类在main函数所在的py文件中,导致修改py主文件比较困难。
2、 添加了 util.py 文件
包含公共方法,例如将当前字典转为字符串
3.,新的 weather_service.py 文件
主要负责构建windows服务,也是一个py主文件,与第一篇文章中的主py文件weather_report.py不同。这是我们已经实现的两种计划任务,可以单独运行。如果要通知微信好友天气信息,启动weather_report.py,如果是发送邮件,直接安装weather_service.py作为windows服务,启动,记住需要配置的任务列表运行,下面将描述如何配置任务
4、 添加了timing_task.py 文件
包含任务方法executeJob(),主要在服务中循环运行,然后在合适的时间爬取天气,发送到指定邮箱。任务的参数通过配置json字符串实现
三、优化定时任务
本文的定时任务是在windows服务中运行的,所以我们首先需要安装pywin32模块
1.,安装pywin32
pip install pywin32
2.、服务操作相关命令
1.安装服务 python PythonService.py install
2.让服务自动启动 python PythonService.py --startup auto install
3.启动服务 python PythonService.py start
4.重启服务 python PythonService.py restart
5.停止服务 python PythonService.py stop
6.删除/卸载服务 python PythonService.py remove
3.,启动服务时拒绝
Installing service timingTaskDaemon
Error installing service: 拒绝访问。 (5)
一个。大部分原因是由于python环境的配置造成的。python默认安装时配置的pah是用户环境变量。这里我们需要将其更改为系统环境变量。具体请参考Python编写windows服务并启动服务。错误1053:服务不可用及时响应启动或控制请求
湾。考虑命令行是否有权限,我自己的win8系统默认权限不够,需要右键管理员启动
4、 要实现windows服务功能,我们需要继承win32serviceutil.ServiceFramework类,将要执行的业务逻辑放到SvcDoRun函数中。下面代码中的executeJob()函数就是我们定期执行的任务
class WeatherPythonService(win32serviceutil.ServiceFramework):
_svc_name_ = "weather_service_test4"
_svc_display_name_ = "weather_service_test4"
_svc_description_ = "i am a test weather_service_test"
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
# Create an event which we will use to wait on.
# The "service stop" request will set this event.
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
self.run = True
def SvcStop(self):
# Before we do anything, tell the SCM we are starting the stop process.
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
# And set my event.
win32event.SetEvent(self.hWaitStop)
self.run = False
def SvcDoRun(self):
#what to do#
while self.run:
executeJob()
time.sleep(5)
#win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)
if __name__ == '__main__':
#executeJob()
win32serviceutil.HandleCommandLine(WeatherPythonService)
5、任务执行函数
def executeJob():
now_time = time.localtime(time.time())
now_hour = now_time.tm_hour
now_minute = now_time.tm_min
for job in my_jobs:
ts = job['time']
for t in ts.split(','):
jobtime = t.split('.')
h = jobtime[0]
m = jobtime[1]
if (now_hour != h and now_minute != m):
code = city_code.find_code(job['city'])
wea = getWeath(code)
strWea = strDic(wea)
title = '{}天气预报'.format(job['city'])
send_email(job['receivers'], 'title', title + ":\n" + strWea)
任务执行时,需要配置任务执行列表,也就是上面代码中的my_jobs对象。这个对象是一个标准的json字符串,和上一篇的json格式不同。本文的任务参数如下。总体任务是一个数组。该数组包含任务对象,每个任务对象由3个字段组成,分别是邮件收件人邮箱接收者、爬取城市城市和爬取时间时间
my_jobs = [{
"receivers":['1134024095@qq.com'],
"city":"昌平",
"time":"6.30,17.30"
},{
"receivers":['1134024095@qq.com'],
"city":"海淀",
"time":"6.30,17.30"
}]
6.、安装服务,服务启动成功后,但是任务没有正常执行,可以查看系统任务事件判断错误原因,如下图,这是一个我的故障排除屏幕截图
查询系统日志:win+r回车输入eventvwr.exe回车
四、发送电子邮件
这里我们以QQ邮箱为例进行演示,使用smtplib库发送邮件
1.,QQ邮箱需要申请密码,申请方法
2、 选择邮件发送服务器 smtp.qq.com 和端口号 465
3.,构造发送者、接收者和消息内容
message = MIMEText(text, 'plain', 'utf-8')
message['From'] = formataddr(["就差一点儿", sender])
message['To'] = Header(','.join(receivers), 'utf-8')
message['Subject'] = Header(title, 'utf-8')
text是邮件的内容,发件人信息由From构造,收件人信息由To构造。此构造只是显示的文本字符串,如本节底部屏幕截图所示的收件人和发件人,它真正接受了电子邮件。发送电子邮件时指定的帐号。
4.,连接邮箱服务器,登录
smtpObj = smtplib.SMTP_SSL()
smtpObj.connect(mail_host, mail_port) # mail_port 为 SMTP 端口号
smtpObj.login(mail_user, mail_pass)
5、 发送邮件
smtpObj.sendmail(sender, receivers, message.as_string())
6.,邮件发送成功
7、 完成发送邮件代码
# 三个参数:第一个为文本内容,第二个 plain 设置文本格式,第三个 utf-8 设置编码
def send_email(receivers, title, text):
message = MIMEText(text, 'plain', 'utf-8')
message['From'] = formataddr(["就差一点儿", sender]) # 括号里的对应发件人邮箱昵称、发件人邮箱账号
message['To'] = Header(','.join(receivers), 'utf-8')#接受者
message['Subject'] = Header(title, 'utf-8')
ret = True
try:
smtpObj = smtplib.SMTP_SSL()
smtpObj.connect(mail_host, mail_port) # mail_port 为 SMTP 端口号
smtpObj.login(mail_user, mail_pass)
smtpObj.sendmail(sender, receivers, message.as_string())
except smtplib.SMTPException:
ret = False
f = open('./sendemail_weather.log', 'a', encoding = 'utf-8')
if ret:
f.write(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ':邮件发送成功\n')
else:
f.write(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') +':无法发送邮件\n')
f.close()
8、测试发送邮件
send_email(['1134024095@qq.com','1024068757@qq.com'], "昌平", "6.30")