Python使用poplib模块和smtplib模块收发电子邮件的教程


Posted in Python onJuly 02, 2016

poplib模块接收邮件
python的poplib模块是用来从pop3收取邮件的,也可以说它是处理邮件的第一步。
POP3协议并不复杂,它也是采用的一问一答式的方式,你向服务器发送一个命令,服务器必然会回复一个信息。pop3命令码如下:

命令 poplib方法参数
  状态

 描述
-----------------------------------------------------------------------------------------------
USER
user   username
认可
用户名,此命令与下面的pass命令若成功,将导致状态转换
PASS
pass_ 
password
认可
用户密码
 
APOP
apop
 Name,Digest 认可  Digest是MD5消息摘要
-----------------------------------------------------------------------------------------------
STAT
stat 
None
  处理
请求服务器发回关于邮箱的统计资料,如邮件总数和总字节数
UIDL
uidl
 [Msg#]
 处理
返回邮件的唯一标识符,POP3会话的每个标识符都将是唯一的
LIST
list
 [Msg#]
 处理
返回邮件数量和每个邮件的大小
RETR
retr
 [Msg#]   处理
返回由参数标识的邮件的全部文本
DELE
dele
 [Msg#]   处理
服务器将由参数标识的邮件标记为删除,由quit命令执行
RSET
rset
 None
  处理  服务器将重置所有标记为删除的邮件,用于撤消DELE命令
TOP
 top 
 [Msg#]
 处理  服务器将返回由参数标识的邮件前n行内容,n必须是正整数
NOOP
noop
 None

处理
服务器返回一个肯定的响应
----------------------------------------------------------------------------------------------
QUIT
quit   None

更新

 

python的poplib也针对这些命令分别提供了对应的方法,上面在第二列里已经标出来。收取邮件的过程一般是:
1. 连接pop3服务器 (poplib.POP3.__init__)
2. 发送用户名和密码进行验证 (poplib.POP3.user poplib.POP3.pass_)
3. 获取邮箱中信件信息 (poplib.POP3.stat)
4. 收取邮件 (poplib.POP3.retr)
5. 删除邮件 (poplib.POP3.dele)
6. 退出 (poplib.POP3.quit)
注意的是,上面我在括号里写的是使用什么方法来完成这个操作,在实际的代码中不能那样写,应该是创建poplib.POP3的对象,然后,调用这个对象的方法。比如:

poplib.POP3.quit

应该理解为

a = poplib.POP3(host)
a.quit()

下面看看实际的代码:

#-*- encoding: gb2312 -*-
import os, sys, string
import poplib

# pop3服务器地址
host = "pop3.163.com"
# 用户名
username = "xxxxxx@163.com"
# 密码
password = "xxxxxxx"
# 创建一个pop3对象,这个时候实际上已经连接上服务器了
pp = poplib.POP3(host)
# 设置调试模式,可以看到与服务器的交互信息
pp.set_debuglevel(1)
# 向服务器发送用户名
pp.user(username)
# 向服务器发送密码
pp.pass_(password)
# 获取服务器上信件信息,返回是一个列表,第一项是一共有多上封邮件,第二项是共有多少字节
ret = pp.stat()
print ret
# 需要取出所有信件的头部,信件id是从1开始的。
for i in range(1, ret[0]+1):
  # 取出信件头部。注意:top指定的行数是以信件头为基数的,也就是说当取0行,
  # 其实是返回头部信息,取1行其实是返回头部信息之外再多1行。
  mlist = pp.top(i, 0)
  print 'line: ', len(mlist[1])
# 列出服务器上邮件信息,这个会对每一封邮件都输出id和大小。不象stat输出的是总的统计信息
ret = pp.list()
print ret
# 取第一封邮件完整信息,在返回值里,是按行存储在down[1]的列表里的。down[0]是返回的状态信息
down = pp.retr(1)
print 'lines:', len(down)
# 输出邮件
for line in down[1]:
  print line
# 退出
pp.quit()

在有些地方,有安全邮件这一说,其实是对pop3做了ssl加密。这样的,poplib一样可以处理,只不过不是用POP3这个类,而是用POP3_SSL, 他们的方法都一样。因此支持ssl在上面代码中,替换创建pop3对象的一行为:

pp = poplib.POP3_SSL(host)

smtplib: 用python发送SSL/TLS安全邮件
python的smtplib提供了一种很方便的途径发送电子邮件。它对smtp协议进行了简单的封装。
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指定的这个邮件接收者必须在这个服务器上存在,否则是不会接收的。 先看看代码:

#-*- encoding: gb2312 -*-
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这个过程。

#-*- encoding: gb2312 -*-
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的命令来支持。
    看看第一种怎么办:

#-*- encoding: gb2312 -*-
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,才表示支持。相对于发送普通邮件的第二种方法来说,只需要新增加一行代码就可以了:

#-*- encoding: gb2312 -*-
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 相关文章推荐
python之import机制详解
Jul 03 Python
Python数据库的连接实现方法与注意事项
Feb 27 Python
深入理解Python装饰器
Jul 27 Python
windows下安装Python和pip终极图文教程
Mar 05 Python
Python正则捕获操作示例
Aug 19 Python
Flask框架实现给视图函数增加装饰器操作示例
Jul 16 Python
python通过ffmgep从视频中抽帧的方法
Dec 05 Python
深入了解Django中间件及其方法
Jul 26 Python
django-初始配置(纯手写)详解
Jul 30 Python
QML使用Python的函数过程解析
Sep 26 Python
python编写计算器功能
Oct 25 Python
Python可视化学习之seaborn绘制矩阵图详解
Feb 24 Python
python图片验证码生成代码
Jul 02 #Python
Python彩色化Linux的命令行终端界面的代码实例分享
Jul 02 #Python
Python使用迭代器打印螺旋矩阵的思路及代码示例
Jul 02 #Python
Python使用ntplib库同步校准当地时间的方法
Jul 02 #Python
Python编程中对super函数的正确理解和用法解析
Jul 02 #Python
Python中的复制操作及copy模块中的浅拷贝与深拷贝方法
Jul 02 #Python
快速排序的算法思想及Python版快速排序的实现示例
Jul 02 #Python
You might like
PHP5.0正式发布 不完全兼容PHP4 新增多项功能
2006/10/09 PHP
15种PHP Encoder的比较
2007/04/17 PHP
PHP中的闭包(匿名函数)浅析
2015/02/07 PHP
yii框架无限极分类的实现方法
2017/04/08 PHP
JavaScript 匿名函数(anonymous function)与闭包(closure)
2011/10/04 Javascript
jquery中的mouseleave和mouseout的区别 模仿下拉框效果
2012/02/07 Javascript
JavaScript数值数组排序示例分享
2014/05/27 Javascript
JavaScript中用字面量创建对象介绍
2014/12/31 Javascript
Ext JS框架中日期函数的用法及日期选择控件的实现
2016/05/21 Javascript
Angularjs中$http以post请求通过消息体传递参数的实现方法
2016/08/05 Javascript
javascript中对象的定义、使用以及对象和原型链操作小结
2016/12/14 Javascript
BootstrapValidator实现注册校验和登录错误提示效果
2017/03/10 Javascript
移动端使用localResizeIMG4压缩图片
2017/04/22 Javascript
深入浅析JS中的严格模式
2018/06/04 Javascript
vue中组件的过渡动画及实现代码
2018/11/21 Javascript
node.js使用express框架进行文件上传详解
2019/03/03 Javascript
JavaScript 中判断变量是否为数字的示例代码
2020/10/22 Javascript
在Python的Django框架中编写错误提示页面
2015/07/22 Python
python实现二分查找算法
2017/09/21 Python
unittest+coverage单元测试代码覆盖操作实例详解
2018/04/04 Python
python应用Axes3D绘图(批量梯度下降算法)
2020/03/25 Python
在django中查询获取数据,get, filter,all(),values()操作
2020/08/09 Python
python 制作网站筛选工具(附源码)
2021/01/21 Python
英国时尚家具、家居饰品及礼品商店:Graham & Green
2016/09/15 全球购物
英国儿童图书网站:Scholastic
2017/03/26 全球购物
美国知名的家庭连锁百货商店:Boscov’s
2017/07/27 全球购物
英国最大的香水商店:The Fragrance Shop
2018/07/06 全球购物
优秀团员个人事迹材料
2014/01/29 职场文书
开工仪式主持词
2014/03/20 职场文书
建设投标担保书
2014/05/13 职场文书
党员群众路线对照检查材料思想汇报
2014/09/17 职场文书
超市食品安全承诺书
2015/04/29 职场文书
阳光体育运动标语口号
2015/12/26 职场文书
oracle表分区的概念及操作
2021/04/24 Oracle
在Django中使用MQTT的方法
2021/05/10 Python
将Python代码打包成.exe可执行文件的完整步骤
2021/05/12 Python