Welcome, guest | Sign In | My Account | Store | Cart

After wrestling and wrestling with Outlook 2000 to turn off HTML mail, I had to figure out how to send HTML from Python for a recent project. Oh the irony, the irony.

It's good policy (and trivially easy) to embed two versions of your message if you're planning on sending HTML mail: the HTML version, and a text-only version. Lots of folks still prefer their character-mode MUAs (viva Mutt!) so don't go alienating them by sending them something that they can't read.

The e-mails generated by this code have been tested on and render correctly with Outlook 2000, Eudora 4.2, Hotmail, and Netscape Mail. It's likely that they will work in other HTML-capable MUAs as well.

Python, 67 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
def createhtmlmail (html, text, subject):
      	"""Create a mime-message that will render HTML in popular
	   MUAs, text in better ones"""
	import MimeWriter
	import mimetools
	import cStringIO
	
	out = cStringIO.StringIO() # output buffer for our message 
	htmlin = cStringIO.StringIO(html)
 	txtin = cStringIO.StringIO(text)
	
	writer = MimeWriter.MimeWriter(out)
	#
	# set up some basic headers... we put subject here
	# because smtplib.sendmail expects it to be in the
	# message body
	#
	writer.addheader("Subject", subject)
	writer.addheader("MIME-Version", "1.0")
	#
	# start the multipart section of the message
	# multipart/alternative seems to work better
	# on some MUAs than multipart/mixed
	#
	writer.startmultipartbody("alternative")
	writer.flushheaders()
	#
	# the plain text section
	#
	subpart = writer.nextpart()
	subpart.addheader("Content-Transfer-Encoding", "quoted-printable")
	pout = subpart.startbody("text/plain", [("charset", 'us-ascii')])
	mimetools.encode(txtin, pout, 'quoted-printable')
	txtin.close()
	#
	# start the html subpart of the message
	#
	subpart = writer.nextpart()
	subpart.addheader("Content-Transfer-Encoding", "quoted-printable")
	#
	# returns us a file-ish object we can write to
	#
	pout = subpart.startbody("text/html", [("charset", 'us-ascii')])
	mimetools.encode(htmlin, pout, 'quoted-printable')
	htmlin.close()
	#
	# Now that we're done, close our writer and
	# return the message body
	#
	writer.lastpart()
	msg = out.getvalue()
	out.close()
	print msg
	return msg

if __name__=="__main__":
	import smtplib
	f = open("newsletter.html", 'r')
	html = f.read()
	f.close()
	f = open("newsletter.txt", 'r')
	text = f.read()
	subject = "Today's Newsletter!"
	message = createhtmlmail(html, text, subject)
	server = smtplib.SMTP("localhost")
	server.sendmail('agillesp@i-noSPAMSUCKS.com', 'agillesp@i-noSPAMSUCKS.com', message)
	server.quit()

Although the karmic outcome of foisting HTML mail upon the masses is questionable, it seems HTML mail is here to stay. As long as you're conscientious about sending along a plain text version for users who prefer text e-mail, you probably won't get flamed too badly.

The following page http://www.arsdigita.com/asj/mime/ helped me out quite a bit when I was tracking down this problem. Henry Minsky does a good job of sorting out the myriad related RFCs and the fact that there really is no standard for HTML mail.

I hope this is useful. Let me know at agillesp@i-noSPAMSUCKS.com

3 comments

Cliff Wells 18 years, 5 months ago  # | flag

Needs to send To and From headers. This script is nice, except that it doesn't set the To and From headers which will make some MUA's (Thunderbird for example) show "undisclosed recipients" in the To field. The fix is simple, just pass the sender and recipient to the createhtmlmail() function and add the following two lines to that function:

writer.addheader("From", sender)
writer.addheader("To", recipient)
Richard Him Lok 18 years, 4 months ago  # | flag

How do you add images to HTML Messages? The script is great, but I also need to embed images with the IMG tag into the body of my HTML messages. How do I ensure that they come with them message and are displayed in the correct IMG placeholders? I do not want to rely on an image linked to a file sitting on a server, but carried with the message?

s g 17 years, 6 months ago  # | flag

HTML with images, too. See this recipie: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/473810