【案例-自动化报表】mysql数据+python报表和发送邮件

一、思路

1、mysql创建表wy_user,包含用户信息和用户邮箱;
2、mysql创建表wy_user_data,包含用户用电信息;
3、python读取mysql的两个表,形成自动化报表(文字+图);
4、python将形成的报表发送至用户邮箱。

二、执行

1、mysql的user表

CREATE SCHEMA `mysql_python` ;
use mysql_python;
CREATE TABLE `mysql_python`.`wy_user` (
  `id` INT NOT NULL,
  `name` VARCHAR(45) NULL,
  `email_address` VARCHAR(45) NULL,
  PRIMARY KEY (`id`));
INSERT INTO `mysql_python`.`wy_user` (`id`, `name`, `email_address`) VALUES ('1', '张三', 'xxxxxxx@qq.com');
INSERT INTO `mysql_python`.`wy_user` (`id`, `name`, `email_address`) VALUES ('2', '李四', 'xxxxxxx@qq.com');

结果:


image.png

2、mysql的data表

CREATE TABLE `mysql_python`.`wy_user_data` (
  `id` INT NOT NULL,
  `Q1` INT NULL,  `Q2` INT NULL,  `Q3` INT NULL,
  `Q4` INT NULL,  `Q5` INT NULL,  `Q6` INT NULL,
  `Q7` INT NULL,  `Q8` INT NULL,  `Q9` INT NULL,
  `Q10` INT NULL,  `Q11` INT NULL,  `Q12` INT NULL,
  `Q13` INT NULL,  `Q14` INT NULL,  `Q15` INT NULL,
  `Q16` INT NULL,  `Q17` INT NULL, `Q18` INT NULL,
  `Q19` INT NULL, `Q20` INT NULL,  `Q21` INT NULL,
  `Q22` INT NULL,  `Q23` INT NULL, `Q24` INT NULL,
  PRIMARY KEY (`id`));
INSERT INTO `mysql_python`.`wy_user_data` (`id`, `Q1`, `Q2`, `Q3`, `Q4`, `Q5`, `Q6`, `Q7`, `Q8`, `Q9`, `Q10`, `Q11`, `Q12`, `Q13`, `Q14`, `Q15`, `Q16`, `Q17`, `Q18`, `Q19`, `Q20`, `Q21`, `Q22`, `Q23`, `Q24`) VALUES ('1', '200', '322', '450', '511', '645', '111', '234', '564', '1234', '1234', '452', '1234', '1234', '102', '046', '1023', '1054', '503', '432', '346', '275', '345', '456', '880');
INSERT INTO `mysql_python`.`wy_user_data` (`id`, `Q1`, `Q2`, `Q3`, `Q4`, `Q5`, `Q6`, `Q7`, `Q8`, `Q9`, `Q10`, `Q11`, `Q12`, `Q13`, `Q14`, `Q15`, `Q16`, `Q17`, `Q18`, `Q19`, `Q20`, `Q21`, `Q22`, `Q23`, `Q24`) VALUES ('2', '30', '246', '304', '241', '0054', '204', '213', '459', '42', '1234', '105', '2014', '243', '895', '940', '1054', '234', '1247', '243', '220', '189', '204', '36', '55');

结果:


image.png

3、python形成报表

# -*- coding:utf-8 -*-
import pymysql
import pandas as pd
from docx import Document
from docx.shared import Inches
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_TAB_ALIGNMENT, WD_TAB_LEADER
import time
import seaborn as sns
import matplotlib.pyplot as plt

# 取表(定义函数一直有点问题,所以就不定义了)
# 连接数据库
db = pymysql.connect("xxx.mysql.rds.aliyuncs.com", "xxx", "xxx", "mysql_python")
cursor = db.cursor()
sql1 = 'select * from wy_user'
sql2 = 'select * from wy_user_data'
cursor.execute(sql1)
user = cursor.fetchall()
columnDes = cursor.description  # 获取描述信息
columnNames = [columnDes[i][0] for i in range(len(columnDes))]
user_df = pd.DataFrame([list(i) for i in user], columns=columnNames)
user_df.set_index('id', inplace=True)

cursor.execute(sql2)
user_data = cursor.fetchall()
columnDes = cursor.description  # 获取描述信息
columnNames = [columnDes[i][0] for i in range(len(columnDes))]
user_data_df = pd.DataFrame([list(i) for i in user_data], columns=columnNames)
user_data_df.set_index('id', inplace=True)

# python报表

plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

for id in range(2):
    #数据准备
    name = user_df.iloc[id]['name']
    all_value = user_data_df.iloc[id].sum()
    high_value = user_data_df.iloc[id][7:11].sum() + user_data_df.iloc[id][19:23].sum()
    flat_value = user_data_df.iloc[id][11:19].sum()
    valley_value = user_data_df.iloc[id].sum() - high_value - flat_value
    #图片准备
    fig, ax = plt.subplots(figsize=(10, 5))
    plt.title("Electricity-Quantity Distribution", fontsize=15)
    plt.xticks(rotation=90)
    plt.xlabel('Time Scale', fontsize=12)
    plt.ylabel('Volumn(kWh)', fontsize=12)
    sns.barplot(x=list(user_data_df.columns), y=list(user_data_df.iloc[id]))
    plt.savefig('/Users/ranmo/Desktop/数据分析案例/自动化报表/%d.jpg' % (id + 1))

    # 创建文档
    document = Document()
    # 标题
    year = int(time.strftime('%Y', time.localtime()))
    month = int(time.strftime('%m', time.localtime())) - 1

    title = ('%d年%d月客户用电分析报告' % (year, month))
    document.add_heading(title, 0)

    # 开头
    head = document.add_heading('尊敬的  ', level=1)
    head.add_run(name).italic = True
    head.add_run('  客户:')

    # 正文

    p = document.add_paragraph('    您好!')
    p.add_run('您%d年%d月的总用电量为%d千瓦时,其中峰、平、谷段的电量分别为:%d千瓦时、%d千瓦时以及%d千瓦时。具体用电情况如下图所示:' % (
    year, month, all_value, high_value, flat_value, valley_value))
    document.add_picture('/Users/ranmo/Desktop/数据分析案例/自动化报表/%d.jpg' % (id + 1), width=Inches(6))
    document.add_paragraph('')
    document.add_paragraph('')
    document.add_paragraph('')

    a = document.add_paragraph()
    a.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.RIGHT  # 段落文字居右设置
    a.add_run('wy公司')

    b = document.add_paragraph()
    b.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.RIGHT  # 段落文字居右设置
    b.add_run('%d年%d月01日' % (year, (month+1)))

    document.save('/Users/ranmo/Desktop/数据分析案例/自动化报表/%d_%s_报告.docx'%(id+1,name))

python生成word:
https://python-docx.readthedocs.io/en/latest/
https://www.cnblogs.com/xiao987334176/p/9995976.html

(本来还想把word转成pdf再发送的,但是api好像都是win环境的)

4、定时发送用户邮箱
a.python代码发送用户邮箱:

# -*- coding:utf-8 -*-

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header

import pymysql
import pandas as pd

db = pymysql.connect("xxx.mysql.rds.aliyuncs.com", "xxx", "xxx", "mysql_python")
cursor = db.cursor()
sql1 = 'select * from wy_user'
cursor.execute(sql1)
user = cursor.fetchall()
columnDes = cursor.description  # 获取描述信息
columnNames = [columnDes[i][0] for i in range(len(columnDes))]
user_df = pd.DataFrame([list(i) for i in user], columns=columnNames)
user_df.set_index('id', inplace=True)
cursor.close()


for id in range(2):
    my_sender = 'xxx'
    my_pass = 'xxx'
    my_user = user_df.iloc[id]['email_address']  # 接收邮件
    name = user_df.iloc[id]['name']

    #创建一个带附件的实例
    message = MIMEMultipart()
    message['From'] = Header("Arthur", 'utf-8')
    message['To'] =  Header(name, 'utf-8')
    subject = '客户用电分析报告'
    message['Subject'] = Header(subject, 'utf-8')

    #邮件正文内容
    message.attach(MIMEText('您好!这是您上月的用电分析报告,请查收!', 'plain', 'utf-8'))

    # 构造附件1,传送当前目录下的 test.txt 文件
    att1 = MIMEText(open('/Users/ranmo/Desktop/数据分析案例/自动化报表/%d_%s_报告.docx'%(id+1,name), 'rb').read(), 'base64', 'utf-8')
    att1["Content-Type"] = 'application/octet-stream'
    # 这里的filename可以任意写,写什么名字,邮件中显示什么名字
    att1.add_header("Content-Disposition", "attachment", filename=("utf-8", "", "%d_%s_报告.docx"%(id+1,name)))
    message.attach(att1)



    try:
        server=smtplib.SMTP_SSL("smtp.qq.com", 465)  # 发件人邮箱中的SMTP服务器,端口是465
        server.login(my_sender, my_pass)  # 括号中对应的是发件人邮箱账号、邮箱密码
        server.sendmail(my_sender,my_user,message.as_string())  # 括号中对应的是发件人邮箱账号、收件人邮箱账号、发送邮件
        server.quit()  # 关闭连接
        print ("邮件发送成功")
    except smtplib.SMTPException:
        print ("Error: 无法发送邮件")

python发送邮件:
https://www.runoob.com/python3/python3-smtp.html

b.编写定时执行脚本

#!/bin/bash
# -*- coding: utf-8 -*-
cd /Users/ranmo/Desktop/数据分析案例/自动化报表
python3 auto-word.py
python3 Email-Sending.py

* * * * *  /Users/ranmo/Desktop/数据分析案例/自动化报表/auto-word-sending.sh

crontab定时器:
https://blog.csdn.net/ty_hf/article/details/72354230
http://www.shanhuxueyuan.com/news/detail/118.html

ps:写crontab脚本的时候,遇到环境变量配置问题,最后发展成了“拯救环境变量三连”:
1、export PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
2、open -e ~/.bash_profile
3、source ~/.bash_profile
结论就是,有的路径不能往配置里面写。。不然会导致崩溃。
https://blog.csdn.net/haishen111/article/details/88867968

我写好了脚本,可以直接拖到终端执行,但是crontab提示“python3: command not found”,查询了半天,可能是因为crontab里面调用py,必须指定解释器的路径
https://www.cnblogs.com/zhhiyp/p/10160754.html
原执行脚本修改为:

#!/bin/bash
# -*- coding: utf-8 -*-
cd /Users/ranmo/Desktop/数据分析案例/自动化报表
/Users/ranmo/anaconda3/bin/python3 auto-word.py
/Users/ranmo/anaconda3/bin/python3 Email-Sending.py

搞定!

三、结果
1、收到邮件


image.png

2、邮件附件


image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

  • # Python 资源大全中文版 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列...
    小迈克阅读 2,961评论 1 3
  • 一、Python简介和环境搭建以及pip的安装 4课时实验课主要内容 【Python简介】: Python 是一个...
    _小老虎_阅读 5,719评论 0 10
  • 更改ip和dnsVi /etc/sysconfig/network-scripts/ifcfg-eth0vi /...
    Xwei_阅读 1,800评论 0 3
  • 祝各位朋友腊八节快乐! 明天开始会有一段时间不能画画,因为我的小二宝就要出生了。人生就像盒子里的巧克力糖,你...
    F56春雨sunny阅读 596评论 0 3
  • 01 今天,被18岁刷屏了。 原来,最后一批的90后们已经18岁了,步入成年时代,而00后们又将开始粉墨登场。 不...
    P学长阅读 1,208评论 11 32