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 文件重命名工具代码
Jul 26 Python
Python的函数的一些高阶特性
Apr 27 Python
深入理解Python中的*重复运算符
Oct 28 Python
解决pycharm运行时interpreter为空的问题
Oct 29 Python
python 多线程中子线程和主线程相互通信方法
Nov 09 Python
Python 寻找局部最高点的实现
Dec 05 Python
pytorch:实现简单的GAN示例(MNIST数据集)
Jan 10 Python
Matplotlib使用字符串代替变量绘制散点图的方法
Feb 17 Python
python实现简单的购物程序代码实例
Mar 03 Python
基于python实现查询ip地址来源
Jun 02 Python
史上最详细的Python打包成exe文件教程
Jan 17 Python
Python IO文件管理的具体使用
Mar 20 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中strstr、strrchr、substr、stristr四个函数的区别总结
2014/09/22 PHP
服务器安全设置的几个注册表设置
2007/07/28 Javascript
web前端开发也需要日志
2010/12/09 Javascript
js点击事件链接的问题解决
2014/04/25 Javascript
Jquery动态添加及删除页面节点元素示例代码
2014/06/16 Javascript
node.js+Ajax实现获取HTTP服务器返回数据
2014/11/26 Javascript
JS实现的适合做faq或menu滑动效果示例
2016/11/17 Javascript
Javascript实现登录记住用户名和密码功能
2017/03/22 Javascript
基于js 字符串indexof与search方法的区别(详解)
2017/12/04 Javascript
使用vue制作探探滑动堆叠组件的实例代码
2018/03/07 Javascript
Canvas实现微信红包照片效果
2018/08/21 Javascript
Angular8基础应用之表单及其验证
2019/08/11 Javascript
解决echarts图表使用v-show控制图表显示不全的问题
2020/07/19 Javascript
Vue开发中常见的套路和技巧总结
2020/11/24 Vue.js
python常用知识梳理(必看篇)
2017/03/23 Python
Python决策树之基于信息增益的特征选择示例
2018/06/25 Python
使用matlab或python将txt文件转为excel表格
2019/11/01 Python
xadmin使用formfield_for_dbfield函数过滤下拉表单实例
2020/04/07 Python
Python3 webservice接口测试代码详解
2020/06/23 Python
python json.dumps() json.dump()的区别详解
2020/07/14 Python
Python生成器传参数及返回值原理解析
2020/07/22 Python
Python 使用生成器代替线程的方法
2020/08/04 Python
以太网Ethernet IEEE802.3
2013/08/05 面试题
涉外经济法专业毕业生推荐信
2013/11/24 职场文书
企业项目策划书
2014/01/11 职场文书
xxx同志考察材料
2014/02/07 职场文书
消防战士优秀事迹材料
2014/02/13 职场文书
制药工程专业职业生涯规划范文
2014/03/10 职场文书
机械工程学院大学生求职信
2014/05/25 职场文书
银行求职信
2014/05/31 职场文书
中华在我心中演讲稿
2014/09/13 职场文书
2014年旅游局法制宣传日活动总结
2014/11/01 职场文书
解除劳动关系协议书2篇
2014/11/28 职场文书
倡议书范文大全
2015/04/28 职场文书
2015年青年志愿者工作总结
2015/05/20 职场文书
Python文件的操作示例的详细讲解
2021/04/08 Python