自动化系列(三)Python实现定时邮件

自动化系列(三)Python实现定时邮件

在日常数据交付中,定时邮件是必不可少的。一般企业的数仓会开发出相关平台供分析师使用,但仅限于SQL语言,虽然大多数场景下足够了,但难免碰到一些复杂的需求需要SQL查询+Python处理,这个时候就需要自定义的定时邮件了。

正所谓技多不压身,本文教大家如何通过PySpark+Crontab完成企业级的定时邮件

⚠️注意:以下需要在企业服务器上的jupyter上操作,本地jupyter是无法连接企业hive集群的。考虑到不是所有同学当前都有企业集群资源,附赠一个本地python实现定邮案例帮助上手。

PySpark数据处理

#!/usr/bin/env
# -*- coding: utf-8 -*-

import sys
import traceback
import pandas as pd
import datetime
from pyspark.sql import SparkSession
from pyspark import SparkContext
from pyspark import HiveContext
from pyspark import SparkConf

import smtplib
import email.mime.multipart
import email.mime.text
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication

# // spark setup
spark = SparkSession.builder.enableHiveSupport().getOrCreate()
conf = SparkConf().setMaster('yarn').setAppName("mail")
conf.set("spark.executor.memory", "20g")
conf.set("spark.driver.memory", "12g")
conf.set("spark.driver.maxResultSize", "10g")
conf.set("spark.executor.cores", "12")
conf.set("spark.num.executors", "800")
conf.set("spark.kryoserializer.buffer.max", "1024")
conf.set("spark.executor.memoryOverhead", "9216")
conf.set("spark.maxResult", "1024000")
conf.set("spark.rpc.message.maxSize", "1024000")
conf.set("spark.sql.legacy.allowCreatingManagedTableUsingNonemptyLocation", "true")
conf.set("spark.sql.sources.partitionOverwriteMode", "dynamic")
# conf.set("spark.sql.autoBroadcastJoinThreshold", "-1")

sc = SparkContext.getOrCreate(conf)
sqlContext = HiveContext(sc)


# 数据查询语句
sql_f = '''
select
    date_add(current_date,-1) as dt 
    ,user
    ,count(distinct t.id) as nums
from
    (
        select
            taskid
        from 
            temp.hh_operation_log
        where
            to_date(dbctime) = date_add(current_date,-1)
    )ol 
join
    (
        select
            id 
            ,user
        from
            temp.hh_task_da
    )t on ol.taskid=t.id 
group by 
    user
'''

# 生成csv文件,为了简洁,后面不增加Python处理过程
df = sqlContext.sql(sql_f).toPandas()
df.to_csv('每日工作量.csv',index=None)


# 定义邮件函数
def send_email_part (sendAddr, password, recipientAddrs, subject='', content=''):
    msg = email.mime.multipart.MIMEMultipart()
    recipients = recipientAddrs
    emaillist = [elem.strip().split(',') for elem in recipients]
    msg['From'] = sendAddr
    msg['Subject'] = subject
    
    # 文字描述内容
    text_part = '''
    数据详见附件
    如有问题,请联系***
    数据生成日期:%s
    ''' % (datetime.datetime.now().strftime('%Y-%m-%d'))
    msg.attach(MIMEText(text_part, 'plain', 'utf-8'))
    
    # 添加附件
    att1 = MIMEApplication(open('每日工作量.csv', 'rb').read())
    att1.add_header('Content-Disposition', 'attachment', filename=('%s 每日工作量.csv' % (datetime.datetime.now().strftime('%Y-%m-%d'))))
    msg.attach(att1)
    
    smtp = smtplib.SMTP_SSL('smtp.exmail.qq.com', 465) # 腾讯企业邮箱的SMTP协议
    smtp.login(sendAddr, password)
    smtp.sendmail(msg['From'], emaillist, str(msg))
    smtp.quit()

recipients_ldap = ['****@****.com','****@163.com'] # 收件人邮箱列表(企业邮箱、个人邮箱均可)

try:
    recipients = recipients_ldap
    sendAddr ='****@****.com' # 你的企业邮箱
    password = '****' # 你的企业邮箱的授权码
    subject=('测试数据')
    send_email_part(sendAddr, password, recipients, subject)
except Exception as err:
    print('Error: ')
    print(err)

将上述代码保存为works.py,并上传到企业服务器自己的文件夹内,例如本文的/home/hh下。

Crontab设置定时任务

通过crontab -e进入当前用户vim编辑界面。输入以下指令45 10 * * * source /home/maintain/.bashrc; python /home/hh/works.py > /home/hh/works.log 2>&1esc退出编辑界面,按shift+:后输入wq保存退出即可。

💡建议:简单的crontab命令可参考Linux Crontab 定时任务

45 10 * * *:表示每天早上10点45执行后续操作

source /home/maintain/.bashrc;:重启.bashrc文件更新环境变量并生效

python /home/hh/works.py:执行/home/hh/路径下的works.py文件

> /home/hh/works.log 2>&1:覆盖写入/home/hh/路径下的works.log文件,没有则新建后写入。其中2>&1表示不仅终端正常信息的输出保存到works.log文件中,产生错误信息的输出也保存到works.log文件中

定邮案例-每日一句

由于读者中并不是都拥有企业服务器的权限或资源,因此这里分享一个简单的本地定邮案例:通过qq个人邮箱每日推送一句名言。

定邮py脚本

import json
import emoji
import requests
from lxml import etree 
import random
import datetime
from bs4 import BeautifulSoup
import numpy as np
import smtplib
import email.mime.multipart
import email.mime.text
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication

# 获取城市code
def get_city_code(city):
    response = requests.get(url="http://toy1.weather.com.cn/search?cityname=" + city)
    res = response.content.decode('utf-8')
    city_info = eval(res)[0]["ref"]
    city_code = city_info.split("~")[0]
    
    return city_code

# 每日天气
def get_weather(city_code):
    url = f'http://www.weather.com.cn/weather/{city_code}.shtml'
    req = requests.get(url) 
    req.encoding = 'utf-8' 
    soup = BeautifulSoup(req.text, 'html.parser')
    ul_tag = soup.find('ul', 't clearfix')
    li_tag = ul_tag.findAll('li')[0]         #获取当日数据
    
    # 获取天气、低温、高温、风力
    weat = li_tag.find('p', 'wea').string
    low_tem = li_tag.find('p', 'tem').find('i').string if li_tag.find('p', 'tem').find('i') else None
    high_tem = li_tag.find('p', 'tem').find('span').string if li_tag.find('p', 'tem').find('span') else None
    wind = li_tag.find('p', 'win').find('i').string
    
    return weat,low_tem,high_tem,wind

# 每日一句
def get_one_line():
    get_request = requests.get(url='https://v.api.aa1.cn/api/yiyan/index.php')
    html = etree.HTML(get_request.text)
    text = html.xpath('/html/body/p/text()')[0]
    
    return text

# 主函数 输出结果
def main(city):
    # 获取日期
    date = datetime.datetime.now().strftime('%Y-%m-%d')
    # 获取每日一句
    one_line = get_one_line()
    # 获取天气信息
    city_code = get_city_code(city)
    gw = get_weather(city_code)
    weat = gw[0]
    wind = gw[-1]
    
    if gw[1] is None or gw[2] is None:
        tem = gw[1] or gw[2]
    else:
        tem = gw[1] + '~' + gw[2]
    
    # 格式化字符串
    out_format = " %s \n \n |日期:%s \n |坐标: %s\n |天气: %s\n |温度:%s\n |风力:%s \n \n 微信搜索HsuHeinrich,发现更多精彩👍"
    out_str = out_format % (one_line, date, city, weat, tem, wind)
    return out_str


# 邮件发送
def send_email_part (sendAddr, password, recipientAddrs, city, subject='', content=''):
    msg = email.mime.multipart.MIMEMultipart()
    recipients = recipientAddrs
    emaillist = [elem.strip().split(',') for elem in recipients]
    msg['From'] = sendAddr
    msg['Subject'] = subject
    
    # 文字描述内容
    text_part = main(city)
    msg.attach(MIMEText(text_part, 'plain', 'utf-8'))
    
    smtp = smtplib.SMTP_SSL('smtp.qq.com', 465) ## qq邮箱的SMTP协议
    smtp.login(sendAddr, password)
    smtp.sendmail(msg['From'], emaillist, str(msg))
    smtp.quit()

city = '北京'
recipients_ldap = ['****@qq.com'] # 收件人邮箱列表(企业邮箱、个人邮箱均可)

try:
    recipients = recipients_ldap
    sendAddr ='****@qq.com' # 你的qq邮箱
    password = '****' # 你的qq邮箱的授权码
    subject=(f'早安!{city}')
    send_email_part(sendAddr, password, recipients, city, subject)
except Exception as err:
    print('Error: ')
    print(err)


print('succeed:' + str(datetime.datetime.today()))

将以上文件生成oneLine.py文件,放到指定文件夹下,例如桌面。

配置crontab定时任务

mac在执行定时任务时,存在一些权限问题,需要手动配置下。

  1. 增加crontab权限
# 打开.vimrc文件进入vim编辑界面
sudo vim ~/.vimrc 

# 添加如下内容并保存
autocmd filetype crontab setlocal nobackup nowritebackup
  1. 添加cron磁盘访问权限

    • 打开Mac系统偏好设置-安全性与隐私,找到完全磁盘访问权限,解锁后点击下边的+号,按shift+command+G前往usr/sbin找到cron文件,点击打开将其加入到隐私列表中。

      image-20221201232245159

    • 在隐私列表中点选crontab、终端、iTerm.app(如果你没有安装iterm2则忽略)的权限

      1

  2. 创建crontab定时任务

    在终端中输入crontab -e进入vim编辑器界面,输入以下内容后退出保存。

    ⚠️注意1:vim的退出保存步骤,按esc进入退出编辑,按shift+:后输入wq保存退出

    ⚠️注意2:如果你没有配置zsh,则source ~/.bashrc;

    20 11 * * * source ~/.zshrc; cd /Users/heinrich/Desktop/; python oneLine.py > oneLine.log 2>&1
    

    如果想停止定时任务的话,只需删除crontab的任务即可。

    在终端中输入crontab -e进入vim编辑器界面,删除刚才的内容后保存退出。

然后你在每天早上就可以收到一封早安邮件了,参考如下图

image-20221202113117948

总结

定邮能很好地解决复杂的定期数据需求,如果邮件配置存在问题,例如非常见的企业邮箱的SMTP协议,可以寻求数仓或者DBA同学的帮助,因为他们需要日常进行告警,所以对于配置定邮、短信、工作群机器人是门清的。

共勉~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值