How to send utf-8 e-mail?

PythonEmailUtf 8Smtp

Python Problem Overview


how to send utf8 e-mail please?

import sys
import smtplib
import email
import re

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

def sendmail(firm, fromEmail, to, template, subject, date):
    with open(template, encoding="utf-8") as template_file:
        message = template_file.read()

    message = re.sub(r"{{\s*firm\s*}}", firm, message)
    message = re.sub(r"{{\s*date\s*}}", date, message)
    message = re.sub(r"{{\s*from\s*}}", fromEmail, message)
    message = re.sub(r"{{\s*to\s*}}", to, message)
    message = re.sub(r"{{\s*subject\s*}}", subject, message)

    msg = MIMEMultipart("alternative")
    msg.set_charset("utf-8")
    
    msg["Subject"] = subject
    msg["From"] = fromEmail
    msg["To"] = to

    #Read from template
    html = message[message.find("html:") + len("html:"):message.find("text:")].strip()
    text = message[message.find("text:") + len("text:"):].strip()

    part1 = MIMEText(html, "html")
    part2 = MIMEText(text, "plain")
    
    msg.attach(part1)    
    msg.attach(part2)

    try:
        server = smtplib.SMTP("10.0.0.5")
        server.sendmail(fromEmail, [to], msg.as_string())
        return 0
    except Exception as ex:
        #log error
        #return -1
        #debug
        raise ex
    finally:
        server.quit()

if __name__ == "__main__":
    #debug
    sys.argv.append("Moje")
    sys.argv.append("[email protected]")
    sys.argv.append("[email protected]")
    sys.argv.append("may2011.template")
    sys.argv.append("This is subject")
    sys.argv.append("This is date")

    
    if len(sys.argv) != 7:
        exit(-2)

    firm = sys.argv[1]
    fromEmail = sys.argv[2]
    to = sys.argv[3]
    template = sys.argv[4]
    subject = sys.argv[5]
    date = sys.argv[6]
    
    exit(sendmail(firm, fromEmail, to, template, subject, date))

Output

Traceback (most recent call last):
  File "C:\Documents and Settings\Administrator\Plocha\Newsletter-build-desktop\sendmail.py", line 69, in <module>
    exit(sendmail(firm, fromEmail, to, template, subject, date))   
  File "C:\Documents and Settings\Administrator\Plocha\Newsletter-build-desktop\sendmail.py", line 45, in sendmail
    raise ex
  File "C:\Documents and Settings\Administrator\Plocha\Newsletter-build-desktop\sendmail.py", line 39, in sendmail
    server.sendmail(fromEmail, [to], msg.as_string())
  File "C:\Python32\lib\smtplib.py", line 716, in sendmail
    msg = _fix_eols(msg).encode('ascii')
UnicodeEncodeError: 'ascii' codec can't encode character '\u011b' in position 385: ordinal not in range(128)

Python Solutions


Solution 1 - Python

You should just add 'utf-8' argument to your MIMEText calls (it assumes 'us-ascii' by default).

For example:

# -*- encoding: utf-8 -*-

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

msg = MIMEMultipart("alternative")
msg["Subject"] = u'テストメール'
part1 = MIMEText(u'\u3053\u3093\u306b\u3061\u306f\u3001\u4e16\u754c\uff01\n',
                 "plain", "utf-8")
msg.attach(part1)

print msg.as_string().encode('ascii')

Solution 2 - Python

The question asked by Martin Drlík is 7 years and 8 months old... And nowadays, thanks to the developers of Python, encoding problems are solved with version 3 of Python.

Consequently, it is no longer necessary to specify that one must use the utf-8 encoding:

#!/usr/bin/python2
# -*- encoding: utf-8 -*-
...
    part2 = MIMEText(text, "plain", "utf-8")

We will simply write:

#!/usr/bin/python3
...
    part2 = MIMEText(text, "plain")

Ultimate consequence: Martin Drlík's script works perfectly well!

However, it would be better to use the email.parser module, as suggested in email: Examples.

Solution 3 - Python

For whom it might interest, I've written a Mailer library that uses SMTPlib, and deals with headers, ssl/tls security, attachments and bulk email sending.

Of course it also deals with UTF-8 mail encoding for subject and body.

You may find the code at: https://github.com/netinvent/ofunctions/blob/master/ofunctions/mailer/__init__.py

The relevant encoding part is

message["Subject"] = Header(subject, 'utf-8')
message.attach(MIMEText(body, "plain", 'utf-8'))

TL;DR: Install with pip install ofunctions.mailer

Usage:

from ofunctions.mailer import Mailer

mailer = Mailer(smtp_server='myserver', smtp_port=587)
mailer.send_email(sender_mail='[email protected]', recipient_mails=['[email protected]', '[email protected]'])

Encoding is already set as UTF-8, but you could change encoding to whatever you need by using mailer = Mailer(smtp_srever='...', encoding='latin1')

Solution 4 - Python

The previous answers here were adequate for Python 2 and earlier versions of Python 3. Starting with Python 3.6, new code should generally use the modern EmailMessage API rather than the old email.message.Message class or the related MIMEMultipart, MIMEText etc classes. The newer API was unofficially introduced already in Python 3.3, and so the old one should no longer be necessary unless you need portability back to Python 2 (or 3.2, which nobody in their right mind would want anyway).

With the new API, you no longer need to manually assemble an explicit MIME structure from parts, or explicitly select body-part encodings etc. Nor is Unicode a special case any longer; the email library will transparently select a suitable container type and encoding for regular text.

import sys
import re
import smtplib
from email.message import EmailMessage

def sendmail(firm, fromEmail, to, template, subject, date):
    with open(template, "r", encoding="utf-8") as template_file:
        message = template_file.read()

    message = re.sub(r"{{\s*firm\s*}}", firm, message)
    message = re.sub(r"{{\s*date\s*}}", date, message)
    message = re.sub(r"{{\s*from\s*}}", fromEmail, message)
    message = re.sub(r"{{\s*to\s*}}", to, message)
    message = re.sub(r"{{\s*subject\s*}}", subject, message)

    msg = EmailMessage()    
    msg["Subject"] = subject
    msg["From"] = fromEmail
    msg["To"] = to

    html = message[message.find("html:") + len("html:"):message.find("text:")].strip()
    text = message[message.find("text:") + len("text:"):].strip()

    msg.set_content(text)
    msg.add_alternative(html, subtype="html")

    try:
        server = smtplib.SMTP("10.0.0.5")
        server.send_message(msg)
        return 0
     # XXX FIXME: useless
    except Exception as ex:
        raise ex
    finally:
        server.quit()
        # Explicitly return error
        return 1

if __name__ == "__main__":
    if len(sys.argv) != 7:
        # Can't return negative
        exit(2)

    exit(sendmail(*sys.argv[1:]))

I'm not sure I completely understand the template handling here. Practitioners with even slightly different needs should probably instead review the Python email examples documentation which contains several simple examples of how to implement common email use cases.

The blanket except is clearly superfluous here, but I left it in as a placeholder in case you want to see what exception handling might look like if you had something useful to put there.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMartin Drl&#237;kView Question on Stackoverflow
Solution 1 - PythonabbotView Answer on Stackoverflow
Solution 2 - PythonMaxiReglisseView Answer on Stackoverflow
Solution 3 - PythonOrsiris de JongView Answer on Stackoverflow
Solution 4 - PythontripleeeView Answer on Stackoverflow