美文网首页
python3之发送和读取邮件

python3之发送和读取邮件

作者: rr1990 | 来源:发表于2019-08-09 16:14 被阅读0次

    一、发送邮件

    • 发送邮件使用SMTP协议【Simple Mail Transfer Protocol简单的邮件传输协议】,SMTP协议是SMTP客户端与SMTP服务器之间的通信协议。
    • python中发送邮件使用的模块有smtplib和email:
      使用smtplib模块进行发送邮件;
      使用email模块来添加发送的邮件内容。

    1. smtplib模块

    导入模块:import smtplib

    1.1. 创建SMTP对象

    smtplib.SMTPsmtplib.SMTP_SSL:均可以用来创建SMTP对象;
    smtplib.SMTP_SSL:使用安全加密的SSL协议连接到SMTP服务器;
    smtplib.SMTP:没有进行安全加密。
    故若待测试邮箱不允许使用非SSL和非TLS频道通信时,则无法使用smtp.SMTP方式来创建客户端对象。
    【查看邮箱的通信方式:邮箱设置菜单中,查看邮箱的接收服务器和发送服务器信息。】

    如:腾讯企业邮箱
    接收服务器:
    imap.exmail.qq.com(使用SSL,端口号993)
    发送服务器:
    smtp.exmail.qq.com(使用SSL,端口号465)
    
    • smtplib.SMTP(host, port, local_hostname, timeout, source_address)
    • smtplib.SMTP_SSL(host, port, local_hostname, keyfile, certfile, timeout, source_address, context)
      创建SMTP对象。
      host:SMTP发送服务器主机
      port:SMTP服务器端哭口号
    1.2. SMTP对象操作
    • login(user, password, *, initial_response_ok=True)
      SMTP对象登录
      user:授权登录的用户名
      password:授权登录的密码
    • sendmail(from_addr, to_addrs, msg, mail_options=[], rcpt_options=[])
      SMTP对象发送邮件
      from_addr:发件人地址,字符串类型。
      to_addr:收件人地址,包括收件人和抄送人。
      多个收件人时to_addr参数为列表,单个收件人时to_addr参数可以为列表或字符串。
      msg:要发送的信息
    • quite()
      终止SMTP会话

    2. 发送邮件的实例

    2.1. 添加邮件内容,包括收件人、抄送人、正文、附件
    from email.mime.multipart import MIMEMultipart
    from email.header import Header
    from email.mime.text import MIMEText
    from email.mime.image import MIMEImage
    import os
    class EmailContent:
       
        def __init__(self, senderAdr, emailSubject, toReceivers, ccReceivers):
            # 邮件对象
            self.msg = MIMEMultipart()
            # 添加发件人头
            self.msg['From'] = Header("测试" + "<" + senderAdr + ">", 'utf-8')
            # 添加收件人
            if isinstance(toReceivers, str):
                self.msg["To"] = toReceivers
            elif isinstance(toReceivers, list):
                self.msg['To'] = ";".join(toReceivers)
            # 添加抄送人
            if isinstance(ccReceivers, str):
                self.msg["Cc"] = ccReceivers
            elif isinstance(ccReceivers, list):
                self.msg["Cc"] = ";".join(ccReceivers)
            # 添加邮件主题
                self.msg['Subject'] = Header(emailSubject, "utf-8")
    
        def addBody(self, bodyType):
            """
            添加不同的邮件正文的实例
            1. body为字符串:(如)"这是一个邮件正文内容"
            2. body为html格式的字符串:(如)"<div><p>第一段</p><p>&nbsp;第二段</p></div>"
            3. body正文中包含有图片:
            """
            if bodyType == "string":
                body = "这是一个邮件正文内容"
                mimeText = MIMEText(body, "plain", "utf-8")
                self.msg.attach(mimeText)
            elif bodyType == "html":
                body = "<div><p>第一段</p><p>&nbsp;第二段</p></div>"
                mimeText = MIMEText(body, "html", "utf-8")
                self.msg.attach(mimeText)
            elif "image" in bodyType:
                imageFile = "E://log//test.png"
                imageId = os.path.split(imageFile)[1]
                # 添加内容
                body = '''
                        <p>测试图片为:</p>
                        <p><img src="cid:{imageId}"></p>
                        '''.format(imageId=imageId)
                mimeText = MIMEText(body, "html", "utf-8")
                self.msg.attach(mimeText)
                # 读取图片,并设置图片id用于邮件正文引用
                with open(imageFile, "rb") as fp:
                    mimeImage = MIMEImage(fp.read())
                mimeImage.add_header("Content-ID", imageId)
                self.msg.attach(mimeImage)
    
            def addAttachment(self, attachmentName):
            """
            添加附件
            :return:
            """
            file = "E://log//test.txt"
            # file = "E://log//test.zip"
            # file = "E://log//test.png"
            filePath, fileName = os.path.split(file)
            print("fileName =", fileName)
            enclosure = MIMEText(open(file, 'rb').read(), 'base64', 'utf-8')
            enclosure['Content-Type'] = 'application/octet-stream'
            if attachmentName == "英文":
                enclosure['Content-Disposition'] = 'attachment; filename="%s"' % fileName
            elif attachmentName == "中文":
                enclosure.add_header("Content-Disposition", "attachment", filename=("gbk", "", fileName))
            self.msg.attach(enclosure)
    
    
    2.2. 发送邮件
    import smtplib
    def SendEmail():
        """发送邮件"""
        # SMTP的服务器信息
        smtpHost = "smtp.exmail.qq.com"
        sslPort = 465
        senderAdr = "xx@xx.cn"
        senderPwd = "XXXX"
        # 创建SMTP对象
        smtpServer = smtplib.SMTP_SSL(smtpHost, sslPort)
        # # 设置debug模块
        # smtpServer.set_debuglevel(True)
        # 登录
        smtpServer.login(senderAdr, senderPwd)
        # 添加邮件内容
        toReceivers = ["a@xx.cn", "b@xx.cn"]
        ccReceivers = ["d@xx.cn", "e@xx.cn"]
        toAddrs = toReceivers + ccReceivers
        emailSubject = "这是个自动发送的邮件"
        emailContent = EmailContent(senderAdr, emailSubject, toReceivers, ccReceivers)
        emailContent.addBody("html")
        emailContent.addAttachment("英文")
        message = emailContent.msg
        # 发送
        smtpServer.sendmail(senderAdr, toAddrs, message.as_string())
        # 终止SMTP会话
        smtpServer.quit()
    
    SendEmail()
    

    二、读取邮件

    • 收取邮件使用POP3协议;
    • 解析邮件:需要将收取的邮件转化为email.message.Message对象,再使用email模块解析内容。

    1. 读取邮件的实例

    1.1. 获取某封邮件的对象
    import poplib
    from email.parser import Parser
    """POP的服务器信息"""
    popHost = "pop.exmail.qq.com"
    userAdr = "xx@xx.cn"
    userPwd = "xxxxx"
    
    """ 创建POP3对象,添加用户名和密码"""
    pop3Server = poplib.POP3(popHost)
    pop3Server.user(userAdr)
    pop3Server.pass_(userPwd)
    
    """获取邮件数量和占用空间"""
    messageCount, mailboxSize = pop3Server.stat()
    
    """获取邮件请求返回状态码、每封邮件的字节大小(b'第几封邮件 此邮件字节大小')、"""
    response, msgNumOctets, octets = pop3Server.list()
    
    """ 获取任意一封邮件的邮件对象【第一封邮件的编号为1,而不是0】"""
    msgIndex = random.randint(1,messageCount)
    print(msgIndex)
    # 获取第msgIndex封邮件的信息
    response, msgLines, octets = pop3Server.retr(msgIndex)
    # msgLines中为该邮件的每行数据,先将内容连接成字符串,再转化为email.message.Message对象
    msgLinesToStr = b"\r\n".join(msgLines).decode("utf8", "ignore")
    messageObject = Parser().parsestr(msgLinesToStr)
    print(messageObject)
    
    """ 终止POP3服务"""
    pop3Server.quit()
    
    1.2. 解析邮件对象
    1.2.1. 获取邮件日期
    msgDate = messageObject["date"]
    print(msgDate)
    
    1.2.2. 获取邮件发件人实名、邮箱地址

    获取邮件实名时,名称一般是加密的,此时就需要对头文件进行解码才可获取它的实际内容

    from email.header import decode_header
    def decodeMsgHeader(header):
        """
        解码头文件
        :param header: 需解码的内容
        :return:
        """
        value, charset = decode_header(header)[0]
        if charset:
            value = value.decode(charset)
        return value
    
    from email.utils import parseaddr
    senderContent = messageObject["From"]
    # parseaddr()函数返回的是一个元组(realname, emailAddress)
    senderRealName, senderAdr = parseaddr(senderContent) 
    # 将加密的名称进行解码 
    senderRealName = decodeMsgHeader(senderRealName)   
    print(senderRealName)
    print(senderAdr)
    
    1.2.3. 获取邮件主题

    获取的邮件的主题也是加密的,此时就需要对头文件进行解码才可获取它的实际内容

    msgHeader = messageObject["Subject"]
    # 对头文件进行解码
    msgHeader = decodeMsgHeader(msgHeader )
    print(msgHeader)
    
    1.2.4. 获取邮件正文

    一封邮件的正文内容,可能是由几部分构成,每部分的格式不同。

    """获取邮件正文内容"""
    msgBodyContents = []
    if messageObject.is_multipart():  # 判断邮件是否由多个部分构成
        messageParts = messageObject.get_payload()  # 获取邮件附载部分
        for messagePart in messageParts:
            bodyContent = decodeBody(messagePart)
            if bodyContent:
                msgBodyContents.append(bodyContent)
    else:
        bodyContent = decodeBody(messageObject)
        if bodyContent:
            messageBodyContents.append(bodyContent)
    print(msgBodyContents)
    
    def decodeBody(msgPart):
        """
        解码内容
       :param msgPart: 邮件某部分
        """
        contentType = msgPart.get_content_type()  # 判断邮件内容的类型,text/html
        textContent = ""
        if contentType == 'text/plain' or contentType == 'text/html':
            content = msgPart.get_payload(decode=True)
            charset = msgPart.get_charset()
            if charset is None:
                contentType = msgPart.get('Content-Type', '').lower()
                position = contentType.find('charset=')
                if position >= 0:
                    charset = contentType[position + 8:].strip()
            if charset:
                textContent = content.decode(charset)
        return textContent
    
    1.2.5. 获取邮件附件

    邮件附件名为中文时,需借助头文件解码方式进行解码,否则会为乱码。

    messageAttachments = []
    if messageObject.is_multipart():  #   判断邮件是否由多个部分构成
        messageParts = messageObject.get_payload()  # 获取邮件附载部分
        for messagePart in messageParts:
            name = messagePart.get_param("name")  # 名字存在,则表示此部分为附件
            if name:
                fileName = decodeMsgHeader(name)   # 解码
                messageAttachments.append(fileName)
    else:
        name = messageObject.get_param("name")
        if name:
           fileName = decodeMsgHeader(name)   # 解码
           messageAttachments.append(fileName)
    print(messageAttachments)
    

    2. 读取邮件时遇到的问题

    2.1. 提示“poplib.error_proto: line too long”

    File "XXX/EmailInfo.py", line 22, in getMessageObject
        return parser.Parser().parsestr(b"\n".join(self.popServer.retr(i)[1]).decode("utf8", "ignore"))
      File "/usr/local/lib/python3.6/poplib.py", line 248, in retr
        return self._longcmd('RETR %s' % which)
      File "/usr/local/lib/python3.6/poplib.py", line 183, in _longcmd
        return self._getlongresp()
      File "/usr/local/lib/python3.6/poplib.py", line 168, in _getlongresp
        line, o = self._getline()
      File "/usr/local/lib/python3.6/poplib.py", line 130, in _getline
        raise error_proto('line too long')
    poplib.error_proto: line too long
    

    POP3对行长度做了限制,默认为_MAXLINE = 2048,故若是邮件超过此长度就会提示“poplib.error_proto: line too long”。
    解决方案:在读取邮件代码中重新定义最大行长度,即给poplib._MAXLINE设置新值。

    import poplib
    poplib._MAXLINE=20480
    

    相关文章

      网友评论

          本文标题:python3之发送和读取邮件

          本文链接:https://www.haomeiwen.com/subject/jxwnaqtx.html