How to send utf-8 e-mail?
Asked Answered
E

6

54

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)
Equipment answered 6/5, 2011 at 10:31 Comment(2)
We are missing line numbers so we can locate the exact line of the error.Mcgehee
The error is probably in the message.as_string(). You need to provide more, or we won't be able to help you.Tumescent
R
89

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')
Regan answered 6/5, 2011 at 11:9 Comment(7)
Sorry for being this old post and I write a new question underneath but I have to... Is supposed a human to write things like 'u\3035\u3093 ????? you have some japanese word right? how do you come up with these codes? from the tutorials i have read I always see this numbers ( while I understand are codes for letters , I don;t get how the input is done to this code or similar ones ...)Abscind
These codes are here only for illustration purposes. You can set your python source input encoding to utf-8 and type Unicode constants directly. If you need to get those '\u...' codes for some chars, you can just do something like print repr(u'テストメール')Regan
First thank you for the quick answer...Check this out ( to understand my issue with this question) I did this test : I put a utf-8 word (greek letters) in a variable and stored in a dictionary 'ΤΕΣΤ'=X. In the "Immediate window" when I do print listOfutfs reports me some u'' codes! ( from mysql I had ΤΕΣΤ retrieved ). Then I say ok give me the value back as listOfutfs['ΤΕΣΤ'], it replies to me KeyError: '\xce\xa6\xce\xa1\xce\x9b\xce\x9a'!!!!!!!!!!!!!!!!Abscind
ok, got it [u'ΤΕΣΤ']! terrible sorry, I didn't know that this whole thing of u'' is for explanatory reasons....How do you concatenate though, unicodes in python then? you can not write u""" ΤΕΣΤ """ also, right ?Abscind
You actually can. Follow the python Unicode howto: docs.python.org/howto/unicode.htmlRegan
Thank you abbot, I just realised how many months lost...cheersAbscind
I have tested this on python2.6.4 and it does not work as expected, the way to get past it is to also encode the unicode string as 'utf', it does however seem to work on python 2.7.2. In 2.6.4 you would do as an example, MIMEText( u'\3053'.encode('utf-8'), 'plain', 'utf-8')Marimaria
C
7

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.

Carborundum answered 26/1, 2019 at 14:34 Comment(0)
F
3

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.

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

Florella answered 17/4, 2022 at 10:42 Comment(1)
Manually doing re.sub smack dab in the middle of your email handling code is probably something you want to avoid anyway. If you need templating, probably look at an existing library such as Jinja2, or the facilities of the Python standard library; see also wiki.python.org/moin/TemplatingFlorella
J
1

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()
Jacobjacoba answered 24/12, 2022 at 21:21 Comment(0)
D
0

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

Denounce answered 17/4, 2022 at 9:17 Comment(2)
If this still uses the old MIMEText API, it should perhaps be avoided by anyone writing new code.Florella
Well my code is compatible from python 2.7 up to recent 3.10, and tested via continuous integration. So as far as I want to keep retrocompatibility, I'll stick to MIMEText which, except of some shortcommings which I already cover via my code, doesn't have any major flaw AFAIK. Anyway, thanks for your advice.Denounce
L
-1

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)
Ligature answered 26/11, 2022 at 16:57 Comment(2)
How is this different from this answer, apart from the use of SSL, which is irrelevant to the question?Glomeration
It's more concise and uses SSL for anyone that wants to send a UTF-8 message in 2022. The previous answer would not work with modern SMTP servers. Not sure why you downvote an alternative answer.Ligature

© 2022 - 2024 — McMap. All rights reserved.