需求分析
公司业务提出需求,需要将数据库的销售记录导出到excel给她们。并且每天都要给。。我特喵
想了想也很简单,写个脚本从数据库查询数据并导出到excel,然后做个定时任务每天定时发邮件给他们不就完事儿了吗~So easy!
不说了,开撸!
首先安装需要用到的包
#与数据库打交道的模块,可以连接数据库,查询数据等
pip3 install pymysql
#与Excel打交道的模块,可以读取,写入数据到excel,以及一系列的excel操作
pip3 install openpyxl
话不多说,上代码
import os
import pymysql
import smtplib
from openpyxl import Workbook
from openpyxl.utils import get_column_letter
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
class SelectData(object):
def __init__(self,sql_items):
self.sql_items = sql_items
def select_sql(self):
sheet_item = [] #定义一个空列表,存放查询到的数据
for i in self.sql_items: #遍历所有的SQL项目
sql_query = i['sql_query'] #获取当前SQL项目中的SQL语句
db_info = i['db'] #获取当前SQL项目中的数据库连接信息
#生成一个数据库连接
try:
db = pymysql.connect(host=db_info['host'],user=db_info['user'],password=db_info['password'],database=db_info['database'],port=db_info['port'],charset=db_info['charset'])
except Exception as e:
print(f'数据库连接失败:{e}')
continue
cursor = db.cursor() #生成一个数据库游标
cursor.execute(sql_query) #用游标运行SQL语句,查询数据
col = cursor.description # 获取列名作为Excel的表头
results = cursor.fetchall() #获取查询到的数据
if not results:
#判断当前SQL项目查询是否有数据,如果查询结果为空,则进入下一个循环
continue
cursor.close() #关闭游标
db.close() #关闭数据库连接
#将查询到的数据以字典的形式存储
items = {
'sheet_name': i['sheet_name'], #写入的Excel的Sheet名
'results': results, # 查询结果数据
'col': col, #列名
}
sheet_item.append(items) #将查询到的数据添加到数据列表
if not sheet_item:
#判断数据列表是否有数据,如果没有返回空
return
return sheet_item #返回数据列表
class WriteExcel(object):
"""
写入数据到excel
"""
def __init__(self,sheet_item,save_path):
self.sheet_item = sheet_item # 接收数据列表
self.save_path = save_path # 接收excel保存路径
def _get_maxcol(self,results,columnName):
##获取表格存放的数据的最大列宽,自适应调整列宽
columnName_len = [] #定义一个存放列宽大小的列表
for n in range(len(columnName)): #遍历列名
max_len = 10
column_len = len(str(columnName[n]).encode('utf-8')) #列名长度
if column_len > max_len: #判断如果列名长度大于10的话,就替换最大长度
max_len = column_len
for row in range(len(results)): #循环遍历每行数据中第n列的数据
data = str(results[row][n]) #取出第row行的数据
data_len = len(data.encode('utf-8')) #判断此数据的长度
if data_len > max_len: #如果次数据的长度大于max_len则max_len等于此数据的长度。
max_len = data_len
elif data_len > 100: #如果此数据的长度大于100,则取列宽为100,单列太宽了没法看。。
max_len = 100
columnName_len.append(max_len) #将最大列宽加入到列宽的列表中
return columnName_len #返回列宽列表
def write_excel(self):
wb = Workbook() #生成一个excel的workbook对象
aaa = wb.active #第一个sheet
for item in self.sheet_item: #循环遍历接收到的数据列表
results = item['results'] #查询结果数据
columnName = [] #定义一个excel列名列表
for c in item['col']: #循环遍历数据库中返回的列名元组信息
columnName.append(c[0]) #获取列名的字符串,并添加到列名列表
columnName_len = self._get_maxcol(results,columnName) #获取每列的最大列宽值
rows = len(results) #数据结果的长度即为excel的数据行数
sheet_name = item['sheet_name'] #sheet名
sheet = wb.create_sheet(title=sheet_name) #创建一个sheet对象
for i in range(1, len(columnName) + 1): #循环遍历列名列表,并将列名写入sheet对象的首行
col_letter = get_column_letter(i) #获取此列对应的字母,比如第一列对应的为A
col_len = columnName_len[i-1] #获取此列的列宽值
sheet.column_dimensions[col_letter].width = col_len #设置此列的宽度
sheet.cell(row=1, column=i, value=columnName[i - 1]) #将列名写入表格
for n in range(1,rows+1): #循环遍历总行数
for j in range(1,len(columnName)+1): #循环遍历列数
sheet.cell(row=n+1,column=j,value=results[n-1][j-1]) #将对应的行列坐标的数据写入excel
wb.remove(aaa) #移除第一个sheet
wb.save(self.save_path) #保存excel到指定的路径
class SendMail(object):
def __init__(self,save_path,excel_name,to_mail_addr,cc_mail_addr):
self.save_path = save_path #接收excel的保存路径
self.excel_name = excel_name #接收excel名
self.to_mail_addr = to_mail_addr #接收收件人邮件地址
self.cc_mail_addr = cc_mail_addr #接收抄送人邮件地址
def send_mail(self):
username = 'aaa@163.com' #发件人邮箱
password='*********' #发件人邮箱密码
sender='aaa@163.com' #显示的发件人
receiver = self.to_mail_addr + self.cc_mail_addr #接收邮件的收件人
if not receiver:
return '没有收件人!邮件未发送'
subject = '%s报表' % self.excel_name #邮件主题
msg = MIMEMultipart('mixed') #定义一个msg对象
msg['Subject'] = subject #显示的邮件主题
msg['From'] = 'aaa <aaa@163.com>' #显示的发件人信息
msg['To'] = ";".join(self.to_mail_addr) #显示的收件人信息
msg['Cc'] = ';'.join(self.cc_mail_addr) #显示的抄送人信息
text = "Dear All:\n\n附件是%s,请查收。" % self.excel_name #邮件的文本内容
text_plain = MIMEText(text,'plain', 'utf-8') #设置邮件的文本格式为text,也可以为html
msg.attach(text_plain) #将文本添加到msg对象
sendfile = open(self.save_path,'rb').read() #读取要发送的附件文件
text_att = MIMEText(sendfile, 'base64', 'utf-8') #将附件添加到邮件的正文文本
text_att["Content-Type"] = 'application/octet-stream'
text_att.add_header('Content-Disposition', 'attachment', filename='%s' % self.excel_name) #附件显示名
msg.attach(text_att) #将附件信息添加到msg对象中
smtp = smtplib.SMTP(host='smtp.163.com') #定义一个smtp发件服务器信息
smtp.connect('smtp.163.com',25) #连接smtp发件服务器
smtp.starttls() #以加密的方式通信
smtp.login(username, password) #登陆smtp服务器
try:
smtp.sendmail(sender, receiver, msg.as_string()) #发送邮件
smtp.quit() #推出smtp邮件服务器
except Exception as e:
return f'邮件发送错误:{e}'
success_log = "'%s'报表已发送到您邮箱,请注意查收!" % self.excel_name
return success_log #打印发送邮件成功的信息
if __name__ == '__main__':
excel_name = 'test.xlsx' # 导出的excel的名称
save_path = os.path.join('./', excel_name) # 导出的excel的存储路径
to_mail_addr = ['xxx@163.com', 'bbb@126.com'] # 邮件收件人
cc_mail_addr = [] # 邮件抄送人
# 查询语句,以及连接的数据库和导出的sheet名,可以添加多个项目,
sql_items = [
{
'sql_query': """select * from app02_user;""", #这里最好用三引号,这样你SQL如果换行的话也不用加\换行符
'db': {'host': "localhost", 'user': "root", 'password': "123.com",'database': "db_test", 'port': 3306, 'charset': 'utf8'}, ####数据库连接####
'sheet_name': 'test' #excel工作簿中的sheet名
},
]
print("开始查询数据...")
data = SelectData(sql_items).select_sql()
if not data:
print('没有查到任何数据!')
print('数据写入excel中...')
WriteExcel(data,save_path).write_excel()
print('正在发送邮件...')
msg = SendMail(save_path,excel_name,to_mail_addr,cc_mail_addr).send_mail()
print(msg)
注:
拷贝过去,修改以下参数即可运行:
excel_name,save_path,to_mail_addr,cc_mail_addr和sql_items中的SQL语句和数据库连接信息即可。
我们查询数据库的时候有是有需要传参数,比如传一个限制时间等。大家就自行研究吧,也可以私信我探讨
特别说明:发邮件的邮箱根据你自己的邮箱情况设定。账户密码以及发件的SMTP服务器、端口等。
如果还有什么不清楚的,也可以私信我,我们交流交流。
新手上路~请老司机勿喷。如果觉得还行对您有帮助,辛苦帮我点个赞 ~谢谢各位啦