python 工资邮件群发工具开发(一个小公司的需求)
------------------------------------
该需求有两个表格,第一个表格是计算薪资的,第二个表格是员工信息,包括姓名和邮箱
这里只要把薪资表按照每个人截图放到一个文件夹中,从信息表取姓名和截图名字做对应,然后发送邮件,企业邮箱和qq邮箱均可,返回日志文件XXX.log
1、工资表样式,名字必须和员工信息表一致
2、员工信息表样式注意列,我没有做行列自定义选择的功能,现在是写死的,当然也可以加个变量自定义
软件样式:
本程序是根据员工信息表的基础,来判定是否成功发送邮件,如果工资表没有这个人,那么也没有这个人的截图,那这个人就未发送。
授权码就是qq邮箱的 第三方授权
--------------------------------------------------
用到的包pillow 、xlwings、email、tkinter(界面UI)
-----------------------------------
这里不多废话,直接贴代码
1 from PIL import ImageGrab 2 import xlwings as xw 3 import smtplib 4 from email.mime.multipart import MIMEMultipart 5 from email.mime.text import MIMEText 6 from email.mime.image import MIMEImage 7 import tkinter as tk # 使用Tkinter前需要先导入 8 from tkinter import filedialog 9 import os 10 import datetime 11 12 class Monitor(): 13 def __init__(self): 14 #错误信息 15 self.error = [] 16 self.nosend = [] 17 self.dictname =[] 18 #发送完成信息 19 self.succcees ='' 20 21 #判断文件夹是否已经创建 22 def is_exists(self,dir): 23 if not os.path.exists(dir): 24 os.makedirs(dir) 25 #日志函数 26 def log(self,excel_name): 27 path = './'+str(datetime.datetime.now())[:-16]+'.log' 28 with open(path,'a+') as f: 29 f.write(str(datetime.datetime.now())[:-7] +' '+ excel_name+'\n') 30 f.write(' 发送成功人员:'+self.succcees+'\n') 31 32 # 截图函数 33 def excel_catch_screen(self,wbs,save_floder,shot_sheetname): 34 sheet = wbs.sheets(shot_sheetname) # 选定sheet 35 all = sheet.used_range # 获取有内容的range 36 all.api.CopyPicture() # 复制图片区域 37 sheet.api.Paste() # 粘贴 38 pic = sheet.pictures[0] # 当前图片 39 pic.api.Copy() # 复制图片 40 img = ImageGrab.grabclipboard() # 获取剪贴板的图片数据 41 self.is_exists(save_floder) # 判断文件夹 42 try: 43 img.save(save_floder+'/'+shot_sheetname + ".png") # 保存图片 44 pic.delete() # 删除sheet上的图片 45 except: 46 print('%s 截图失败!'%shot_sheetname) 47 48 # 邮件函数 49 # 1、查看邮件excel中的所有对应值 (name,email) 50 def get_info(self,dicts,info): 51 app = xw.App(visible=False, add_book=False) 52 wb = app.books.open(info) 53 sheet = wb.sheets[0] 54 maxrow = sheet.used_range.last_cell.row # 最大行 55 for row in range(2,maxrow+1): 56 name = sheet.range('B' + str(row)).value 57 email = str(sheet.range('J' + str(row)).value).replace('@qq.com','') 58 dicts[name] = email.replace('.0','')+'@qq.com' 59 wb.close() 60 app.quit() 61 #2、发送邮件 62 def send_png(self,subject,sender,name,receivers,password,png): 63 message = MIMEMultipart('related') 64 message['Subject'] = subject 65 message['From'] = sender 66 message['To'] = receivers 67 content = MIMEText('<html><body><img src="cid:imageid" alt="imageid"></body></html>','html','utf-8') 68 message.attach(content) 69 file=open(png, "rb") 70 img_data = file.read() 71 file.close() 72 img = MIMEImage(img_data) 73 img.add_header('Content-ID', 'imageid') 74 message.attach(img) 75 try: 76 if(sender[-6:] == 'qq.com'): 77 server=smtplib.SMTP_SSL("smtp.qq.com",465) 78 else: 79 server=smtplib.SMTP_SSL("smtp.exmail.qq.com",465) 80 server.login(sender,password) #"填写qq邮箱的授权码" 81 server.sendmail(sender,receivers,message.as_string()) 82 server.quit() 83 print ("邮件发送成功") 84 self.succcees+=name+',' 85 except smtplib.SMTPException as e: 86 self.error.append(name) 87 print(e) 88 89 def work(self,subject,sender,password,excel_name,info_name): 90 sheetname=[] 91 app = xw.App(visible=False, add_book=False) 92 wb = app.books.open(excel_name) 93 num = len(wb.sheets) 94 #用sheetname 接受所有员工的sheet名 95 for i in range(1,num): 96 sheet = wb.sheets[i] 97 sheetname.append(sheet.name) 98 #循环截屏并保存到自定义文件夹下 99 save_floder = './'+subject 100 for name in sheetname: 101 self.excel_catch_screen(wb,save_floder,name) 102 #存储员工信息表 103 dicts = {} 104 self.get_info(dicts,info_name) 105 106 wb.close() # 不保存,直接关闭 107 app.quit() 108 #循环发送邮件 #发送邮件 根据excel中的邮件人姓名和地址进行匹配已保存的png图片,顺序发送 109 # 遍历字典,在文件夹中找对应图片 110 for name, receivers in dicts.items(): 111 self.dictname.append(name) 112 for file in os.listdir(save_floder): 113 self.nosend.append(file.replace('.png','')) 114 for name, receivers in dicts.items(): 115 if name in self.nosend: 116 self.send_png(subject,sender,name,receivers,password,save_floder+'/'+name+'.png') 117 print(sender,receivers,name,save_floder+'/'+name+'.png') 118 else: 119 self.error.append(name) 120 121 def main(self): 122 # 界面化处理 123 window = tk.Tk() 124 window.title('邮件群发V1.0') 125 # 第3步,设定窗口的大小(长 * 宽) 126 window.geometry('400x400') # 这里的乘是小x 127 128 def selectPath(): 129 entry_excel_name = filedialog.askopenfilename(title='选择工资计算表', 130 initialdir='/', filetypes=[('.xlsx','*.xlsx'),('全部文件','*.*')]) 131 return entry_excel_name 132 def selectPath1(): 133 entry_info_name = filedialog.askopenfilename(title='选择员工信息表', 134 initialdir='/', filetypes=[('.xlsx','*.xlsx'),('全部文件','*.*')]) 135 return entry_info_name 136 137 def exec(entry_excel_name,entry_info_name): 138 try: 139 subject = entry_title.get() 140 sender = entry_usr_name.get() 141 password = entry_usr_pwd.get() 142 excel_name = entry_excel_name 143 info_name = entry_info_name 144 145 print("请等待...") 146 self.work(subject,sender,password,excel_name,info_name) 147 print("生成日志文件完成,若要查看,请移步当前文件夹下logs") 148 self.log(excel_name) 149 150 if len(self.error) == 0: 151 text.insert('insert','所有邮件发送成功') 152 else: 153 text.insert('insert','%s 邮件发送失败' % self.error) 154 except Exception as e: 155 if e == 'No such file: ': 156 text.insert('insert','没有选择文件') 157 else: 158 text.insert('insert',e) 159 160 # 用户信息 161 tk.Label(window, text='邮件标题:', font=('Arial', 10)).place(x=10, y=5) 162 tk.Label(window, text='Q Q 号:', font=('Arial', 10)).place(x=10, y=35) 163 tk.Label(window, text='授权码:', font=('Arial', 10)).place(x=10, y=65) 164 165 subject = tk.StringVar() 166 subject.set('XX年XX月薪资') 167 entry_title = tk.Entry(window, textvariable=subject, font=('Arial', 10)) 168 entry_title.place(x=120, y=5) 169 170 var_usr_name = tk.StringVar() 171 var_usr_name.set('XXXX邮箱') 172 entry_usr_name = tk.Entry(window, textvariable=var_usr_name, font=('Arial', 10)) 173 entry_usr_name.place(x=120, y=35) 174 175 var_usr_pwd = tk.StringVar() 176 var_usr_pwd.set('XXXX授权码') 177 entry_usr_pwd = tk.Entry(window, textvariable=var_usr_pwd, font=('Arial', 10), show='*') 178 entry_usr_pwd.place(x=120, y=65) 179 180 #程序运行框 181 text = tk.Text(window, show=None) 182 text.place(relx=0.01,rely=0.3,relwidth=0.98,relheight=0.7) 183 184 # 执行按钮 185 btn_login = tk.Button(window, text='立即执行',height = 4, width = 15 ,command= lambda : exec(selectPath(),selectPath1())) 186 btn_login.place(x=275, y=6) 187 188 # 主窗口循环显示 189 window.mainloop() 190 191 if __name__ == '__main__': 192 app = Monitor() 193 app.main()