python邮件管理

一、基本概念

1、协议简介

  • POP3
    收邮件。允许电子邮件客户端下载服务器上的邮件,但是在客户端的操作(如移动邮件、标记已读等),不会反馈到服务器上。
  • IAMP
    收邮件。在电子邮件客户端收取的邮件仍然保留在服务器上,同时在客户端上的操作都会反馈到服务器上。
  • SMTP
    发邮件。必须在提供了账户名和密码之后才可以登录 SMTP 服务器,这就使得那些垃圾邮件的散播者无可乘之机。

2、邮箱地址

189邮箱(SSL加密)

  • POP3:pop.189.cn 端口995
  • SMTP:smtp.189.cn 端口465
  • IMAP:imap.189.cn 端口993

公司邮箱(SSL加密)

  • POP3:pop.chinatelecom.cn 端口995
  • SMTP:smtp.chinatelecom.cn 端口465
  • IMAP:imap.chinatelecom.cn 端口993

QQ邮箱(SSL加密)

  • POP3:pop.qq.cn 端口995
  • SMTP:smtp.qq.cn 端口465
  • IMAP:imap.qq.cn 端口993
    注意: QQ邮件等需要手动开通 SMTP服务

3、模块简介

python发邮件需要掌握两个模块的用法,smtplib和email。smtplib模块主要负责发送接收邮件,email模块主要负责构造邮件

smtplib模块

email模块

email模块下有mime包,mime英文全称为“Multipurpose Internet Mail Extensions”,该mime包下常用的有三个模块:text,image,multpart。
导入方法如下:

from email.mime.multipart import MIMEMultipart    
from email.mime.text import MIMEText    
from email.mime.image import MIMEImage

构造一个邮件对象就是一个Message对象,如果构造一个MIMEText对象,就表示一个文本邮件对象,如果构造一个MIMEImage对象,就表示一个作为附件的图片,要把多个对象组合起来,就用MIMEMultipart对象,而MIMEBase可以表示任何对象。它们的继承关系如下。

  • MIMEBase
    • MIMEMultipart
    • MIMENonMultipart
      • MIMEMessage
      • MIMEText 文本邮件对象
      • MIMEImag图片邮件对象

常见的multipart类型有三种:multipart/alternative, multipart/related和multipart/mixed。

邮件类型为"multipart/alternative"的邮件包括纯文本正文(text/plain)和超文本正文(text/html)。

邮件类型为"multipart/related"的邮件正文中包括图片,声音等内嵌资源。

邮件类型为"multipart/mixed"的邮件包含附件。向上兼容,如果一个邮件有纯文本正文,超文本正文,内嵌资源,附件,则选择mixed类型。

4、中文编码问题

from email.header import Header
subject = '中文标题'
subject=Header(subject, 'utf-8').encode()

二、代码展示

1、SMTP发送邮件

Python对SMTP支持有 smtplib 和 email 两个模块,email 负责构造邮件,smtplib 负责发送邮件。

构造邮件

文本正文:

from email.mime.text import MIMEText
msg = MIMEText('hello, send by Python...', 'plain', 'utf-8')

HTML正文:

from email.mime.text import MIMEText
msg = MIMEText('

test

', 'plain', 'utf-8')

发送邮件

import smtplib

from_addr = '189****[email protected]'
password = '*******'
to_addr = '[email protected]'
smtp_server = 'smtp.189.cn'
server = smtplib.SMTP_SSL(smtp_server, 465) # SMTP协议默认端口是465
server.set_debuglevel(1)    # 打印出和SMTP服务器交互的所有信息
server.login(from_addr, password)   # 登录SMTP服务器
server.sendmail(from_addr, [to_addr], msg.as_string())    # 发邮件
server.quit()

sendmail() 方法就是发邮件,由于可以一次发给多个人,所以传入一个 list,邮件正文是一个 str,as_string() 把MIMEText对象变成 str。

添加邮件标题、收/发件人

message = MIMEText(message_context, 'plain', 'utf-8')
# 设置发件人(声称的)
message["From"] = Header(sender_email_address, "utf-8")
# 设置收件人(声称的)
message["To"] = Header(receiver_email, "utf-8")
# 设置邮件主题
message["Subject"] = Header(message_subject, "utf-8")

添加抄送,暗送

# ccto_list 抄送
# bccto_list 暗送

msg['from'] = user
msg['to'] = ','.join(to_list)   #注意,不是分号
msg['cc'] = ','.join(ccto_list)
msg['subject'] = subject
server.sendmail(sender,to_list+ccto_list+bccto_list, str(msg))

附件

要想发送附件,需要构造一个 MIMEMultipart 对象代表邮件本身,然后往里面加上一个 MIMEText 作为邮件正文,再继续往里面加上表示附件的 MIMEBase 对象

sendfile=open(r'D:\pythontest\1111.txt','rb').read()
text_att = MIMEText(sendfile, 'base64', 'utf-8')    
text_att["Content-Type"] = 'application/octet-stream'    
text_att["Content-Disposition"] = 'attachment; filename="显示的名字.txt"' 

图片

sendimagefile=open(r'D:\pythontest\testimage.png','rb').read()
image = MIMEImage(sendimagefile)
image.add_header('Content-ID','')

完整代码

#coding: utf-8    
  
import smtplib    
from email.mime.multipart import MIMEMultipart    
from email.mime.text import MIMEText    
from email.mime.image import MIMEImage 
from email.header import Header   
    
#设置smtplib所需的参数
#下面的发件人,收件人是用于邮件传输的。
smtpserver = 'smtp.163.com'
username = '[email protected]'
password='XXX'
sender='[email protected]'
#receiver='[email protected]'
#收件人为多个收件人
receiver=['[email protected]','[email protected]']

subject = 'Python email test'
#通过Header对象编码的文本,包含utf-8编码信息和Base64编码信息。以下中文名测试ok
#subject = '中文标题'
#subject=Header(subject, 'utf-8').encode()
    
#构造邮件对象MIMEMultipart对象
#下面的主题,发件人,收件人,日期是显示在邮件页面上的。
msg = MIMEMultipart('mixed') 
msg['Subject'] = subject
msg['From'] = '[email protected] '
#msg['To'] = '[email protected]'
#收件人为多个收件人,通过join将列表转换为以;为间隔的字符串
msg['To'] = ";".join(receiver) 
#msg['Date']='2012-3-16'

#构造文字内容   
text = "Hi!\nHow are you?\nHere is the link you wanted:\nhttp://www.baidu.com"    
text_plain = MIMEText(text,'plain', 'utf-8')    
msg.attach(text_plain)    

#构造图片链接
sendimagefile=open(r'D:\pythontest\testimage.png','rb').read()
image = MIMEImage(sendimagefile)
image.add_header('Content-ID','')
image["Content-Disposition"] = 'attachment; filename="testimage.png"'
msg.attach(image)

#构造html
#发送正文中的图片:由于包含未被许可的信息,网易邮箱定义为垃圾邮件,报554 DT:SPM :

html = """

Hi!
How are you?
Here is the link you wanted.

"""
text_html = MIMEText(html,'html', 'utf-8') text_html["Content-Disposition"] = 'attachment; filename="texthtml.html"' msg.attach(text_html) #构造附件 sendfile=open(r'D:\pythontest\1111.txt','rb').read() text_att = MIMEText(sendfile, 'base64', 'utf-8') text_att["Content-Type"] = 'application/octet-stream' #以下附件可以重命名成aaa.txt #text_att["Content-Disposition"] = 'attachment; filename="aaa.txt"' #另一种实现方式 text_att.add_header('Content-Disposition', 'attachment', filename='aaa.txt') #以下中文测试不ok #text_att["Content-Disposition"] = u'attachment; filename="中文附件.txt"'.decode('utf-8') msg.attach(text_att) #发送邮件 smtp = smtplib.SMTP() smtp.connect('smtp.163.com') #我们用set_debuglevel(1)就可以打印出和SMTP服务器交互的所有信息。 #smtp.set_debuglevel(1) smtp.login(username, password) smtp.sendmail(sender, receiver, msg.as_string()) smtp.quit()

2、IAMP接收邮件

接收邮件

import datetime
import imaplib
import email


def recv_email_by_imap4(self):
    imap_server_host = 'imap.chinatelecom.cn'
    # imap_server_host = 'imap.189.cn'
    imap_server_port = 993
    try:
        imap = imaplib.IMAP4_SSL(host=imap_server_host, port=imap_server_port)
    except Exception as e:
        print(e)
    try:
        # 验证邮箱及密码是否正确
        imap.login(self._email_addr, self._email_password)
    except Exception as e:
        print(e)
    # 邮箱中其收到的邮件的数量
    imap.select()

    # 邮件状态设置,新邮件为Unseen
    # Messagestatues = 'All,Unseen,Seen,Recent,Answered, Flagged'
    resp, data = imap.search(None, "All")
    # resp, data = imap.search(None, "Unseen")
    index = data[0].decode().split()
    number = 1
    for i in data[0].split()[::-1]:
        resp, mail_data = imap.fetch(i, "(RFC822)")
        mail_text = mail_data[0][1].decode()
        msg = email.message_from_string(mail_text)
        if msg['Subject'] is not None:
            subject = email.header.decode_header(msg["Subject"])
        sub_fin = ''
        for item in subject:
            if item[1] is not None:
                sub_fin += item[0].decode(item[1])
            else:
                sub_fin += str(item[0])
        email_content, suffix = self.parse_email(msg)

        # 命令窗体输出邮件基本信息
        # 保存邮件正文

        if (suffix != None and suffix != '') and (email_content != None and email_content != ''):
            savefile(str(number) + suffix, email_content, self.mypath)
            number = number + 1

    # 关闭select
    imap.close()
    # 关闭连接
    imap.logout()

解析邮件

解析邮件每部分内容,包括发邮件介绍的,text、html、附件、图片等

def parse_email(self, msg):
    mail_content = None
    #正文类型:text/plain、text/html
    content_type = None
    suffix = None
    # 循环解析multipart
    for part in msg.walk():
        if not part.is_multipart():
            content_type = part.get_content_type()
            filename = part.get_filename()
            charset = part.get_charset()
            # 是否有附件
            if filename:
                dh = email.header.decode_header(filename)
                name = dh[0][0]
                encode_type = dh[0][1]
                if encode_type is not None:
                    if charset is None:
                        name = name.decode(encode_type, 'gbk')
                    else:
                        name = name.decode(encode_type, charset)
                data = part.get_payload(decode=True)
                self.save_file(name, data)
            else:
                if content_type in ['text/plain']:
                    suffix = '.txt'
                if content_type in ['text/html']:
                    suffix = '.htm'
                if charset is None:
                    mail_content = part.get_payload(decode=True)
                else:
                    mail_content = part.get_payload(decode=True).decode(charset)
    return mail_content, suffix

保存附件

def save_file(self, filename, data):
    try:
        file_path = self._path + filename
        f = open(file_path, 'wb+')
        f.write(data)
        f.close()
    except Exception as e:
        print('filename error')
        print(e)
        f.close()

你可能感兴趣的:(python)