最新消息:20210917 已从crifan.com换到crifan.org

【已解决】Python中如何把smtp的From和To进行Header编码

Python crifan 2080浏览 0评论

折腾:

【已解决】Python中smtp如何发送多个收件人地址且带名字的且可以被格式化

后,需要继续研究,如何把From和To(可能多个收件人)去编码。

参考:

Python email.Utils.formataddr Examples

18.1.5. email.header: Internationalized headers — Python 2.7.14 documentation

然后继续去研究Header格式化From和 To

根据:

https://docs.python.org/2/library/email.header.html

的解释是:

最早的话,用的协议是RFC 822,那时都是ASCII字符串

后来扩展到更多国家使用,支持多国语言后,但是协议却还是要求必须要用(7-bit的)ASCII去传输

-》而不允许其他语言的非ASCII字符

-〉所以后来就出现了协议的变通,支持把非ASCII字符,编码,转换为,ASCII字符

-》对应的协议就是:RFC 2822兼容性的一些协议,包括:RFC 2045、RFC 2046、RFC 2047、RFC 2231

-》对应的Python中的email的库,支持这些协议。

-〉其中的Header就是可以实现这个转换功能:把非ASCII编码转换为ASCII

-〉既然涉及到编码转换,则对应的就有个encode编码参数,最常见的当然是utf-8编码了。

搞懂基本逻辑后,就可以去写代码了。

此处也故意去试试,把之前的收件人的名字从英文:

“receivers”: [

  {

    “email”: “green-waste@163.com”,

    “username” : “green waste”

  },

  {

    “email”: “admin@crifan.org”,

    “username” : “Crifan Li”

  }

]

改为非ASCII的中文:

“receivers”: [

  {

    “email”: “green-waste@163.com”,

    “username” : “绿色垃圾”

  },

  {

    “email”: “admin@crifan.org”,

    “username” : “克瑞芬”

  }

看看发出来的邮件的标题是什么样的:

果然,收件人的字符串:

u’绿色垃圾 <green-waste@163.com>, 克瑞芬 <admin@crifan.org>’

变成乱码了:

所以非ASCII还是要去通过utf-8编码才对。

然后此处,对于多个收件人,直接用逗号拼接后的name和address的组合,去用Header去encode,结果是:

u’绿色垃圾 <green-waste@163.com>, 克瑞芬 <admin@crifan.org>’

编码为:

=?utf-8?b?57u/6Imy5Z6D5Zy+IDxncmVlbi13YXN0ZUAxNjMuY29tPiwg5YWL55Ge6Iqs?=

=?utf-8?q?_=3Cadmin=40crifan=2Ecom=3E?=

编码后的完整的内容是:

Content-Type: text/html; charset=”utf-8″

MIME-Version: 1.0

Content-Transfer-Encoding: base64

From: Crifan2003 <crifan2003@163.com>

To: =?utf-8?b?57u/6Imy5Z6D5Zy+IDxncmVlbi13YXN0ZUAxNjMuY29tPiwg5YWL55Ge6Iqs?=

=?utf-8?q?_=3Cadmin=40crifan=2Ecom=3E?=

Subject: =?utf-8?b?W+mrmOS7t10g5qCH6aKY5bim5Lit5paHIERlbGwgWFBTIDEzIFhQUzkzNjAt?=

=?utf-8?q?5797SLV-PUS_Laptop?=

CjxodG1sPgogICAgPGJvZHk+CiAgICAgICAgPGgxPlvpq5jku7ddIOagh+mimOW4puS4reaWhyBE

ZWxsIFhQUyAxMyBYUFM5MzYwLTU3OTdTTFYtUFVTIExhcHRvcDwvaDE+CiAgICAgICAgPHA+Tm90

IGJ1eSA8YSBocmVmPSJodHRwczovL3d3dy5taWNyb3NvZnQuY29tL2VuLXVzL3N0b3JlL2QvZGVs

bC14cHMtMTMteHBzLTkzNjAtbGFwdG9wLXBjLzhxMTczODRncnozNy9HVjVEP2FjdGl2ZXRhYj1w

aXZvdCUyNTNhb3ZlcnZpZXd0YWIiPuagh+mimOW4puS4reaWhyBEZWxsIFhQUyAxMyBYUFM5MzYw

LTU3OTdTTFYtUFVTIExhcHRvcDwvYT4gZm9yIGN1cnJlbnQgcHJpY2UgPGI+JDY5OS4wMDwvYj4g

Jmd0OyBleHBlY3RlZCBwcmljZSA8Yj4kNTk5LjAwPC9iPjwvcD4KICAgICAgICA8cD5TbyBzYXZl

IGZvciBsYXRlciBwcm9jZXNzPC9wPgogICAgPC9ib2R5Pgo8L2h0bWw+Cg==

看看能否发送成功,收件方能否正常解析

结果是不行的:

虽然标题title等中文可以正常解析。但是name+address用逗号分隔的,没法直接解码。

看来真的需要用之前的,拆分后,分别去Header中去格式化才可以。

参考之前别人的代码:

     name, addr = parseaddr(nameAndAddress)

     return formataddr(( \

         Header(name, ‘utf-8’).encode(), \

         addr.encode(‘utf-8’) if isinstance(addr, unicode) else addr))

去试试

然后是可以的:

不过后续的调试出现个问题:

    smtpObj.sendmail(sender, receiverList, msgStr)

  File “/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/smtplib.py”, line 751, in sendmail

    raise SMTPDataError(code, resp)

smtplib.SMTPDataError: (554, ‘DT:SPM 163 smtp8,DMCowADH57MSky5aS+kSDQ–.22544S2 1513001757,please see http://mail.163.com/help/help_spam_16.htm?ip=121.238.253.149&hostid=smtp8&time=1513001757‘)

去看:

企业退信的常见问题?-163邮箱常见问题

“554 DT:SPM 发送的邮件内容包含了未被许可的信息,或被系统识别为垃圾邮件。请检查是否有用户发送病毒或者垃圾邮件;”

-》估计是最近连续发了好多封类似邮件的内容,所以导致被误判为垃圾邮件了。

把邮件标题和内容换一下试试

结果问题依旧。

接着:

【已解决】Python中smtp用163账号发送邮件出错:550 User has no permission

但是却还是554 :

    raise SMTPDataError(code, resp)

smtplib.SMTPDataError: (554, ‘DT:SPM 163 smtp7,C8CowABXBOa5mS5a8VlHBg–.59457S2 1513003465,please see http://mail.163.com/help/help_spam_16.htm?ip=121.238.253.149&hostid=smtp7&time=1513003465‘)

再回头试试163的,看看单个163账号发送给其他单个账号时的from和to的Header编码是否正常。

【总结】

目前用代码,对于from和to都是单个发件人和收件人的话,是可以用Header正常编码的:

<code>
def formatEmailHeader(headerValue, encode="utf-8"):
    """
    format non-ASCII email header value to RFC 2822-compliant

    Example:
        u'绿色垃圾' -&gt; =?utf-8?b?57u/6Imy5Z6D5Zy+?=

        u'Crifan2003 &lt;crifan2003@163.com&gt;, 克瑞芬 &lt;admin@crifan.org&gt;'
        -&gt;
        =?utf-8?b?Q3JpZmFuMjAwMyA8Y3JpZmFuMjAwM0AxNjMuY29tPiwg5YWL55Ge6IqsIDxh?=
=?utf-8?q?dmin=40crifan=2Ecom=3E?=

    :param headerValue:
    :param encode:
    :return:
    """
    encodedHeaderValue = Header(headerValue, encode)
    return encodedHeaderValue

def formatEmailNameAddrHeader(nameAndAddress, encode="utf-8"):
    """

    Example:
        u'绿色垃圾 &lt;green-waste@163.com&gt;' -&gt; '=?utf-8?b?57u/6Imy5Z6D5Zy+?= &lt;green-waste@163.com&gt;'

    :param nameAndAddress:
    :param encode:
    :return:
    """
    (nameUnicode, addrUnicode) = parseaddr(nameAndAddress)
    nameStr = nameUnicode.encode(encode) # '绿色垃圾'
    addrStr = addrUnicode.encode(encode) # 'green-waste@163.com'
    formatedNameHeaderUnicode = formatEmailHeader(nameStr)
    formatedNameHeaderStr = formatedNameHeaderUnicode.encode(encode) # =?utf-8?b?57u/6Imy5Z6D5Zy+?=
    formatedNameAndAddress = formataddr((formatedNameHeaderStr, addrStr)) # '=?utf-8?b?57u/6Imy5Z6D5Zy+?= &lt;green-waste@163.com&gt;'
    return formatedNameAndAddress

def sendEmail(  sender, senderPassword, receiverList,
                senderName="", receiverNameList= "",
                smtpServer = "", smtpPort = None, useSSL=False,
                type = "plain", title = "", body = ""):
    """
    send email
    :param sender:
    :param senderPassword:
    :param receiverList:
    :param senderName:
    :param receiverNameList:
    :param smtpServer:
    :param smtpPort:
    :param type:
    :param title:
    :param body:
    :return:
    """

    logging.debug("sender=%s, senderName=%s, smtpServer=%s, smtpPort=%s, useSSL=%s, type=%s, title=%s, body=%s",
                  sender, senderName, smtpServer, smtpPort, useSSL, type, title, body)
    logging.debug("receiverList=%s, receiverNameList=%s", receiverList, receiverNameList)

    defaultPort = None
    SMTP_PORT_NO_SSL = 25
    SMTP_PORT_SSL = 465
    if useSSL:
        defaultPort = SMTP_PORT_SSL
    else:
        defaultPort = SMTP_PORT_NO_SSL


    if not smtpPort:
        smtpPort = defaultPort

    # init smtp server if necessary
    if not smtpServer:
        # extract domain from sender email
        # crifan2003@163.com -&gt; 163.com
        atIdx = sender.index('@')
        afterAtIdx = atIdx + 1
        lastDomain = sender[afterAtIdx:]
        smtpServer = 'smtp.' + lastDomain
        # smtpServer = "smtp.163.com"
        # smtpPort = 25

    # RECEIVER_SEPERATOR = '; '
    RECEIVER_SEPERATOR = ', '

    senderNameAddr = "%s &lt;%s&gt;" % (senderName, sender)
    receiversAddr = RECEIVER_SEPERATOR.join(receiverList)
    receiverNameAddrList = []
    formatedReceiverNameAddrList = []
    for curIdx, eachReceiver in enumerate(receiverList):
        eachReceiverName = receiverNameList[curIdx]
        eachNameAddr = "%s &lt;%s&gt;" % (eachReceiverName, eachReceiver)
        eachFormatedNameAddr = formatEmailNameAddrHeader(eachNameAddr)
        receiverNameAddrList.append(eachNameAddr)
        formatedReceiverNameAddrList.append(eachFormatedNameAddr)

    formatedReceiversNameAddr = RECEIVER_SEPERATOR.join(formatedReceiverNameAddrList) # '=?utf-8?b?57u/6Imy5Z6D5Zy+?= &lt;green-waste@163.com&gt;, =?utf-8?b?5YWL55Ge6Iqs?= &lt;admin@crifan.org&gt;'
    mergedReceiversNameAddr = RECEIVER_SEPERATOR.join(receiverNameAddrList) # u'Crifan2003 &lt;crifan2003@163.com&gt;, 克瑞芬 &lt;admin@crifan.org&gt;'
    # formatedReceiversNameAddr = formatEmailHeader(mergedReceiversNameAddr) #=?utf-8?b?Q3JpZmFuMjAwMyA8Y3JpZmFuMjAwM0AxNjMuY29tPiwg5YWL55Ge6IqsIDxh?=
    # =?utf-8?q?dmin=40crifan=2Ecom=3E?=

    # def _format_addr(nameAndAddress):
    #     """
    #     format email address
    #     :param nameAndAddress: email name and address
    #     :return:
    #     """
    #     name, addr = parseaddr(nameAndAddress)
    #     return formataddr(( \
    #         Header(name, 'utf-8').encode(), \
    #         addr.encode('utf-8') if isinstance(addr, unicode) else addr))

    msg = MIMEText(body, _subtype=type, _charset="utf-8")
    # msg["From"] = _format_addr(senderNameAddr)
    # msg["To"] = _format_addr(receiversNameAddr)
    msg["From"] = formatEmailHeader(senderNameAddr)
    # msg["From"] = senderNameAddr
    # msg["To"] = formatEmailHeader(formatedReceiversNameAddr)
    # msg["To"] = formatedReceiversNameAddr
    # msg["To"] = mergedReceiversNameAddr
    # msg["To"] = formatEmailHeader(receiversAddr)
    msg["To"] = formatEmailHeader(mergedReceiversNameAddr)
    # titleHeader = Header(title, "utf-8")
    # encodedTitleHeader = titleHeader.encode()
    # msg['Subject'] = encodedTitleHeader
    msg['Subject'] = formatEmailHeader(title)
    # msg['Subject'] = title
    msgStr = msg.as_string()

    # try:
    # smtpObj = smtplib.SMTP('localhost')
    smtpObj = None
    if useSSL:
        smtpObj = smtplib.SMTP_SSL(smtpServer, smtpPort)
    else:
        smtpObj = smtplib.SMTP(smtpServer, smtpPort)
    smtpObj.set_debuglevel(1)
    smtpObj.login(sender, senderPassword)
    # smtpObj.sendmail(sender, receiversAddr, msgStr)
    smtpObj.sendmail(sender, receiverList, msgStr)
    logging.info("Successfully sent email: message=%s", msgStr)
    # except smtplib.SMTPException:
    #     logging.error("Fail to sent email: message=%s", message)

    return
</code>

转载请注明:在路上 » 【已解决】Python中如何把smtp的From和To进行Header编码

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
92 queries in 0.194 seconds, using 23.36MB memory