How to send utf-8 e-mail?

Question:

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*firms*}}", firm, message)
    message = re.sub(r"{{s*dates*}}", date, message)
    message = re.sub(r"{{s*froms*}}", fromEmail, message)
    message = re.sub(r"{{s*tos*}}", to, message)
    message = re.sub(r"{{s*subjects*}}", 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 SettingsAdministratorPlochaNewsletter-build-desktopsendmail.py", line 69, in <module>
    exit(sendmail(firm, fromEmail, to, template, subject, date))   
  File "C:Documents and SettingsAdministratorPlochaNewsletter-build-desktopsendmail.py", line 45, in sendmail
    raise ex
  File "C:Documents and SettingsAdministratorPlochaNewsletter-build-desktopsendmail.py", line 39, in sendmail
    server.sendmail(fromEmail, [to], msg.as_string())
  File "C:Python32libsmtplib.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)
Asked By: Martin Drlík

||

Answers:

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'u3053u3093u306bu3061u306fu3001u4e16u754cuff01n',
                 "plain", "utf-8")
msg.attach(part1)

print msg.as_string().encode('ascii')
Answered By: abbot

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.

Answered By: MaxiReglisse

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')

Answered By: Orsiris de Jong

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*firms*}}", firm, message)
    message = re.sub(r"{{s*dates*}}", date, message)
    message = re.sub(r"{{s*froms*}}", fromEmail, message)
    message = re.sub(r"{{s*tos*}}", to, message)
    message = re.sub(r"{{s*subjects*}}", 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.

Perhaps notice also that smtplib allows you to say with SMTP("10.9.8.76") as server: with a context manager.

Answered By: tripleee

I did it using the standard packages: ssl, smtplib and email.

import configparser
import smtplib
import ssl
from email.message import EmailMessage

# define bcc:[str], cc: [str], from_email: str, to_email: str, subject: str, html_body: str, str_body: str
... 

# save your login and server information in a settings.ini file
cfg.read(os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.ini"))

msg = EmailMessage()
msg['Bcc'] = ", ".join(bcc)
msg['Cc'] = ", ".join(cc)
msg['From'] = from_email
msg['To'] = to_email
msg['Subject'] = subject

msg.set_content(str_body)
msg.add_alternative(html_body, subtype="html")

# add SSL (layer of security)
context = ssl.create_default_context()

# log in and send the email
with smtplib.SMTP_SSL(cfg.get("mail", "server"), cfg.getint("mail", "port"), context=context) as smtp:
    smtp.login(cfg.get("mail", "username"), cfg.get("mail", "password"))
    smtp.send_message(msg=msg)
Answered By: Hans Daigle

In my company we use next code

import smtplib

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

...

msg = MIMEMultipart('alternative')
msg['Subject'] = subject
msg['From'] = from_addr
msg['To'] = to_addr
msg.attach(MIMEText(html, 'html', 'utf-8'))  
# or you can use 
# msg.attach(MIMEText(text, 'plain', 'utf-8')

server = smtplib.SMTP('localhost')
server.sendmail(msg['From'], [msg['To']], msg.as_string())
server.quit()
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.