注:作者编程小白,高手勿喷,如有疏漏,还请指正!
近期在跟着廖大大的教程学 Python,正好看到 SMTP发送邮件,于是乎自己尝试了一下,不(好)巧,自己主要用的是新浪邮箱,遇到了一些问题。在此记录一下。
from email.mime.text import MIMEText
msg = MIMEText('hello, send by Python...', 'plain', 'utf-8')
# 输入Email地址和口令:
from_addr = input('From: ')
password = input('Password: ')
# 输入收件人地址:
to_addr = input('To: ')
# 输入SMTP服务器地址:
smtp_server = input('SMTP server: ')
import smtplib
server = smtplib.SMTP(smtp_server, 25) # SMTP协议默认端口是25
server.set_debuglevel(1)
server.login(from_addr, password)
server.sendmail(from_addr, [to_addr], msg.as_string())
server.quit()
运行结果报错:
smtplib.SMTPDataError: (553, b'Envolope sender mismatch with header from..')
代码中 msg 的第一个参数不论怎么改都会报相同的错!下面我会提到为什么单独对这个参数进行分析……
msg = MIMEText('XXX', 'plain', 'utf-8')
# “XXX”随便改什么 string
from email import encoders
from email.header import Header
from email.mime.text import MIMEText
from email.utils import parseaddr, formataddr
import smtplib
def _format_addr(s):
name, addr = parseaddr(s)
return formataddr((Header(name, 'utf-8').encode(), addr))
from_addr = input('From: ')
password = input('Password: ')
to_addr = input('To: ')
smtp_server = input('SMTP server: ')
msg = MIMEText('hello, send by Python...', 'plain', 'utf-8')
msg['From'] = _format_addr('Python爱好者 <%s>' % from_addr)
msg['To'] = _format_addr('管理员 <%s>' % to_addr)
msg['Subject'] = Header('来自SMTP的问候……', 'utf-8').encode()
server = smtplib.SMTP(smtp_server, 25)
server.set_debuglevel(1)
server.login(from_addr, password)
server.sendmail(from_addr, [to_addr], msg.as_string())
server.quit()
这一次发现打印信息表示发送成功,
data: (250, b'ok queue id 420484393705')
send: 'quit\r\n'
reply: b'221 smtp-5-123.smtpsmail.fmail.xd.sinanode.com\r\n'
reply: retcode (221); Msg: b'smtp-5-123.smtpsmail.fmail.xd.sinanode.com'
然而打开收件邮箱查看是否收到时,却发现并没有!然后再次尝试修改 msg 的第一个参数:
msg = MIMEText("from:[email protected], send by Python...", 'plain', 'utf-8')
#“XXX”替换为发件箱的地址
再次尝试,发现——收件箱收到邮件了!
在测试“最简单的文本邮件”遇到报错问题时也搜索了很多的解决方案并尝试,其中发现有一种做法就是直接用 string 定义邮件正文内容:
msg = """
to:%s
from:%s
Hello,I am smtp server
""" %(to_addr, from_addr)
...
server.sendmail(from_addr, [to_addr], msg)
这个时候发现居然可以收到邮件了(尽管会有“仔细观察,发现如下问题”)!
关于这个报错百度了不少解决方案,发现并没有一个跟我发现的情况一样。大概意思应该是 smtp.sina.com 会解析邮件正文内容,如果里面包含“from:[email protected]”信息,与 msg [‘From’] 一致,就算作 match。
但是在具体的各种试验的时候发现还是有点不太理解其中的规则,后面有时间再深入研究一下吧(应该给 smtp.sina.com 提交个 bug 先 :-p)!
一些简单的测试结果:
简单/完整 | msg类型 | msg值/参数值 | 发件人定义 | 测试结果 |
---|---|---|---|---|
简单文本 | MIMEText | 参数1不包含 “From:[email protected]” | from_addr = “[email protected]” | 报错mismacth |
简单文本 | MIMEText | 参数1包含 “From:[email protected]” | from_addr = “[email protected]” | 报错mismacth |
简单文本 | string | 不包含string “From:[email protected]” | from_addr = “[email protected]” | 报错mismacth |
简单文本 | string | 包含string “From:[email protected]” | from_addr = “[email protected]” | 成功发送 & 接收成功 |
完整邮件 | MIMEText | 参数1不包含 “From:[email protected]” | from_addr = “[email protected]” | 报错mismacth |
完整邮件 | MIMEText | 参数1包含 “From:[email protected]” | from_addr = “[email protected]” | 报错mismacth |
完整邮件 | MIMEText | 参数1不包含 “From:[email protected]” | msg [‘From’] = XXX | 发送成功 & 接收失败 |
完整邮件 | MIMEText | 参数1包含 “From:[email protected]” | msg [‘From’] = XXX | 发送成功 & 接收成功 |
发现还有不少问题,HTML内容好像可以成功,但是加上附件了又收不到邮件,不知道为什么。附上测试用的代码,可自行修改对应的用户名、密码进行测试。
发现带附件发的话原来进了QQ邮箱的垃圾箱,所以还是可以成功发送并接收的!
# https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001432005226355aadb8d4b2f3f42f6b1d6f2c5bd8d5263000
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
def _format_addr(s):
name, addr = parseaddr(s)
return formataddr((Header(name, 'utf-8').encode(), addr))
#定义“结构体”init_smtp_info()
class MyClass():
def __init__(self, name = ""):
self.name = name
self.data_dic = {}
self.index = -1
class Struct():
def __init__(self, from_addr, password, smtp_server, to_addr, msg):
self.from_addr = from_addr
self.password = password
self.smtp_server = smtp_server
self.to_addr = to_addr
self.msg = msg
#return struct item.
def init_smtp_info(self, from_addr, password, smtp_server, to_addr, msg):
return self.Struct(from_addr, password, smtp_server, to_addr, msg)
#创建smtp_info集实例
smtp_info_collection = MyClass()
#通用-直接定义收件人
to_addr = "XXX@XX" #请对应修改指定收件邮箱信息
#通用-定义邮件正文内容
msg1 = MIMEText("from:[email protected], send by Python...", 'plain', 'utf-8') #请对应修改邮箱信息
msg2 = MIMEText("hello, send by Python...", 'plain', 'utf-8')
msg3 = MIMEText('Hello
' +
'send by Python...
' +
'', 'html', 'utf-8')
#定义带附件的邮件内容
msg4 = MIMEMultipart()
msg4.attach(msg1)
with open('XXX', 'rb') as f: #请对应修改图片文件信息
# 设置附件的MIME和文件名,这里是jpg类型:
mime = MIMEBase('image', 'jpg', filename='XXX.jpg') #请对应修改图片文件信息
# 加上必要的头信息:
mime.add_header('Content-Disposition', 'attachment', filename='XXX.jpg') #请对应修改图片文件信息
mime.add_header('Content-ID', '<0>')
mime.add_header('X-Attachment-Id', '0')
# 把附件的内容读进来:
mime.set_payload(f.read())
# 用Base64编码:
encoders.encode_base64(mime)
# 添加到MIMEMultipart:
msg4.attach(mime)
#确定到底使用哪个msg
msg = msg4
#单独-定义发件人(QQ邮箱)
from_qq_addr = "[email protected]" #请对应修改QQ邮箱信息
pswd_qq = "XXX" #请对应修改QQ邮箱信息
smtp_qq_server = "smtp.qq.com"
#初始化qq邮箱实例
info_qq = smtp_info_collection.init_smtp_info(from_qq_addr,pswd_qq,smtp_qq_server,to_addr,msg)
#单独-定义发件人(新浪邮箱)
from_sina_addr = "[email protected]" #请对应修改新浪邮箱信息
pswd_sina = "XXX" #请对应修改新浪邮箱信息
smtp_sina_server = "smtp.sina.com"
#初始化新浪邮箱实例
info_sina = smtp_info_collection.init_smtp_info(from_sina_addr,pswd_sina,smtp_sina_server,to_addr,msg)
#定义被调用的发件人信息(当前新浪邮箱,用#进行切换)
#finalized_info = info_qq
finalized_info = info_sina
#通用-定义与发送邮件部分
msg['From'] = _format_addr('Python <%s>' % finalized_info.from_addr)
msg['To'] = _format_addr('Target <%s>' % finalized_info.to_addr)
msg['Subject'] = Header('来自%s的问候……' % finalized_info.from_addr, 'utf-8').encode()
#打印完整的msg信息(编码后)
print("\r\n\r\nContent of msg:\r\n"+msg.as_string())
#打印与服务器交互信息
print("\r\n\r\n\r\n%s:\r\n" % finalized_info.smtp_server)
if finalized_info.smtp_server == "smtp.qq.com":
server = smtplib.SMTP_SSL(finalized_info.smtp_server, 465)
elif finalized_info.smtp_server == "smtp.sina.com":
server = smtplib.SMTP(finalized_info.smtp_server, 25)
server.set_debuglevel(1)
server.login(finalized_info.from_addr, finalized_info.password)
if type(msg) == type("string"):
server.sendmail(finalized_info.from_addr, [finalized_info.to_addr], msg)
elif type(msg) != type("string"):
server.sendmail(finalized_info.from_addr, [finalized_info.to_addr], msg.as_string())
server.quit()