, source_address=None) 可以看到主要的实例化参数是 host 和port 。都有默认参数。host是一个字符串,表示smtp服务器URL,port表示服务器开放端口。可以在实例化时输入参数,直接完成连接服务器socket操作。也可以后面用connect()函数指定smtp服务器和端口连接。(注意如果smtp服务器开启了ssl 的话,要用 smtplib.SMTP_SSL() 类构造smtp服务器对象。)
connect() 函数: 连接smtp服务器。相当于建立socket连接。主要是两个参数:host和port。分别表示smtp服务器的URL和开放端口。执行该函数后即完成和服务器之间的socket连接。 connect(host=’localhost’, port=0, sourceaddress=None) method of smtplib.SMTP instance Connect to a host on a given port. If the hostname ends with a colon (`:’) followed by a number, and there is no port specified, that suffix will be stripped off and the number interpreted as the port number to use. Note: This method is automatically invoked by _init , if a host is specified during instantiation.
login() 函数: 登陆smtp服务器,认证并获取权限。参数为邮箱账号明文字符串和登录密码明文字符串。相当于 原始命令:auth login 。
sendmail() 函数: 发送邮件。三个必须参数分别是:发送源邮箱,发送目的邮箱,发送邮件主体(需要特定格式的主体)。相当于封装了:mail from ;rcpt to ;data 三个原始命令。 sendmail(from_addr, to_addrs, msg, mail_options=[], rcpt_options=[]) method of smtplib. 注意 msg 必须是有特定格式字符串,包括邮件头部消息和邮件正文。就好像现实中邮寄一封信,信件上必须有发件人,收件人,这里再加上主题。还要注意,msg这里是一个字符串。即sendmail() 发送邮件时,邮件的内容必须是一个字符串。后面会看到,如果用email构造邮件主体对象,在用sendmail() 函数发送这个邮件主体对象时必须调用as_string()函数,把邮件主体对象转化为字符串。 quit() 函数: 退出邮箱登陆。同时断开socket连接。相当于封装了:quit 命令加 Ctrl + C操作。
三、用Python 脚本发纯文本格式的邮件。主要是熟悉smtplib和email两个模块。
#-*-coding:GBK -*-
import smtplib
from email . mime . text import MIMEText
#注意第一行。在Windows里需要用 GBK编码格式,在Linux里需要用utf-8编码格式。。。
#指定邮件中转服务器:MTA
mail_host = 'smtp.163.com'
#发件人
mail_sender = 'zhouyy_1987@163.com'
#邮箱登陆密码或验证码
mail_pass = 'XXXXXXXX'
mail_recevier = 'yiyong.zhou@msxf.com'
mail_subject = '测试邮件主题'
mail_content = '你好!请看邮件。' #邮件内容,文本形式的邮件正文。
msg = MIMEText ( mail_content , 'plain' , 'utf-8' ) #构造邮件主体对象。构造的是纯文本格式的对象。
msg [ "From" ] = mail_sender #给这个对象添加属性,这个操作类似于字典。
msg [ "To" ] = mail_recevier #实际上是给这个对象增加一行消息头内容
msg [ "Subject" ] = mail_subject #直接将该对象打印出来可以看到
mail_server = smtplib . SMTP ( mail_host , 25 )
#创建一个邮件服务器对象,该对象初始化可以指定邮件服务器和邮件端口。也可以不指定,后面再用connect()方法
#连接指定服务器和端口。
mail_server . login ( mail_sender , mail_pass ) #登陆邮件服务器
mail_server . sendmail ( mail_sender , mail_recevier , msg . as_string ()) #发送邮件
mail_server . quit () #退出邮件服务器登陆
总结: smtplib 主要用于控制smtp服务器登陆,认证和邮件收发过程。email 模块主要用于构造邮件主体内容。邮件主体内容包含邮件头部和邮件正文,还有可能包括附件。这些都可以用email模块里的类来构造。
MIMEText() 类是构造邮件主体对象的类。其init函数主要接收3个参数:邮件正文,邮件正文的mime格式,邮件正文字符集。下面是其 init 函数的注释信息:init (self, _text, _subtype=’plain’, _charset=None, , policy=None) | Create a text/ type MIME document. _text is the string for this message object. _subtype is the MIME sub content type, defaulting to “plain”. _charset is the character set parameter added to the Content-Type | header. This defaults to “us-ascii”. Note that as a side-effect, the | Content-Transfer-Encoding header will also be set.
可以看出,_text 是邮件正文的字符串,是必须参数。 _subtype 是邮件内容的mime格式,默认参数是 ‘plain’ ,即明文字符串。还可以选择 ‘html’,即指定接收方MUA用HTML规则解析邮件正文内容。_charset 是邮件内容的字符集,接收方需要用相同的字符集去解析邮件正文内容。默认是None,即用标志ASCII字符集解析。有中文内容时还可以选择 utf-8 。 对MIMEText() 类构造的对象。可以通过类似字典增加键值对的方式增加属性。msg[“From”] = XXX 。这样的属性dir() 看不到,但直接打印出来 :print(msg) ,可以看到 msg 头部增加了一行字符: “From:XXX” 。这就是在构造邮件主体的头部消息。一个邮件必须有三个头部消息:From,To,Subject 。不然会出错,或被识别为垃圾邮件。
MIMEText() 类构造的邮件主体对象,在用sendmail()函数发送时必须转换为字符串。(前面提过,sendmail()智只能发字符串),故msg 还支持 as_string()方法。在邮件发送时需要调用。
如果想发送HTML,可以把mail_content直接指定为HTML字符串。然后用MIMEText() 类构造邮件对象时选择_subtype参数为 ‘html’即可。这样接收方MUA就会用HTML规则去解析邮件内容。这也是mime协议的目的。
关于mime协议: 原始电子邮件只能发送纯文本信息。而后来想对电子邮件发送内容进行扩展,发送更多的种类的信息。这样的信息发出去之后,接收方就需要一个标签来明白收到的信息该如何处理。是该用ASCII码直接解析为纯文本文件呢,还是该用HTML解析呢。这就需要一个消息标签,用来标注信息的格式,告诉消息接收方该用何种方式解析消息。这个消息标签就是mime标签。这种思路后来也扩展到了http协议。这里的消息接收方就是接收电子邮件的 MUA 或接收http消息的网页浏览器。把msg打印出来可以看见其mime标签格式,类似 text/html、image/png 这种格式的数据: print(msg) Content-Type: text/html; charset=”utf-8” MIME-Version: 1.0 Content-Transfer-Encoding: base64 / 前面的text,image标签是由构造对象的类决定的。MIMEText()类就是构造了一个text类的邮件主体,MIMEImage()就是创建一个image类的邮件主体。/ 后面的子类在对象实例化时用 _subtype 参数指定 。实例化时还可以指定字符集和转码方法。这样接收方就会先用base64解码消息,然后用html去解析文本,用utf-8字符集显示。
text模块里MIMEText()类专门用于构建邮件主体。其常用的mime类型是:text/plain 、text/html和octet-stream。分别用于构建纯文本格式的邮件,HTML格式的邮件和邮件附件。
注意引入类MIMEText() 的过程。 这里的 email, mime 都是包(packet),text是模块(mudule),不能直接引入包然后调用,只能一级一级地引入模块,然后调用里面的类和函数。且包不能 dir()看内容,只能用 help() 看包里有啥内容。
msg[“To”] = mail_recevier 是在指定邮件发送目的。mail_recevier可以是一个邮箱地址的字符串,也可以是多个邮箱地址,中间用 ; (分号)组成的长字符串。即向多个目的邮箱发送邮件。常用多个邮箱的地址字符串组成列表,然后用 ;.jion() ,这样构造多目的邮箱字符串。
四、用Python 脚本发正文里带图片的邮件。主要是熟悉 email.mime.image 模块里 MIMEImage() 类。
#-*-coding:utf-8 -*-
import smtplib
from email . mime . image import MIMEImage
mail_host = 'smtp.163.com'
mail_sender = 'zhouyy_1987@163.com'
mail_pass = 'XXXXXXXX'
mail_recevier = 'yiyong.zhou@msxf.com'
mail_subject = '测试邮件主题'
fp = open ( './1.png' , 'rb' )
msg = MIMEImage ( fp . read (), 'png' )
fp . close ()
msg [ "From" ] = mail_sender
msg [ "To" ] = mail_recevier
msg [ "Subject" ] = mail_subject
mail_server = smtplib . SMTP ( mail_host , 25 )
mail_server . login ( mail_sender , mail_pass )
mail_server . sendmail ( mail_sender , mail_recevier , msg . as_string ())
mail_server . quit ()
总结: IMEImage()类构造了一个图片类型的邮件主体对象。mime类型是 image/ ,/ 后面的子类型在对象实例化时用subtype参数指定。这个邮件主体对象和MIMEText()对象一样,可以加上邮件头部信息后,用asstring()函数字符串化,然后用sendmail() 函数发出。这个类的 init函数注释如下: __init (self, _imagedata, _subtype=None, _encoder=, , policy=None, *_params) | Create an image/ type MIME document. _imagedata is a string containing the raw image data. If this data | can be decoded by the standard Python `imghdr’ module, then the | subtype will be automatically included in the Content-Type header. | Otherwise, you can specify the specific image subtype via the _subtype | parameter.
直接用图片读出来的信息,用 IMEImage ()类构造邮件主体,在上面添加邮件头部信息然后发送。注意 msg 仍然要调用 as_string () 方法转换为字符串。 这里仍然有问题,构造的时候指定了 mime 类型为 'image/png' 。但邮箱接收到时邮件正文里仍然不会显示图片,只会把图片当做一个附件。这个问题后面再研究下。
研究结果: email 包里有 mime 包。 mime 包里有三个模块最常用。分别是 text , image , multipart 。里面的类和函数分别用于构建邮件主体,邮件图片附件和多类型的邮件主体。就是说 image 里的 MIMEImage ()只用于构建图片附件,故不能在邮件正文中显示图片。虽然 MIMEImage ()只能构建图片附件对象,但也能把这个对象当成邮件主体对象对待。即给这个对象添加 From , To , Subject 等属性,用 sendmail ()函数发出去。只是接收方只能收到邮件附件,邮件正文内容是空的。要在邮件正文里显示图片,必须使用 multipart 模块里的 MIMEMultipart ()类构造一个多类型的邮件主体对象。看下面的例子。
五、用Python 脚本发带图片的HTML邮件。主要熟悉email.mime.multipart 模块里的 MIMEMultipart()类,和如何在HTML文件里添加图片。
#-*-coding:utf-8 -*-
import smtplib
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
mail_host = 'smtp.163.com'
mail_sender = 'zhouyy_1987@163.com'
mail_pass = 'XXXXXXX'
mail_recevier = 'zhouyy_1987@163.com'
mail_subject = '测试邮件主题'
mail_content = '''<p>你好,邮件发送测试...</p >
<p>这是使用python登录163邮箱发送HTML格式和图片的测试邮件:</p >
<p>图片演示:</p >
<p>
<br>< img src="cid:image1"></br >
<br>< img src="cid:image2"></br >
</P>
'''
mail_body = MIMEText(mail_content, 'html', 'utf-8')
msg = MIMEMultipart()
msg.attach(mail_body)
msg["From"] = mail_sender
msg["To"] = mail_recevier
msg["Subject"] = mail_subject
fp = open('./1.png', 'rb')
image1 = MIMEImage(fp.read(), 'png')
fp.close()
fp2 = open('./2.jpg', 'rb')
image2 = MIMEImage(fp2.read(), 'jpeg')
fp2.close()
image1.add_header('Content-ID', '<image1>')
image2.add_header('Contend-ID', '<image2>')
msg.attach(image1)
msg.attach(image2)
mail_server = smtplib.SMTP(mail_host, 25)
mail_server.login(mail_sender, mail_pass)
mail_server.sendmail(mail_sender, mail_recevier, msg.as_string())
mail_server.quit()
总结: MIMEMultipart()类构造了一个多内容(或者说多类型)的邮件主体对象。之前用的MIMEText() 和 MIMEImage() 都是构造邮件主体对象的类。但都只能构造单一类型的邮件主体。这点从邮件主体的mime类型上就可以看出来。如果要发送多类型的主体(比如既有普通文本,还有html超文本,或者包括后面的发送附件),就要用 MIMEMultipart()类构造邮件主体对象。这个类构造的邮件主体对象就像构建了一个箱子。里面可以有多类型的邮件内容,邮件内容可以直接被“添加”到大箱子里(用attach() 函数)。注意邮件内容添加进去之前也要是合法的邮件对象。比如经过MIMEText()或 MIMEImage()处理过的对象。(构造附件用的是MIMEText()类,有点奇怪。。)
第24行,实例化了一个多类型的邮件主体对象。 第25行,添加了text/html格式的邮件对象(可以没有邮件头部信息,但要有mime 信息,这就是MIMEText() 和MIMEImage() 两个类的意义)。第42行和43行添加了image格式的邮件对象。第47行调用sendmail() 函数把邮件主体对象发出去,注意仍然要调用 as_string() 方法。
注意HTML里怎么包含图片。 这里是给image格式的邮件对象用add_header()方法添加 ‘Content-ID’ 属性,用后面的属性值去对应HTML里的 cid 值,cid:image1 。这里仍然有问题。我发了两张图片,但只有一张能在邮件正文里显示出来,另一张在附件,正文不显示。后面研究下怎么发多张图片。
六、用Python 脚本发带附件的邮件。主要熟悉如何在邮件主体对象里添加附件。
#-*-coding:utf-8 -*-
import smtplib
from email.mime.image import MIMEImage
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
mail_host = 'smtp.163.com'
mail_sender = 'zhouyy_1987@163.com'
mail_pass = 'XXXXXXXX'
mail_recevier = 'zhouyy_1987@163.com'
mail_subject = '测试邮件主题'
mail_content = '''<p>你好,邮件发送测试...</p >
<p>这是使用python登录163邮箱发送HTML格式和图片的测试邮件:</p >
<p>图片演示:</p >
<p>
<br>< img src="cid:image1"></br >
</p >
'''
mail_body = MIMEText(mail_content, 'html', 'utf-8')
msg = MIMEMultipart()
msg.attach(mail_body)
msg["From"] = mail_sender
msg["To"] = mail_recevier
msg["Subject"] = mail_subject
fp = open('./1.png', 'rb')
image1 = MIMEImage(fp.read(), 'png')
fp.close()
image1.add_header('Content-ID', '<image1>')
msg.attach(image1)
att1 = MIMEText(open('./附件1-配置命令.txt', 'rb').read(), 'octet-stream', 'utf-8')
att1.add_header('Content-Disposition', 'attachment', filename = '附件1-配置命令.txt')
att2 = MIMEText(open('./附件2-线路账单.rar', 'rb').read(), 'octet-stream', 'utf-8')
att2.add_header('Content-Disposition', 'attachment', filename = '附件2-线路账单.rar')
att3 = MIMEText(open('./附件3-账单.pdf', 'rb').read(), 'octet-stream', 'utf-8')
att3.add_header('Content-Disposition', 'attachment', filename = '附件3-账单.pdf')
msg.attach(att1)
msg.attach(att2)
msg.attach(att3)
mail_server = smtplib.SMTP(mail_host, 25)
mail_server.login(mail_sender, mail_pass)
mail_server.sendmail(mail_sender, mail_recevier, msg.as_string())
mail_server.quit()
总结: 第36行,39行和第42行,构造了三个附件对象。第45,46,47行将三个对象添加进多类型邮件主体对象中。 这里对附件对象应用 add_header() 方法非常重要。这里错了的话,接收方收到邮件后无法显示名字和扩展名,不知道用什么程序打开附件。add_header()函数的注释如下,注意前面两个参数的含义,以及最后一个参数的赋值方法,要用键值对的方式赋值。 add_header(_name, _value, **_params) method of email.mime.image.MIMEImage instance Extended header setting.
name is the header field to add. keyword arguments can be used to set additional parameters for the header field, with underscores converted to dashes. Normally the parameter will be added as key=”value” unless value is None, in which case only the key will be added. If a parameter value contains non-ASCII characters it can be specified as a three-tuple of (charset, language, value), in which case it will be encoded according to RFC2231 rules. Otherwise it will be encoded using the utf-8 charset and a language of ‘’. Examples: msg.add_header(‘content-disposition’, ‘attachment’, filename=’bud.gif’) msg.add_header(‘content-disposition’, ‘attachment’, filename=(‘utf-8’, ‘’, Fußballer.ppt’)) msg.add_header(‘content-disposition’, ‘attachment’, filename=’Fußballer.ppt’))
构造附件对象,这个例子里用的是MIMEText()类,mime type 是text/octet-stream。除了这种方法外,还可以用mime包里的另一个模块:application, 里面的 MIMEApplication()类来构造附件对象。这样可以把36、37行改为:
att1 = MIMEApplication(open('./附件1-配置命令.txt', 'rb').read())
att1.add_header('Content-Disposition', 'attachment', filename = '附件1-配置命令.txt')
七、用第三方模块mailer发送带附件,带图片的HTML邮件。
# -*- coding: UTF-8 -*-
from mailer import Mailer
from mailer import Message
from email.header import Header
mail_host = 'smtp.163.com'
mail_sender = 'zhouyy_1987@163.com'
mail_pass = 'XXXXXXXX'
mail_recevier = 'zhouyy_1987@163.com'
mail_subject = Header('测试邮件主题', 'utf-8').encode()
mail_content = '''<p>你好,邮件发送测试...</p >
<p>这是使用python登录163邮箱发送HTML格式和图片的测试邮件:</p >
<p>图片演示:</p >
<p>
<br>< img src="cid:image1"></br >
</p >
'''
mail_attach = "./1.png"
mail_image = './2.jpg'
#mail_body = '这是邮件主体正文。'
msg = Message(From = mail_sender, To = mail_recevier, charset = 'utf-8', Html = mail_content)
msg.Subject = mail_subject
#msg.Body = mail_body
msg.attach(mail_attach)
msg.attach(mail_image, cid = 'image1')
mail_server = Mailer(host = mail_host, pwd = mail_pass, usr = mail_sender)
mail_server.send(msg)
print('Finish!')
总结: 1、smtplib 和email 是Python的标准库。分别对smtp协议、命令和构造邮件主体内容过程进行了封装。前面的示例演示了如何使用这两个库构造邮件主体及发送邮件。归纳一下 email这个库。其原始的邮件对象叫:Message 对象。另外根据MIME类型的不同,还有多种类用于构造不同MIME类型的邮件对象。其多种对象(类)的继承关系如下: Message +- MIMEBase +- MIMEMultipart +- MIMENonMultipart +- MIMEMessage +- MIEMText +- MIMEImage 如果用MIMEText类,就是构造了一个文本的邮件对象(纯文本或HTML文本),如果用MIMEImage类,就是构造了一个作为附件的图片邮件对象。而要把多类型的邮件对象组合起来,就要用MIMEMultipart类,构造一个多类型的邮件主体对象(就是前面提到的那个“箱子”)。而MIMEBase是基类,可以构造所有的对象。
2、上面分析了email库里面的用于构造邮件对象的多个类及其继承关系。可以看到,用email这个模块构造邮件主体对象还是有些复杂,用起来比较麻烦。原因在于email这个模块抽象的程度还不够。所以就有了很多第三方模块,一般是基于smtplib 和email这连个标准库进行的二次开发,把构造邮件的过程进行了进一步的抽象,让整个工作变得更加简单。mailer就是其中的一个。
3、第二行和第三行,从mailer模块里引入两个类:Message类和Mailer类,分别用于构造邮件主体对象和邮件服务器对象。第23行构造了一个邮件主体对象。和email库里的构造邮件主体对象类似,这里在构造的时候可以直接指定邮件正文。即在实例化对象时,用关键字参数指定邮件内容正文:HTML或Body参数,指定HTML格式的或纯文本格式的正文。邮件主体对象实例化时,也会指定邮件发送者和邮件接收者。这两个属性都会给在邮件发送时被提取出来,交给邮件服务器,作为 mail from 和 rcpt to 命令的参数。这样,发件人和收件人不需要写两遍。这个过程有点类似于直接在客户端上编写邮件,在邮件头部写发件人和收件人。Message类的init函数参数如下: class Message(builtins.object) | Message(kwargs) | Set the To, From, Reply-To, Subject, and Body attributes as plain-text strin gs. | Optionally, set the Html attribute to send an HTML email, or use the | attach() method to attach files. | init (self, kwargs) | Parameters and default values (parameter names are case insensitive): | To=None, From=None, RTo=None, CC=None, BCC=None, Subject=None, Body= one, Html=None, | Date=None, Attachments=None, Charset=None, Headers=None
4、在Message类构造的对象里,邮件相关的内容都是一种属性,可以按照对一个对象属性赋值的方法添加值。比如24行,加了一个Subject 属性,作为邮件主题。 25行,加了一个Body属性,作为邮件正文内容。26和27就和之前的MIMEMultipart类相似地,用attach()函数加附件和图片对象。注意这里的图片资源,直接用文件路径代替即可,不需要 open后read出来再处理。也不需要构造成邮件对象后再加入主体邮件中。比起email中的方法要简单很多。