范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

基于Python的数据导出和邮件发送

  背景
  由于运维工作需要,经常需要将一些数据从数据库中导出,发送给运营和需求部门,天天去手动查询,又有点太费时间了,于是研究学习了Python的基本功能,通过Python脚本和Linux 的crontab命令实现了每天自动化的数据查询和邮件发送。代码实现
  定义了以下几个代码模块实现了配置文件读取、日志记录、数据库连接访问查询、导出到xlsx和带附件的邮件发送功能。
  1、demo.py 示例文件
  2、config.yml 配置文件
  3、common_log.py 实现日志记录
  4、common_db.py 实现数据库连接和访问
  5、common_xlsx.py 实现数据表格的处理
  6、common_email.py 实现带附件的邮件发送1、demo.py 示例# coding: utf-8 import common_db as mydb import common_xlsx as my_xlsx import common_log as mylog import common_email as my_email  if __name__ == "__main__":     phone = "13********7"     sql = """ select orderno,orderAmount,actualAmount,phone order where phone= "{0}" """.format(phone)     # 查询订单     result = mydb.select_by_parameters(sql)     filename = "测试.xlsx"     sheet_name = "订单查询"     # 邮件接收人     receivers = "zhangsan@aliyun.com"     receivers_cc = "lisi@aliyun.com"     # 邮件抄送人     if len(result) > 0:         # 创建工作表         my_xlsx.create_xlsx(filename)         mylog.logger.info("创建工作表成功"+filename)         # 将查询结果放入工作表         my_xlsx.create_sheet_in_xlsx(filename, sheet_name, result,0)         mylog.logger.info("创建工作簿成功"+sheet_name)         # 对工作簿求和         my_xlsx.sum_col_for_sheet(filename, sheet_name)         mylog.logger.info("求和汇总成功")         # 删除空工作表         my_xlsx.delete_sheet_from_xlsx(filename,"Sheet")         # 发送邮件         my_email.to_send_email("测试查询结果", receivers, receivers_cc, filename, "订单查询结果.xlsx")         mylog.logger.info("发送邮件成功")2、config.yml——配置文件mysql:   host: 192.168.x.x   port: 3306   username: xxxx   password: xxxx   database: xxxx log:   log_path: D:log   log_size: 8   log_num: 3 email:   smtp: smtp.126.com   # 发送方邮件地址   from: xxxx@aliyun.com   # 发送方授权码   password: fdsafafdsafsa 3、common_log.py 实现日志记录import logging.handlers import logging import yaml import os import sys  # 提供日志功能 class logger:     # 先读取XML文件中的配置数据     # 由于config.xml放置在与当前文件相同的目录下,因此通过 __file__ 来获取XML文件的目录,然后再拼接成绝对路径     # 这里利用了lxml库来解析XML     # root = etree.parse(os.path.join(os.path.dirname(__file__), "config.xml")).getroot()      # 先读取yml中的配置数据     # 由于     # 读取日志文件保存路径     with open("config.yml", "r") as f:         result = yaml.load(f, Loader=yaml.FullLoader)         config_log = result["log"]      logpath = config_log["log_path"]     # root.find("logpath").text     # 读取日志文件容量,转换为字节     logsize = 1024*1024*int(config_log["log_size"])     # 读取日志文件保存个数     lognum = int(config_log["log_num"])      # 日志文件名:由用例脚本的名称,结合日志保存路径,得到日志文件的绝对路径     logname = os.path.join(logpath, sys.argv[0].split("/")[-1].split(".")[0])+".log"      # 初始化logger     log = logging.getLogger()     # 日志格式,可以根据需要设置     fmt = logging.Formatter("[%(asctime)s][%(filename)s][line:%(lineno)d][%(levelname)s] %(message)s", "%Y-%m-%d %H:%M:%S")      # 日志输出到文件,这里用到了上面获取的日志名称,大小,保存个数     handle1 = logging.handlers.RotatingFileHandler(logname, maxBytes=logsize, backupCount=lognum)     handle1.setFormatter(fmt)     # 同时输出到屏幕,便于实施观察     handle2 = logging.StreamHandler(stream=sys.stdout)     handle2.setFormatter(fmt)     log.addHandler(handle1)     log.addHandler(handle2)      # 设置日志基本,这里设置为INFO,表示只有INFO级别及以上的会打印     log.setLevel(logging.INFO)      # 日志接口,用户只需调用这里的接口即可,这里只定位了INFO, WARNING, ERROR三个级别的日志,可根据需要定义更多接口     @classmethod     def info(cls, msg):         cls.log.info(msg)         return      @classmethod     def warning(cls, msg):         cls.log.warning(msg)         return      @classmethod     def error(cls, msg):         cls.log.error(msg)         return4、common_db.py 实现数据库连接和访问import time import pymysql from common_log import *  # 连接数据库 def get_connection():     _conn_status = True     _max_retries_count = 10  # 设置最大重试次数     _conn_retries_count = 0  # 初始重试次数     _conn_timeout = 3  # 连接超时时间为3秒     with open("config.yml", "r") as f:         result = yaml.load(f, Loader=yaml.FullLoader)         config_mysql = result["mysql"]     while _conn_status and _conn_retries_count <= _max_retries_count:         try:             connect = pymysql.connect(host=config_mysql["host"], user=config_mysql["username"],                                   password=config_mysql["password"], database=config_mysql["database"],                                   port=config_mysql["port"])             _conn_status = False # 如果conn成功则_status为设置为False则退出循环,返回db连接对象             logger.info("连接数据库成功")             return connect         except Exception as e:             _conn_retries_count += 1             logger.info("第%s次连接数据库失败"%(_conn_retries_count))             logger.error(e)         time.sleep(3)         continue  # 查询函数 def select_by_parameters(sql, params=None):     try:         connect = get_connection()         cursor = connect.cursor(pymysql.cursors.DictCursor)         cursor.execute(sql, params)         result = cursor.fetchall()         return result     except Exception as e:         logger.error("执行查询报错")         logger.info(sql)         logger.error(e)     finally:         try:             cursor.close()         except Exception as e:             logger.error("关闭游标对象报错")             logger.error(e)         try:             connect.close()         except Exception as e:             print(e)             print("数据库链接关闭异常")5、common_xlsx.py 实现数据表格的处理import openpyxl import os from openpyxl.styles import PatternFill, Border, Side, Alignment, Protection, Font, colors from openpyxl.utils import get_column_letter  # 定义边框 thin_border = Border(left=Side(style="thin", color="FFFFFF"), right=Side(style="thin", color="FFFFFF"),                      top=Side(style="thin", color="FFFFFF"), bottom=Side(style="thin", color="FFFFFF")) # 居中对齐 alignment_center = Alignment(horizontal="center", vertical="center") # 右对齐 alignment_right = Alignment(horizontal="center", vertical="center")  # 双行填充 fill_double = PatternFill(fgColor="FFDCE6F1", fill_type="solid") # 单行填充 fill_single = PatternFill(fgColor="FFB8CCE4", fill_type="solid")  # 表头填充 fill_head = PatternFill(fgColor="FF366092", fill_type="solid") font_head = Font(bold=True, color="FFFFFFFF") # 1、创建xlsx脚本——在指定的filepath,创建指定的filename的xlsx文件 def create_xlsx(filename):     wb = openpyxl.Workbook()     wb.save(filename)  # 2、增加工作簿脚本——读取指定路径下的xlsx文件,在工作表第index位置增加一个工作簿,并将mysql查询结果result写入到该工作簿 def create_sheet_in_xlsx(filename, sheet_name, result, index):     # 加载文件     wb = openpyxl.load_workbook(filename)     # 在指定位置创建工作表     wb.create_sheet(sheet_name, index)     # 获取新建的工作表     ws = wb[sheet_name]     j = 1     if len(result) > 0:         # 写表头         for key, value in (result[0].items()):             ws.cell(1, j, format(key)).border = thin_border             # 定义对齐方式             ws.cell(1, j).alignment = alignment_center             # 字体 颜色为白色             ws.cell(1, j).font = font_head             # 填充             ws.cell(1, j).fill = fill_head             # 边框             j = j + 1             ws.row_dimensions[1].height = 30         # 写数据         # 根据结果集行数量进行循环         for i in range(len(result)):             # 循环当前行,定义j变量为列使用             j = 1             for key, value in (result[i].items()):                 if i % 2 == 0:                     ws.cell(i+2, j).fill = fill_double                 else:                     ws.cell(i+2, j).fill = fill_single                 ws.cell(i + 2, j, value)                 ws.cell(i + 2, j).border = thin_border                 ws.cell(i + 2, j).alignment = alignment_center                 ws.row_dimensions[i + 2].height = 20                 if "时间" in format(key):                     ws.cell(i+2, j).alignment = alignment_center                     ws.cell(i+2, j).number_format = "yyyy-mm-dd hh:mm:ss"                     j = j + 1                     continue                 # 如果字段名称是数量的话,不保留小数                 if "数量" in format(key):                     ws.cell(i+2, j).number_format = "0"                     j = j + 1                     continue                 # 如果是字段名称包含金额的话,则右对齐                 if "金额" in format(key):                     ws.cell(i+2, j).alignment = alignment_right                     ws.cell(i+2, j).number_format = "#,##0.00"                     j = j + 1                     continue                 j = j + 1     # 保存工作表     wb.save(filename)  # 3、删除工作簿 def delete_sheet_from_xlsx(filename,sheet_name):     wb = openpyxl.load_workbook(filename)     wb.remove_sheet(wb[sheet_name])     wb.save(filename)  # 4、往工作簿中追加行 将mysql的查询结果追加到工作表sheet_name末尾 def add_result_to_sheet(filename,new_sheet_name,result):     wb = openpyxl.load_workbook(filename)     ws = wb[new_sheet_name]     # 获取最大行     mr = ws.max_row     if len(result) > 0:         for i in range(len(result)):             # 循环当前行,定义j变量为列使用             j = 1             for key, value in (result[i].items()):                 if i % 2 == 0:                     ws.cell(i + mr, j).fill = fill_double                 else:                     ws.cell(i + mr, j).fill = fill_single                 ws.cell(i + mr, j, value)                 ws.cell(i + mr, j).border = thin_border                 ws.cell(i + mr, j).alignment = alignment_center                 ws.row_dimensions[i + 2].height = 20                 if "时间" in format(key):                     ws.cell(i + mr, j).number_format = "yyyy-mm-dd hh:mm:ss"                     j = j + 1                     continue                 # 如果字段名称是数量的话,不保留小数                 if "数量" in format(key):                     ws.cell(i + mr, j).number_format = "0"                     j = j + 1                     continue                 # 如果是字段名称包含金额的话,则右对齐                 if "金额" in format(key):                     ws.cell(i + mr, j).alignment = alignment_right                     ws.cell(i + mr, j).number_format = "#,##0.00"                     j = j + 1                     continue                 j = j + 1     wb.save(filename)  # 5、删除工作簿中最大行 def delete_max_row_from_sheet(filename,sheet_name):     wb = openpyxl.load_workbook(filename)     ws = wb[sheet_name]     ws.delete_rows(ws.max_row)     wb.save(filename)  # 6、删除工作簿中指定列 def delete_col_from_sheet(filename,sheet_name,colno):     wb = openpyxl.load_workbook(filename)     ws = wb[sheet_name]     ws.delete_cols(colno)     wb.save(filename)  # 7、对表格中金额和数量字段进行求和 def sum_col_for_sheet(filename,sheet_name):     wb = openpyxl.load_workbook(filename)     ws = wb[sheet_name]     for col in list(ws.columns):         l = [c.value for c in col]         if "金额" in l[0] or "数量" in l[0]:             # 本列行的数量             row_size = len(col) + 1             # 本列的列号是             col_no = col[1].column             col_code = get_column_letter(col[1].column)             ws.cell(row_size, col_no, "=sum(" + str(col_code) + str(2) + ":" + str(col_code) + str(                 row_size - 1) + ")").border = thin_border             ws.cell(row_size, col_no).font = font_head             # 填充             ws.cell(row_size, col_no).fill = fill_head             # 测试添加样式             if "金额" in l[0]:                 ws.cell(row_size, col_no).number_format = "#,##0.00"                 ws.cell(row_size, col_no).alignment = alignment_right             if "数量" in l[0]:                 ws.cell(row_size, col_no).number_format = "0"                 ws.cell(row_size, col_no).alignment = alignment_center             ws.row_dimensions[col_no].height = 20     wb.save(filename)6、common_email.py 实现带附件的邮件发送from email.mime.text import MIMEText from email.header import Header from email.mime.multipart import MIMEMultipart from smtplib import SMTP_SSL from common_log import * import yaml  # # file_Name是路径名称加文件名和扩展名; # new_file_name是在邮件附件中显示的名称 # receivers是收件人列表,中间逗号隔开 # receivers_cc是抄送人列表 # mail_subject是邮件主题 def to_send_email(mail_subject,receivers,receivers_cc,file_name,new_file_name):     with open("config.yml", "r") as f:         result = yaml.load(f, Loader=yaml.FullLoader)         config_email = result["email"]     password = config_email["password"]     msg = MIMEMultipart("related")     msgAlternative = MIMEMultipart("alternative")     msgAlternative.attach(MIMEText("

见附件


", "html", "utf-8")) msg.attach(msgAlternative) # file_name 是指文件路径加名称和扩展名 file1 = MIMEText( open(file_name, "rb").read(), "base64", "utf-8" ) file1["Content-Type"] = "application/octet-stream" # new_file_name是指邮件附件中显示的名称 file1.add_header("Content-Disposition", "attachment",filename=new_file_name) msg.attach(file1) msg["Subject"] = Header(mail_subject, "utf-8").encode() msg["From"] = config_email["from"] msg["To"] = receivers msg["Cc"] = receivers_cc try: smtp = SMTP_SSL(config_email["smtp"]) smtp.login(msg["From"], password) smtp.sendmail(msg["From"], msg["To"].split(",") + msg["Cc"].split(","), msg.as_string()) logger.info("发送邮件成功,邮件接收人是:%s,邮件抄送人是:%s"%(receivers, receivers_cc)) except Exception as e: logger.error("发送邮件出现错误,邮件接收人是:%s,邮件抄送人是:%s" % (receivers, receivers_cc)) logger.error(e) finally: try: smtp.quit() except Exception as e: logger.error(e)

windows7sp1屏幕保护设置方法有不少windows7servicepack1系统的深度技术用户,在安装好系统以后,想要对电脑设置屏幕保护的,但是不知道如何操作的问题。那么下面深度系统小编就分享一下具体的设置方法windows7操作系统开启aero特效的设置方法有不少深度技术的小伙伴都可能还不知道什么是aero特效,其实就是开启win7系统界面透明效果,但是有windows7操作系统的用户不太清楚怎么设置,其实开启的方法很简单,接下来深度win10纯净版wifi热点无法启动承载网络的解决方法有深度技术的小伙伴在使用win10纯净版系统电脑,想要创建无线wifi热点,出现了无法启动承载网络的提示问题,下面深度系统小编就关于此问题,来分享一下具体的处理办法。方法如下1首先win10专业版系统如何设置兼容性的修改方法有深度技术的小伙伴,在电脑升级win10专业版系统以后,打开一些软件都会有不少与win10系统不兼容的程序软件,导致无法正常运行的问题。那么这时我们就需要设置修改兼容性来解决了,但win10纯净版系统如何调整steam分辨率的设置方法有很多深度技术的小伙伴,可能都是通过绝地求生,俗称吃鸡,才知道Steam的吧,它是一个很火爆的游戏平台,然而有不少用户在Steam中玩游戏的时候,发现分辨率不适合,导致图像模糊的问win10专业版安装显卡1660提示停止43的解决方法有一位深度技术用户,刚从网上购买了一块GTX1660的显卡,想要升级一下自己的win10专业版系统电脑硬件来玩一下热门的吃鸡游戏。但是在电脑上安装上1660显卡之后,遇到了在设备管win7电脑蓝屏0x00000050srv。sys的问题有深度技术的小伙伴,在使用win7纯净版系统的时候,出现电脑蓝屏并显示0x00000050错误代码,提示srv。sys的问题,接下来,深度系统小编就这个问题来分享一下具体的解决方法win10专业版电脑复制文件出错0x80004005的问题有深度技术win10专业版用户在使用电脑的时候,复制文件遇到提示一个意外错误使你无法复制该文件,错误0x80004005未指定的错误,导致复制文件失败的问题,下面,深度系统小编来分win7旗舰版64位系统还原网络设置方法有一位深度技术的小伙伴在win7旗舰版64位系统中遇到网络问题,导致电脑无法上网的情况,对于此问题,我们可以通过还原网络设置来解决的,下面深度系统小编就来为大家分享设置网络初始化的正版win7系统如何删除桌面库文件夹的方法很多深度技术用户在安装好正版win7系统时,在桌面上发现有库这个功能,自己又用不到,看着很碍事,想要将其删除,但不知怎么删除的问题。接下来,深度系统小编就来为大家分享一下删除桌面库w7旗舰版系统电脑开机占用内存过高的解决方法有深度技术win7系统用户,在重装win7系统以后发现开机加载时间非常长,占用内存高,电脑很慢的问题。针对此问题,深度系统小编来和大家分享一下具体的解决方法。方法如下1当然是Ctr
大学生毕业摆摊年入百万人啊,千万别瞧不起任何人最近看到一个短视频,大学毕业生陆川,在家乡摆摊卖菜年收入达到百万。有人质疑他大学毕业为什么要来摆摊?原来陆川毕业后做过一段时间通讯行业的工作,因为父亲重病,权衡了好久,他选择回家照神州十二号由哪几部分组成?它的发射意味着什么?今日最新消息,神舟十二号9时22分发射,这一消息标志着我国航天工程的又一个里程碑式的进程,那么神州十二号由哪几部分组成?它的发射意味着什么?下面我们来详细了解一下。据新华社电,神舟明明是自己受苦受累生的孩子,为啥和自己一点都不亲?前几年小王一直忙生意,经常是早出晚归,孩子出生后根本没有时间照顾,于是就让婆婆帮忙照看。今年五一的时候婆婆需要回趟老家,正好小王生意也不是很忙就打算带着儿子回趟娘家,可是小家伙知道男友和闺蜜ampamp39ampamp39出轨ampamp39ampamp39的概率有多大?据不完全统计63的男人对女友的闺蜜心动过35的男人会对女友的闺蜜采取行动23的男人会和女友闺蜜玩暧昧而10的男人会和女友的闺蜜在一起为什么男友出轨闺蜜的指数这么可怕?!做为一个有责2021数字化转型白皮书提出数字化力六大战略由中国企业数字化联盟和国内政企市场专业媒体企业网D1Net联合出品的2021数字化转型白皮书正式发布。白皮书分为七大部分,主要探讨及分析了数字经济数字平台当前数字化转型面临的主要难数字化城管是什么意思?数字化城管是什么意思?很多人一听到城管就感觉很奇怪,一个人怎么可以数字化?莫非是黑客帝国那样,可以自由穿梭虚拟和现实?其实这里的城管并不仅仅是指人,人只是城管中很小的一部分。数字化日系车不全都是好货,这4款车的车主含泪吐槽买回家就后悔对于日系汽车品牌来讲,抛去国家恩怨那真的是一个非常值得信任的品牌,虽说日系车入市多年也出现过减配等现象,但是总体来讲真的是要比德系车靠谱太多了。不管是哪一个日系品牌,在中国的口碑都国产化适配升级银弹谷与统信UOS完成兼容性互认拥有自主可靠知识产权的国产软件春天已经到来6月16日,银弹谷自主研发的VDevSuite银弹谷零代码软件开发套件与统信UOS(统信服务操作系统V20)完成兼容性互认,并被纳入统信服目前最受欢迎的几款旗舰机,骁龙888加持,性能强悍全面的配置和高性价比是一款优秀手机的特点。8128GB内存配置不仅价格便宜,而且完全可以满足实用方的需求,今天给大家推荐几款流行的智能手机,你可以放心购买。IQOO7作为为游戏设计如果一直不工作,你还可以坚持多久?首先,这对我而言是一个非常刺眼的问题,其次,面对这样窘境的人应该不在少数。之所以写这篇文章,是因为我真的闲下来了,而且闲了很久一段时间,复工以后才猛然发觉,工作是一种多么必不可少的快30岁了,该不该放弃工作去创业?大学室友(暂且称呼为X)从北京回来郑州发展,开了一家动画设计的公司。前天晚上X同学请我们几个朋友喝了一顿酒。将近两年没见了,X同学谈起创业谈起未来依然是激情澎湃,仿佛下一年就能走上