Since I posted about using Python to send HTML email, things have changed in Python’s standard library. While that method still works, it’s better to use the new email package.
For the purposes of this post, I am assuming the following:
- Python 2.x (I have Python 2.6 installed on my development server)
- Linux
- Lynx is installed
import os def html2text( html ): fname = os.tmpnam() fp = open(fname, 'w') fp.write(html) fp.close() fp = os.popen('cat %s | lynx -dump -force_html -stdin' % fname) buff = fp.read() fp.close() return buff
The html2text function takes a given HTML document (either local or a you can use an external URL) and converts it to a plain text document. It uses os.popen to do this, by piping the document through Lynx and using that program as a filter to do the conversion.
import email from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText def createhtmlmail (html, text, subject): msg = MIMEMultipart('alternative') part1 = MIMEText(text, 'plain') part2 = MIMEText(html, 'html') msg.attach(part1) msg.attach(part2) return msg
createhtmlmail does the heavy lifting of making the MIME attachments and adding them to the email message. You may notice that I kept the function signature the same as that used in my earlier post onsending HTML email from Python; this way, I was able to use these new functions as a drop-in replacement to what I had in my production environment.
import email import os from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText def compose_mail(html, subject, to_mail, from_mail): text = html2text(html) email_data = createhtmlmail(html, text, subject) email_data['Subject'] = subject email_data['From'] = reply_mail email_data['To'] = to_mail email_data["Date"] = email.Utils.formatdate(localtime=True) return email_data
Here will build the actual email message, and set the Subject, From, and To headers. While Subject is technically optional, some MTA’s and MUA’s complain about mails with Subject headers; I’ve found it easier to always set it.
import smtplib def send_mail(from_mail, to_mail, email_data): server = smtplib.SMTP("localhost") server.sendmail(from_mail, to_mail, email_data) server.quit()
I have a local sendmail running on my Linux machines, so I can use localhost as my SMTP server; you may need to change it according to your actual environment.
With those functions in place, we can now send a MIME multipart email, with the following:
email_data = compose_mail(body, subject, to_mail, from_mail) send_mail(from_mail, to_mail, email_data.as_string())
The only part of this example code that relies on a non-typical piece of software is the html2text function. I use Lynx as the filtering process, but you can replace that with something more appropriate for your platform and environment.
You can download the full file here.
You should also always add the date header. It is required accoring to RFC 822 and messages without it might have an increased spam rating. I suggest the following solution for doing so:
msg[“Date”] = email.Utils.formatdate(localtime=True)
I was relying on sendmail to add that header, but it’s still a good point to add it ourselves. A different MTA may not behave the same way.
I’ve updated the body of the post accordingly, and also updated the attached .py file.
Have you tested mailer?
https://bitbucket.org/ginstrom/mailer
Whats your opinion of it?
It looks like a good option, but seems more heavy-weight than what I was looking to do.