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(Tornado)模拟登录小米抢手机
Nov 12 Python
Python functools模块学习总结
May 09 Python
Queue 实现生产者消费者模型(实例讲解)
Nov 13 Python
python实现简单的单变量线性回归方法
Nov 08 Python
Django框架实现的简单分页功能示例
Dec 04 Python
Python学习笔记之For循环用法详解
Aug 14 Python
Python使用matplotlib绘制Logistic曲线操作示例
Nov 28 Python
使用python-pptx包批量修改ppt格式的实现
Feb 14 Python
python使用pymongo与MongoDB基本交互操作示例
Apr 09 Python
django admin管理工具自定义时间区间筛选器DateRangeFilter介绍
May 19 Python
python实现启动一个外部程序,并且不阻塞当前进程
Dec 05 Python
python爬不同图片分别保存在不同文件夹中的实现
Apr 02 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
php带密码功能并下载远程文件保存本地指定目录 修改加强版
2010/05/16 PHP
PHP实现多条件查询实例代码
2010/07/17 PHP
php的memcached客户端memcached
2011/06/14 PHP
windows下安装php的memcache模块的方法
2015/04/07 PHP
通过Javascript创建一个选择文件的对话框代码
2012/06/16 Javascript
禁用JavaScript控制台调试的方法
2014/03/07 Javascript
JavaScript获取路径设计源码
2014/05/22 Javascript
JavaScript获取页面中表单(form)数量的方法
2015/04/03 Javascript
Bootstrap基本组件学习笔记之分页(12)
2016/12/08 Javascript
BootstrapTable请求数据时设置超时(timeout)的方法
2017/01/22 Javascript
基于JavaScript实现滑动门效果
2017/03/16 Javascript
微信小程序 页面跳转传值实现代码
2017/07/27 Javascript
简单谈谈vue的过渡动画(推荐)
2017/10/11 Javascript
Vue项目中Api的组织和返回数据处理的操作
2019/11/04 Javascript
对Python 2.7 pandas 中的read_excel详解
2018/05/04 Python
Python常用的json标准库
2019/02/19 Python
python 数据提取及拆分的实现代码
2019/08/26 Python
python global关键字的用法详解
2019/09/05 Python
python字符串判断密码强弱
2020/03/18 Python
python如何编写win程序
2020/06/08 Python
Html5跳转到APP指定页面的实现
2020/01/14 HTML / CSS
伯克斯奥特莱斯:Burkes Outlet
2019/03/30 全球购物
乔迁宴答谢词
2014/01/21 职场文书
家长给孩子的评语
2014/01/30 职场文书
教师职称自我鉴定
2014/02/12 职场文书
改作风抓落实促发展心得体会
2014/09/10 职场文书
六查六看个人剖析材料
2014/10/14 职场文书
员工2014年度工作总结
2014/12/09 职场文书
小班教师个人总结
2015/02/05 职场文书
2015年度绩效考核工作总结
2015/05/27 职场文书
医院病假条范文
2015/08/17 职场文书
感谢信的技巧及范例
2019/05/15 职场文书
职工的安全责任书范文!
2019/07/02 职场文书
python操作xlsx格式文件并读取
2021/06/02 Python
Django框架模板用法详解
2022/06/10 Python
MySQL池化框架学习接池自定义
2022/07/23 MySQL