Python发送带zip附件的邮件

应用背景

开发环境没有网络,若想传文件必须通过外网发邮件给内网,经人审核后再内网发邮件给我,而且更麻烦的一封邮件携带的附件大小不能超过20M,而且每封还要写上与项目相关的主题。

解决思路

首先人工将大文件压缩然后设置每卷大小为19M,放到一个文件夹里面。
然后遍历此文件夹下的所有压缩文件,利用Python的smtplib和email模块,对每个压缩文件进行邮件发送。

中途遇到的问题:

  • 如果一次性发送过大的文件(30M)后台会报错,mtplib.SMTPDataError: (451, b'Error: queue file write error')。解决办法:压缩文件大小最好在20M以内。
  • 第一次发送邮件是发送一封,第二次是两封,第三次三封。。。。最后发现是没有把msg = MIMEMultipart()放到方法内,处于全局变量中。
  • 使用qq邮箱发邮件,太过频繁会被认定为垃圾邮件不给发送,报错550。解决方案:使用没有限制的邮箱,我用的学校的邮箱,随便发
  • 使用学校邮箱发邮件,偶尔会有几封邮件报错554,被认定为垃圾邮件。
  • 最后一个server发一封邮件,如果server在那挂着连续发邮件,中途有可能会断开连接,原因不知道。所以应该发一封邮件启动一个server,发完了再关闭,至少我再没有遇到连接中断的问题了。

550:出错原因:该服务器IP的发信频率大幅度超过QQ邮箱限制。
QQ邮箱对来自相同IP的外部发信服务器有一定的频率限制:
1、超过每分钟发信量限制,此IP地址被禁止发信若干分钟。
2、超过每小时发信量限制,此IP地址被禁止发信若干小时。
3、超过每日发信量限制,此IP地址本日内禁止再发信。
4、以上频率限制数值属于QQ邮箱保密数据,恕不公开。

无界面代码

from email import encoders
from email.header import Header
from email.mime.text import MIMEText
from email.utils import parseaddr, formataddr
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
import smtplib

import random
from threading import Thread
import os
from config import *


def _format_addr(s):
	"""添加头部信息"""
	name, addr = parseaddr(s)
	return formataddr((Header(name, 'utf-8').encode(), addr))


def installMsg(from_addr, to_addr, message, each_rar):
	msg = MIMEMultipart()
	msg['From'] = _format_addr('HUST<%s>' % from_addr)
	msg['To'] = _format_addr('zytldq<%s>' % to_addr)
	msg['Subject'] = Header(message, 'utf-8').encode()
	msg.attach(MIMEText(message+",此附件用于信息中心ERP系统开发,请转发谢谢。", 'plain', 'utf-8'))
	with open(each_rar, 'rb') as f:
		# 设置附件的MIME和文件名,这里是rar类型:
		mime = MIMEBase('rar', 'rar', filename=each_rar)
		# 加上必要的头信息:
		mime.add_header('Content-Disposition', 'attachment', filename=each_rar)
		mime.add_header('Content-ID', '<0>')
		mime.add_header('X-Attachment-Id', '0')
		# 把附件的内容读进来:
		mime.set_payload(f.read())
	# 用Base64编码
	encoders.encode_base64(mime)
	#添加到MIMEMultipart
	msg.attach(mime)
	return msg


def sendRAR(email_user, to_addr, message, each_rar):

	server = smtplib.SMTP(email_user[2], 25)
	server.login(email_user[0], email_user[1])

	msg = installMsg(server.user, to_addr, message, each_rar)
	print("使用【%s】发送 ----> "%server.user, end="")
	server.sendmail(server.user, [to_addr], msg.as_string())

	server.quit()


if __name__ == '__main__':

	#单线程版本
	os.chdir(PATH)
	failedEmail = []

	for i in range(len(os.listdir())):
		errors = 0
		while errors < 5:
			try:
				sendRAR(random.choice(EMAIL_USERS), TO_ADDR, MESSAGE, os.listdir()[i])
				print("第%d封邮件发送完成"%(i+1),"还有%d封"%(len(os.listdir())-i-1))
			except Exception as e:
				print("第%d封邮件发送【失败】,将重新发送!原因:%s"%(i+1, e))
				errors += 1
				if errors == 5:
					failedEmail.append((i+1, e))
				continue
			break

	print("-------------------压缩文件发送结果-------------------")
	if not failedEmail:
		print("文件全部发送完毕!")
	else:
		for i in failedEmail:
			print("第%d封文件发送失败!错误原因:%s"%(i[0], i[1]))

	


	"""多线程版本
	
	os.chdir(PATH)
	failedEmail = []
	processList = []

	for i in range(len(os.listdir())):
		p = Thread(target = sendRAR, args = (random.choice(EMAIL_USERS), TO_ADDR, MESSAGE, os.listdir()[i])
		p.start()
		processList.append(p)

	for i in range(len(processList)):
		processList[i].join()
		print("第%d封邮件发送完成"%(i+1),"还有%d封"%(len(os.listdir())-i-1))

	print("邮件发送完毕")
	"""

改进

  • 可以将压缩过程也用python实现,输入文件为带压缩的文件。
  • 用多线程发送邮件队列,节省发邮件时间。使用多线程的方法有一点BUG,发送频率太快了,一直报错connections too many 容易被服务器BAN掉。明天再改进,应该同一时间只允许一个账号创建一个连接不能有多个连接,多线程应该体现在多账号上,这样应该不会BAN了。
  • 使用tkinter写个界面

你可能感兴趣的:(python小工具)