公司动态
影刀RPA 邮件发送的自动化:从简单文本到HTML模板
影刀RPA 邮件发送的自动化从简单文本到HTML模板作者林焱发邮件是RPA流程里最常见的收尾动作——采集完数据发邮件、生成报表发邮件、出错报警发邮件。但很多人写邮件自动化时卡在附件发不出去、中文标题乱码、HTML邮件排版难看、邮箱认证失败。这篇文章从简单到复杂讲清楚影刀RPA里邮件发送的完整方案。一、什么情况用什么场景推荐方案说明简单通知邮件纯文本邮件最简单几行代码搞定带表格的报表邮件HTML邮件表格排版好看带附件的邮件multipart邮件支持任意文件附件群发邮件抄送/密送设置cc/bcc适合通知多人定时日报邮件模板变量替换每天自动生成不同内容个性化群发逐个发送每人不同内容带图片的邮件HTML内嵌图片图片直接显示在邮件里二、怎么做2.1 最简单的纯文本邮件importsmtplibfromemail.mime.textimportMIMETextdefsend_simple_email(smtp_host,port,sender,password,receiver,subject,body):发送纯文本邮件msgMIMEText(body,plain,utf-8)msg[From]sender msg[To]receiver msg[Subject]subjecttry:serversmtplib.SMTP(smtp_host,port)server.starttls()server.login(sender,password)server.sendmail(sender,[receiver],msg.as_string())server.quit()print(邮件发送成功)returnTrueexceptExceptionase:print(f发送失败:{e})returnFalse# QQ邮箱示例使用授权码不是密码# send_simple_email(# smtp.qq.com, 587,# your_emailqq.com, your_auth_code,# receiverexample.com,# RPA流程完成通知,# 商品采集流程已执行完成共采集50条数据。# )常见SMTP服务器配置拼多多店群自动化报活动上架邮箱SMTP服务器端口说明QQ邮箱smtp.qq.com587用授权码163邮箱smtp.163.com587用授权码Gmailsmtp.gmail.com587用应用专用密码| Outlook | smtp.office365.com | 587 | 用账号密码 || 企业微信邮箱 | smtp.exmail.qq.com | 465 | SSL连接 |2.2 HTML邮件带表格importsmtplibfromemail.mime.textimportMIMETextfromdatetimeimportdatetimedefsend_html_email(smtp_host,port,sender,password,receiver,subject,html_content):发送HTML邮件msgMIMEText(html_content,html,utf-8)msg[From]sender msg[To]receiver msg[Subject]subjecttry:serversmtplib.SMTP(smtp_host,port)server.starttls()server.login(sender,password)server.sendmail(sender,[receiver],msg.as_string())server.quit()print(HTML邮件发送成功)returnTrueexceptExceptionase:print(f发送失败:{e})returnFalse# 生成HTML表格defbuild_report_html(title,data_rows,headers):构建HTML报表rows_htmlforrowindata_rows:cells.join(ftd stylepadding:8px;border:1px solid #ddd;{cell}/tdforcellinrow)rows_htmlftr{cells}/trheader_html.join(fth stylepadding:8px;border:1px solid #ddd;background:#f5f5f5;text-align:left;{h}/thforhinheaders)htmlf html body stylefont-family: Microsoft YaHei, Arial, sans-serif; color: #333; h2 stylecolor: #1890ff;{title}/h2 p stylecolor: #666; font-size: 14px;生成时间:{datetime.now().strftime(%Y-%m-%d %H:%M:%S)}/p table styleborder-collapse: collapse; width: 100%; margin: 20px 0; thead tr{header_html}/tr /thead tbody{rows_html}/tbody /table hr styleborder: none; border-top: 1px solid #eee; margin: 20px 0; p stylecolor: #999; font-size: 12px;此邮件由RPA流程自动发送请勿回复。/p /body /html returnhtml# 使用示例# headers [商品名称, 价格, 库存, 状态]# data [# [商品A, ¥99, 500, 在售],# [商品B, ¥199, 0, 缺货],# [商品C, ¥59, 120, 在售],# ]# html build_report_html(商品采集日报, data, headers)# send_html_email(smtp.qq.com, 587, youqq.com, auth_code, bosscompany.com, 商品采集日报, html)2.3 带附件的邮件importsmtplibfromemail.mime.textimportMIMETextfromemail.mime.multipartimportMIMEMultipartfromemail.mime.applicationimportMIMEApplicationimportosdefsend_email_with_attachments(smtp_host,port,sender,password,receiver,subject,body,attachmentsNone,ccNone):发送带附件的邮件msgMIMEMultipart()msg[From]sender msg[To]receiverifcc:msg[Cc]cc msg[Subject]subject# 邮件正文msg.attach(MIMEText(body,html,utf-8))# 添加附件ifattachments:forfile_pathinattachments:ifnotos.path.exists(file_path):print(f附件不存在跳过:{file_path})continuewithopen(file_path,rb)asf:attachmentMIMEApplication(f.read())# 设置附件文件名处理中文文件名filenameos.path.basename(file_path)attachment.add_header(Content-Disposition,attachment,filename(utf-8,,filename))msg.attach(attachment)print(f附件已添加:{filename})# 发送receivers[receiver]ifcc:receivers.extend(cc.split(,))try:serversmtplib.SMTP(smtp_host,port)server.starttls()server.login(sender,password)server.sendmail(sender,receivers,msg.as_string())server.quit()print(f邮件发送成功收件人:{receivers})returnTrueexceptExceptionase:print(f发送失败:{e})returnFalse# 使用示例# send_email_with_attachments(# smtp.qq.com, 587, youqq.com, auth_code,# bosscompany.com,# 周报,# h2本周工作周报/h2p详见附件。/p,# attachments[rD:\report\weekly_report.xlsx, rD:\report\chart.png],# cccolleaguecompany.com# )2.4 邮件模板系统每次发邮件都要重写HTML太麻烦。做一个模板系统只替换变量importosfromdatetimeimportdatetimeclassEmailTemplate:邮件模板管理# 日报模板DAILY_REPORT html body stylefont-family: Microsoft YaHei, Arial, sans-serif; color: #333; h2 stylecolor: #1890ff; {{title}}/h2 p stylecolor: #666;日期: {{date}}/p div stylebackground: #f6f8fa; padding: 15px; border-radius: 5px; margin: 15px 0; h3 stylemargin-top: 0;执行概要/h3 ul li流程状态: strong stylecolor: {{status_color}};{{status}}/strong/li li执行耗时: {{duration}}/li li处理数据量: {{data_count}} 条/li li成功率: {{success_rate}}%/li /ul /div {{table_html}} {{error_section}} hr styleborder: none; border-top: 1px solid #eee; margin: 20px 0; p stylecolor: #999; font-size: 12px;此邮件由RPA流程自动发送 | {{send_time}}/p /body /html # 错误报警模板ERROR_ALERT html body stylefont-family: Microsoft YaHei, Arial, sans-serif; color: #333; h2 stylecolor: #ff4d4f;⚠️ 流程异常报警/h2 div stylebackground: #fff2f0; padding: 15px; border-left: 4px solid #ff4d4f; margin: 15px 0; pstrong流程名称:/strong {{flow_name}}/p pstrong发生时间:/strong {{error_time}}/p pstrong错误信息:/strong/p pre stylebackground: #f5f5f5; padding: 10px; overflow-x: auto; font-size: 13px;{{error_message}}/pre /div p stylecolor: #666;请及时检查并处理。/p hr styleborder: none; border-top: 1px solid #eee; margin: 20px 0; p stylecolor: #999; font-size: 12px;RPA自动报警系统 | {{send_time}}/p /body /html classmethoddefrender(cls,template,**kwargs):渲染模板resulttemplateforkey,valueinkwargs.items():resultresult.replace(f{{{{{key}}}}},str(value))returnresult# 使用示例日报# html EmailTemplate.render(# EmailTemplate.DAILY_REPORT,# title商品采集日报,# datedatetime.now().strftime(%Y-%m-%d),# status成功 if success else 失败,# status_color#52c41a if success else #ff4d4f,# duration3分20秒,# data_count50,# success_rate96,# table_htmlp详见附件/p,# error_section,# send_timedatetime.now().strftime(%Y-%m-%d %H:%M:%S)# )# 使用示例错误报警# html EmailTemplate.render(# EmailTemplate.ERROR_ALERT,# flow_name商品采集_v3,# error_timedatetime.now().strftime(%Y-%m-%d %H:%M:%S),# error_messageTimeoutError: 翻页采集第3页时超时\n请检查网络连接,# send_timedatetime.now().strftime(%Y-%m-%d %H:%M:%S)# )2.5 HTML内嵌图片有时候需要把图片直接显示在邮件正文里不是附件importsmtplibfromemail.mime.textimportMIMETextfromemail.mime.multipartimportMIMEMultipartfromemail.mime.imageimportMIMEImageimportosdefsend_email_with_inline_image(smtp_host,port,sender,password,receiver,subject,html_body,image_path,image_cidreport_chart):发送带内嵌图片的HTML邮件msgMIMEMultipart(related)msg[From]sender msg[To]receiver msg[Subject]subject# HTML正文引用图片用cid:html_with_imagehtml_body.replace(f{{{{image}}}},fcid:{image_cid})msg.attach(MIMEText(html_with_image,html,utf-8))# 添加图片ifos.path.exists(image_path):withopen(image_path,rb)asf:imgMIMEImage(f.read())img.add_header(Content-ID,f{image_cid})img.add_header(Content-Disposition,inline,filenameos.path.basename(image_path))msg.attach(img)print(f图片已内嵌:{image_path})else:print(f图片不存在:{image_path})try:serversmtplib.SMTP(smtp_host,port)server.starttls()server.login(sender,password)server.sendmail(sender,[receiver],msg.as_string())server.quit()print(邮件发送成功)returnTrueexceptExceptionase:print(f发送失败:{e})returnFalse# HTML模板里用 {{image}} 引用图片位置# html # htmlbody# h2销售趋势图/h2# img src{{image}} stylemax-width: 600px;/# /body/html# # send_email_with_inline_image(# smtp.qq.com, 587, youqq.com, auth_code,# bosscompany.com, 销售周报,# html, rD:\charts\sales_trend.png# )三、有什么坑坑1QQ邮箱/163邮箱认证失败用登录密码直接连SMTP会报认证失败。QQ和163邮箱要求使用授权码。解决登录QQ邮箱→设置→账户→开启SMTP服务生成授权码代码里用授权码代替密码坑2中文附件名乱码直接用中文文件名作为附件有些邮箱客户端显示乱码。解决用编码方式设置文件名fromemail.utilsimportformataddrfromemail.headerimportHeader# 正确设置中文附件名attachment.add_header(Content-Disposition,attachment,filename(utf-8,,中文文件名.xlsx))坑3SSL连接 vs STARTTLS有些邮箱用465端口SSL有些用587端口STARTTLS。搞混了连不上。TEMU店群矩阵自动化运营核价报活动# 465端口SSL直连serversmtplib.SMTP_SSL(smtp_host,465)server.login(sender,password)# 587端口STARTTLSserversmtplib.SMTP(smtp_host,587)server.starttls()server.login(sender,password)坑4附件太大发送失败大多数邮箱对附件大小有限制QQ邮箱50MBGmail 25MB。解决大文件先压缩或者上传到网盘后在邮件里放链接importzipfileimportosdefcompress_files(file_paths,zip_path):压缩文件withzipfile.ZipFile(zip_path,w,zipfile.ZIP_DEFLATED)aszf:forfile_pathinfile_paths:ifos.path.exists(file_path):zf.write(file_path,os.path.basename(file_path))original_sizesum(os.path.getsize(f)forfinfile_pathsifos.path.exists(f))zip_sizeos.path.getsize(zip_path)print(f原始:{original_size/1024/1024:.1f}MB → 压缩:{zip_size/1024/1024:.1f}MB)returnzip_path坑5群发邮件被判定为垃圾邮件短时间内发大量邮件或者邮件内容有敏感词可能被邮箱服务商限制。解决控制发送频率每次间隔几秒群发用密送BCC收件人看不到其他人地址避免标题和内容有广告类关键词defsend_batch_emails(smtp_config,email_list,interval5):批量发送邮件带间隔importtime success_count0fori,email_datainenumerate(email_list):print(f[{i1}/{len(email_list)}] 发送给:{email_data[receiver]})resultsend_simple_email(smtp_config[host],smtp_config[port],smtp_config[sender],smtp_config[password],email_data[receiver],email_data[subject],email_data[body])ifresult:success_count1# 间隔等待ifilen(email_list)-1:time.sleep(interval)print(f发送完成:{success_count}/{len(email_list)}成功)总结邮件自动化的核心要点用授权码不用密码——QQ/163邮箱都用授权码HTML邮件用模板——写一次模板每次只换变量中文文件名要编码——否则附件名乱码大文件先压缩——避免超过邮箱附件限制批量发送加间隔——防止被判定为垃圾邮件