smtp协议的基本命令包括:
HELO 向服务器标识用户身份
MAIL 初始化邮件传输 mail from:
RCPT 标识单个的邮件接收人;常在MAIL命令后面,可有多个rcpt to:
DATA 在单个或多个RCPT命令后,表示所有的邮件接收人已标识,并初始化数据传输,以.结束
VRFY 用于验证指定的用户/邮箱是否存在;由于安全方面的原因,服务器常禁止此命令
EXPN 验证给定的邮箱列表是否存在,扩充邮箱列表,也常被禁用
HELP 查询服务器支持什么命令
NOOP 无操作,服务器应响应OK
QUIT 结束会话
RSET 重置会话,当前传输被取消
MAIL FROM 指定发送者地址
RCPT TO 指明的接收者地址
一般smtp会话有两种方式,一种是邮件直接投递,就是说,比如你要发邮件給zzz@163.com,那就直接连接163.com的邮件服务器,把信投給zzz@163.com; 另 一种是验证过后的发信,它的过程是,比如你要发邮件給zzz@163.com,你不是直接投到163.com,而是通过自己在sina.com的另一个邮 箱来发。这样就要先连接sina.com的smtp服务器,然后认证,之后在把要发到163.com的信件投到sina.com上,sina.com会帮 你把信投递到163.com。
第一种方式的命令流程基本是这样:
1. helo
2. mail from
3. rcpt to
4. data
5. quit
但是第一种发送方式一般有限制的,就是rcpt to指定的这个邮件接收者必须在这个服务器上存在,否则是不会接收的。 先看看代码:
import os, sys, string
import smtplib
# 邮件服务器地址
mailserver = " smtp.163.com "
# smtp会话过程中的mail from地址
from_addr = " asfgysg@zxsdf.com "
# smtp会话过程中的rcpt to地址
to_addr = " zhaoweikid@163.com "
# 信件内容
msg = " test mail "
svr = smtplib.SMTP(mailserver)
# 设置为调试模式,就是在会话过程中会有输出信息
svr.set_debuglevel( 1 )
# helo命令,docmd方法包括了获取对方服务器返回信息
svr.docmd( " HELO server " )
# mail from, 发送邮件发送者
svr.docmd( " MAIL FROM: <%s> " % from_addr)
# rcpt to, 邮件接收者
svr.docmd( " RCPT TO: <%s> " % to_addr)
# data命令,开始发送数据
svr.docmd( " DATA " )
# 发送正文数据
svr.send(msg)
# 比如以 . 作为正文发送结束的标记,用send发送的,所以要用getreply获取返回信息
svr.send( " . " )
svr.getreply()
# 发送结束,退出
svr.quit()
注意的是,163.com是有反垃圾邮件功能的,想上面的这种投递邮件的方法不一定能通过反垃圾邮件系统的检测的。所以一般不推荐个人这样发送。
第二种有点不一样:
1.ehlo
2.auth login
3.mail from
4.rcpt to
5.data
6.quit
相对于第一种来说,多了一个认证过程,就是auth login这个过程。
import os, sys, string
import smtplib
import base64
# 邮件服务器地址
mailserver = " smtp.163.com "
# 邮件用户名
username = " xxxxxx@163.com "
# 密码
password = " xxxxxxx "
# smtp会话过程中的mail from地址
from_addr = " xxxxxx@163.com "
# smtp会话过程中的rcpt to地址
to_addr = " yyyyyy@163.com "
# 信件内容
msg = " my test mail "
svr = smtplib.SMTP(mailserver)
# 设置为调试模式,就是在会话过程中会有输出信息
svr.set_debuglevel( 1 )
# ehlo命令,docmd方法包括了获取对方服务器返回信息
svr.docmd( " EHLO server " )
# auth login 命令
svr.docmd( " AUTH LOGIN " )
# 发送用户名,是base64编码过的,用send发送的,所以要用getreply获取返回信息
svr.send(base64.encodestring(username))
svr.getreply()
# 发送密码
svr.send(base64.encodestring(password))
svr.getreply()
# mail from, 发送邮件发送者
svr.docmd( " MAIL FROM: <%s> " % from_addr)
# rcpt to, 邮件接收者
svr.docmd( " RCPT TO: <%s> " % to_addr)
# data命令,开始发送数据
svr.docmd( " DATA " )
# 发送正文数据
svr.send(msg)
# 比如以 . 作为正文发送结束的标记
svr.send( " . " )
svr.getreply()
# 发送结束,退出
svr.quit()
上面说的是最普通的情况,但是不能忽略的是现在好多企业邮件是支持安全邮件的,就是通过SSL发送的邮件,这个怎么发呢?SMTP对SSL安全邮件的支持 有两种方案,一种老的是专门开启一个465端口来接收ssl邮件,另一种更新的做法是在标准的25端口的smtp上增加一个starttls的命令来支 持。
看看第一种怎么办:
import os, sys, string, socket
import smtplib
class SMTP_SSL (smtplib.SMTP):
def __init__ (self, host = '' , port = 465 , local_hostname = None, key = None, cert = None):
self.cert = cert
self.key = key
smtplib.SMTP. __init__ (self, host, port, local_hostname)
def connect(self, host = ' localhost ' , port = 465 ):
if not port and (host.find( ' : ' ) == host.rfind( ' : ' )):
i = host.rfind( ' : ' )
if i >= 0:
host, port = host[:i], host[i + 1 :]
try : port = int(port)
except ValueError:
raise socket.error, " nonnumeric port "
if not port: port = 654
if self.debuglevel > 0: print >> stderr, ' connect: ' , (host, port)
msg = " getaddrinfo returns an empty list "
self.sock = None
for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
af, socktype, proto, canonname, sa = res
try :
self.sock = socket.socket(af, socktype, proto)
if self.debuglevel > 0: print >> stderr, ' connect: ' , (host, port)
self.sock.connect(sa)
# 新增加的创建ssl连接
sslobj = socket.ssl(self.sock, self.key, self.cert)
except socket.error, msg:
if self.debuglevel > 0:
print >> stderr, ' connect fail: ' , (host, port)
if self.sock:
self.sock.close()
self.sock = None
continue
break
if not self.sock:
raise socket.error, msg
# 设置ssl
self.sock = smtplib.SSLFakeSocket(self.sock, sslobj)
self.file = smtplib.SSLFakeFile(sslobj);
(code, msg) = self.getreply()
if self.debuglevel > 0: print >> stderr, " connect: " , msg
return (code, msg)
if __name__ == ' __main__ ' :
smtp = SMTP_SSL( ' 192.168.2.10 ' )
smtp.set_debuglevel( 1 )
smtp.sendmail( " zzz@xxx.com " , " zhaowei@zhaowei.com " , " xxxxxxxxxxxxxxxxx " )
smtp.quit()
这里我是从原来的smtplib.SMTP派生出了新的SMTP_SSL类,它专门来处理ssl连接。我这里测试的192.168.2.10是我自己的测试服务器.
第二种是新增加了starttls的命令,这个很简单,smtplib里就有这个方法,叫smtplib.starttls()。当然,不是所有的邮件系 统都支持安全邮件的,这个需要从ehlo的返回值里来确认,如果里面有starttls,才表示支持。相对于发送普通邮件的第二种方法来说,只需要新增加 一行代码就可以了:
import os, sys, string
import smtplib
import base64
# 邮件服务器地址
mailserver = " smtp.163.com "
# 邮件用户名
username = " xxxxxx@163.com "
# 密码
password = " xxxxxxx "
# smtp会话过程中的mail from地址
from_addr = " xxxxxx@163.com "
# smtp会话过程中的rcpt to地址
to_addr = " yyyyyy@163.com "
# 信件内容
msg = " my test mail "
svr = smtplib.SMTP(mailserver)
# 设置为调试模式,就是在会话过程中会有输出信息
svr.set_debuglevel( 1 )
# ehlo命令,docmd方法包括了获取对方服务器返回信息,如果支持安全邮件,返回值里会有starttls提示
svr.docmd( " EHLO server " )
svr.starttls() # <------ 这行就是新加的支持安全邮件的代码!
# auth login 命令
svr.docmd( " AUTH LOGIN " )
# 发送用户名,是base64编码过的,用send发送的,所以要用getreply获取返回信息
svr.send(base64.encodestring(username))
svr.getreply()
# 发送密码
svr.send(base64.encodestring(password))
svr.getreply()
# mail from, 发送邮件发送者
svr.docmd( " MAIL FROM: <%s> " % from_addr)
# rcpt to, 邮件接收者
svr.docmd( " RCPT TO: <%s> " % to_addr)
# data命令,开始发送数据
svr.docmd( " DATA " )
# 发送正文数据
svr.send(msg)
# 比如以 . 作为正文发送结束的标记
svr.send( " . " )
svr.getreply()
# 发送结束,退出
svr.quit()
注意: 以上的代码为了方便我都没有判断返回值,严格说来,是应该判断一下返回的代码的,在smtp协议中,只有返回代码是2xx或者3xx才能继续下一步,返回4xx或5xx的,都是出错了。
____________________________________________________________
python模块学习 ---- smtplib 邮件发送
在基于互联网的应用中,程序经常需要自动地发送电子邮件。如:一个网站的注册系统会在用户注册时发送一封邮件来确认注册;当用户忘记登陆密码的时 候,通过邮件来取回密码。smtplib模块是python中smtp(简单邮件传输协议)的客户端实现。我们可以使用smtplib模块,轻松的发送电 子邮件。下面的例子用了不到十行代码来发送电子邮件:- #coding=gbk
- import smtplib
- smtp = smtplib.SMTP()
- smtp.connect("smtp.yeah.net", "25")
- smtp.login('用户名', '密码')
- smtp.sendmail('from@yeah.net', 'to@21cn.com', 'From: from@yeah.net/r/nTo: to@21cn.com/r/nSubject: this is a email from python demo/r/n/r/nJust for test~_~')
- smtp.quit()
这个例子够简单吧^_^!下面详细介绍stmplib模块中的类和方法。
smtplib.SMTP([host[, port[, local_hostname[, timeout]]]])
SMTP类构造函数,表示与SMTP服务器之间的连接,通过这个连接我们可以向smtp服务器发送指令,执行相关操作(如:登陆、发送邮件)。 该类提供了许多方法,将在下面介绍。它的所有参数都是可选的,其中host参数表示smtp服务器主机名,上面例子中的smtp主机 为"smtp.yeah.net";port表示smtp服务的端口,默认是25;如果在创建SMTP对象的时候提供了这两个参数,在初始化的时候会自动 调用connect方法去连接服务器。
smtplib模块还提供了SMTP_SSL类和LMTP类,对它们的操作与SMTP基本一致。
smtplib.SMTP提供的方法:
SMTP.set_debuglevel(level)
设置是否为调试模式。默认为False,即非调试模式,表示不输出任何调试信息。
SMTP.connect([host[, port]])
连接到指定的smtp服务器。参数分别表示smpt主机和端口。注意: 也可以在host参数中指定端口号(如:smpt.yeah.net:25),这样就没必要给出port参数。
SMTP.docmd(cmd[, argstring])
向smtp服务器发送指令。可选参数argstring表示指令的参数。下面的例子完全通过调用docmd方法向服务器发送指令来实现邮件的发送(在smtp.yeah.net邮件服务器上试验通过。其他邮件服务器没有试过):
- import smtplib, base64, time
- userName = base64.encodestring('from').strip()
- password = base64.encodestring('password').strip()
- smtp = smtplib.SMTP()
- smtp.connect("smtp.yeah.net:25")
- print smtp.docmd('helo', 'from')
- print smtp.docmd('auth login')
- print smtp.docmd(userName)
- print smtp.docmd(password)
- print smtp.docmd('mail from:', '<from@yeah.net>')
- print smtp.docmd('rcpt to:', '<from@yeah.net>')
- #data 指令表示邮件内容
- print smtp.docmd('data')
- print smtp.docmd('''''from: from@yeah.net
- to: from@yeah.net
- subject: subject
- email body
- .
- ''')
- smtp.quit()
SMTP.helo([hostname])
使用"helo"指令向服务器确认身份。相当于告诉smtp服务器“我是谁”。
SMTP.has_extn(name)
判断指定名称在服务器邮件列表中是否存在。出于安全考虑,smtp服务器往往屏蔽了该指令。
SMTP.verify(address)
判断指定邮件地址是否在服务器中存在。出于安全考虑,smtp服务器往往屏蔽了该指令。
SMTP.login(user, password)
登陆到smtp服务器。现在几乎所有的smtp服务器,都必须在验证用户信息合法之后才允许发送邮件。
SMTP.sendmail(from_addr, to_addrs, msg[, mail_options, rcpt_options])
发送邮件。这里要注意一下第三个参数,msg是字符串,表示邮件。我们知道邮件一般由标题,发信人,收件人,邮件内容,附件等构成,发送邮件的时候,要注意msg的格式。这个格式就是smtp协议中定义的格式。在上面的例子中,msg的值为:
- '''''From: from@yeah.net
- To: to@21cn.com
- Subject: test
- just for test'''
这个字符串的的意思表示邮件发件人为"from@yeah.net",收件人为" to@21cn.com", 邮件标题为"test",邮件内容为"just for test"。细心的你可能会疑问:如果要发送的邮件内容很复杂,包含图片、视频、附件等内容,按照MIME的格式来拼接字符串,将是一件非常麻烦的事。不 用担心,python已经考虑到了这点,它为我们提供了email模块,使用该模块可以轻松的发送带图片、视频、附件等复杂内容的邮件。在介绍完 smtplib模块之后,我会简单介绍email模块的基本使用。
SMTP.quit()
断开与smtp服务器的连接,相当于发送"quit"指令。
email及其相关子模块
emial模块用来处理邮件消息,包括MIME和其他基于RFC 2822 的消息文档。使用这些模块来定义邮件的内容,是非常简单的。下面是一些常用的类:
classemail.mime.multipart.MIMEMultipart: 多个MIME对象的集合。
classemail.mime.audio.MIMEAudio: MIME音频对象。
classemail.mime.image.MIMEImage: MIME二进制文件对象。
classemail.mime.text.MIMEText: MIME文本对象。
看上面的解释可能会觉得云里雾里,其实我对smtp, MIME的理解也很肤浅。但在大多数时候,我们只要会用就可以了。下面是一个简单的例子来演示如何使用这些类来发送带附件的邮件:
- #coding=gbk
- import smtplib, mimetypes
- from email.mime.text import MIMEText
- from email.mime.multipart import MIMEMultipart
- from email.mime.image import MIMEImage
- msg = MIMEMultipart()
- msg['From'] = "from@yeah.net"
- msg['To'] = 'to@21cn.com'
- msg['Subject'] = 'email for tesing'
- #添加邮件内容
- txt = MIMEText("这是邮件内容~~")
- msg.attach(txt)
- #添加二进制附件
- fileName = r'e:/PyQt4.rar'
- ctype, encoding = mimetypes.guess_type(fileName)
- if ctype is None or encoding is not None:
- ctype = 'application/octet-stream'
- maintype, subtype = ctype.split('/', 1)
- att1 = MIMEImage((lambda f: (f.read(), f.close()))(open(fileName, 'rb'))[0], _subtype = subtype)
- att1.add_header('Content-Disposition', 'attachment', filename = fileName)
- msg.attach(att1)
- #发送邮件
- smtp = smtplib.SMTP()
- smtp.connect('smtp.yeah.net:25')
- smtp.login('from', '密码')
- smtp.sendmail('from@yeah.net', 'to@21cn.com', msg.as_string())
- smtp.quit()
- print '邮件发送成功'
是不是很简单。简单就是美,用最少的代码把问题解决,这就是Python。更多关于smtplib的信息,请参考Python手册 smtplib模块。
___________________________________________________
Code Example:
'''
# -*- coding: cp936 -*-
# 甄码农,20120307
import smtplib
from email.mime.text import MIMEText
mail_host = 'smtp.126.com'
mail_user = 'xxx@126.com'
mail_pwd = 'hellopwd'
mail_to = 'xxao@gmail.com'
mail_cc = 'xx@xx.com'
mail_bcc = 'xx@qq.com'
content = 'this is a mail sent with python'
#表头信息
msg = MIMEText(content)
msg['From'] = mail_user
msg['Subject'] = 'this is a python test mail'
msg['To'] = mail_to
msg['Cc'] = mail_cc
msg['Bcc'] = mail_bcc
try:
s = smtplib.SMTP()
s.connect(mail_host)
#login
s.login(mail_user,mail_pwd)
#send mail
s.sendmail(mail_user,[mail_to,mail_cc,mail_bcc],msg.as_string())
s.close()
print 'success'
except Exception ,e:
print e
'''