email:範例

Here are a few examples of how to use theemail package to read, write,and send simple email messages, as well as more complex MIME messages.

First, let's see how to create and send a simple text message (both thetext content and the addresses may contain unicode characters):

# Import smtplib for the actual sending functionimportsmtplib# Import the email modules we'll needfromemail.messageimportEmailMessage# Open the plain text file whose name is in textfile for reading.withopen(textfile)asfp:# Create a text/plain messagemsg=EmailMessage()msg.set_content(fp.read())# me == the sender's email address# you == the recipient's email addressmsg['Subject']=f'The contents of{textfile}'msg['From']=memsg['To']=you# Send the message via our own SMTP server.s=smtplib.SMTP('localhost')s.send_message(msg)s.quit()

ParsingRFC 822 headers can easily be done by the using the classesfrom theparser module:

# Import the email modules we'll need#from email.parser import BytesParserfromemail.parserimportParserfromemail.policyimportdefault# If the e-mail headers are in a file, uncomment these two lines:# with open(messagefile, 'rb') as fp:#     headers = BytesParser(policy=default).parse(fp)#  Or for parsing headers in a string (this is an uncommon operation), use:headers=Parser(policy=default).parsestr('From: Foo Bar <user@example.com>\n''To: <someone_else@example.com>\n''Subject: Test message\n''\n''Body would go here\n')#  Now the header items can be accessed as a dictionary:print('To:{}'.format(headers['to']))print('From:{}'.format(headers['from']))print('Subject:{}'.format(headers['subject']))# You can also access the parts of the addresses:print('Recipient username:{}'.format(headers['to'].addresses[0].username))print('Sender name:{}'.format(headers['from'].addresses[0].display_name))

Here's an example of how to send a MIME message containing a bunch of familypictures that may be residing in a directory:

# Import smtplib for the actual sending function.importsmtplib# Here are the email package modules we'll need.fromemail.messageimportEmailMessage# Create the container email message.msg=EmailMessage()msg['Subject']='Our family reunion'# me == the sender's email address# family = the list of all recipients' email addressesmsg['From']=memsg['To']=', '.join(family)msg.preamble='You will not see this in a MIME-aware mail reader.\n'# Open the files in binary mode.  You can also omit the subtype# if you want MIMEImage to guess it.forfileinpngfiles:withopen(file,'rb')asfp:img_data=fp.read()msg.add_attachment(img_data,maintype='image',subtype='png')# Send the email via our own SMTP server.withsmtplib.SMTP('localhost')ass:s.send_message(msg)

Here's an example of how to send the entire contents of a directory as an emailmessage:[1]

#!/usr/bin/env python3"""Send the contents of a directory as a MIME message."""importosimportsmtplib# For guessing MIME type based on file name extensionimportmimetypesfromargparseimportArgumentParserfromemail.messageimportEmailMessagefromemail.policyimportSMTPdefmain():parser=ArgumentParser(description="""\Send the contents of a directory as a MIME message.Unless the -o option is given, the email is sent by forwarding to your localSMTP server, which then does the normal delivery process.  Your local machinemust be running an SMTP server.""")parser.add_argument('-d','--directory',help="""Mail the contents of the specified directory,                        otherwise use the current directory.  Only the regular                        files in the directory are sent, and we don't recurse to                        subdirectories.""")parser.add_argument('-o','--output',metavar='FILE',help="""Print the composed message to FILE instead of                        sending the message to the SMTP server.""")parser.add_argument('-s','--sender',required=True,help='The value of the From: header (required)')parser.add_argument('-r','--recipient',required=True,action='append',metavar='RECIPIENT',default=[],dest='recipients',help='A To: header value (at least one required)')args=parser.parse_args()directory=args.directoryifnotdirectory:directory='.'# Create the messagemsg=EmailMessage()msg['Subject']=f'Contents of directory{os.path.abspath(directory)}'msg['To']=', '.join(args.recipients)msg['From']=args.sendermsg.preamble='You will not see this in a MIME-aware mail reader.\n'forfilenameinos.listdir(directory):path=os.path.join(directory,filename)ifnotos.path.isfile(path):continue# Guess the content type based on the file's extension.  Encoding# will be ignored, although we should check for simple things like# gzip'd or compressed files.ctype,encoding=mimetypes.guess_file_type(path)ifctypeisNoneorencodingisnotNone:# No guess could be made, or the file is encoded (compressed), so# use a generic bag-of-bits type.ctype='application/octet-stream'maintype,subtype=ctype.split('/',1)withopen(path,'rb')asfp:msg.add_attachment(fp.read(),maintype=maintype,subtype=subtype,filename=filename)# Now send or store the messageifargs.output:withopen(args.output,'wb')asfp:fp.write(msg.as_bytes(policy=SMTP))else:withsmtplib.SMTP('localhost')ass:s.send_message(msg)if__name__=='__main__':main()

Here's an example of how to unpack a MIME message like the oneabove, into a directory of files:

#!/usr/bin/env python3"""Unpack a MIME message into a directory of files."""importosimportemailimportmimetypesfromemail.policyimportdefaultfromargparseimportArgumentParserdefmain():parser=ArgumentParser(description="""\Unpack a MIME message into a directory of files.""")parser.add_argument('-d','--directory',required=True,help="""Unpack the MIME message into the named                        directory, which will be created if it doesn't already                        exist.""")parser.add_argument('msgfile')args=parser.parse_args()withopen(args.msgfile,'rb')asfp:msg=email.message_from_binary_file(fp,policy=default)try:os.mkdir(args.directory)exceptFileExistsError:passcounter=1forpartinmsg.walk():# multipart/* are just containersifpart.get_content_maintype()=='multipart':continue# Applications should really sanitize the given filename so that an# email message can't be used to overwrite important filesfilename=part.get_filename()ifnotfilename:ext=mimetypes.guess_extension(part.get_content_type())ifnotext:# Use a generic bag-of-bits extensionext='.bin'filename=f'part-{counter:03d}{ext}'counter+=1withopen(os.path.join(args.directory,filename),'wb')asfp:fp.write(part.get_payload(decode=True))if__name__=='__main__':main()

Here's an example of how to create an HTML message with an alternative plaintext version. To make things a bit more interesting, we include a relatedimage in the html part, and we save a copy of what we are going to send todisk, as well as sending it.

#!/usr/bin/env python3importsmtplibfromemail.messageimportEmailMessagefromemail.headerregistryimportAddressfromemail.utilsimportmake_msgid# Create the base text message.msg=EmailMessage()msg['Subject']="Pourquoi pas des asperges pour ce midi ?"msg['From']=Address("Pepé Le Pew","pepe","example.com")msg['To']=(Address("Penelope Pussycat","penelope","example.com"),Address("Fabrette Pussycat","fabrette","example.com"))msg.set_content("""\Salut!Cette recette [1] sera sûrement un très bon repas.[1] http://www.yummly.com/recipe/Roasted-Asparagus-Epicurious-203718--Pepé""")# Add the html version.  This converts the message into a multipart/alternative# container, with the original text message as the first part and the new html# message as the second part.asparagus_cid=make_msgid()msg.add_alternative("""\<html>  <head></head>  <body>    <p>Salut!</p>    <p>Cette        <a href="http://www.yummly.com/recipe/Roasted-Asparagus-Epicurious-203718">            recette        </a> sera sûrement un très bon repas.    </p>    <img src="cid:{asparagus_cid}">  </body></html>""".format(asparagus_cid=asparagus_cid[1:-1]),subtype='html')# note that we needed to peel the <> off the msgid for use in the html.# Now add the related image to the html part.withopen("roasted-asparagus.jpg",'rb')asimg:msg.get_payload()[1].add_related(img.read(),'image','jpeg',cid=asparagus_cid)# Make a local copy of what we are going to send.withopen('outgoing.msg','wb')asf:f.write(bytes(msg))# Send the message via local SMTP server.withsmtplib.SMTP('localhost')ass:s.send_message(msg)

If we were sent the message from the last example, here is one way we couldprocess it:

importosimportsysimporttempfileimportmimetypesimportwebbrowser# Import the email modules we'll needfromemailimportpolicyfromemail.parserimportBytesParserdefmagic_html_parser(html_text,partfiles):"""Return safety-sanitized html linked to partfiles.    Rewrite the href="cid:...." attributes to point to the filenames in partfiles.    Though not trivial, this should be possible using html.parser.    """raiseNotImplementedError("Add the magic needed")# In a real program you'd get the filename from the arguments.withopen('outgoing.msg','rb')asfp:msg=BytesParser(policy=policy.default).parse(fp)# Now the header items can be accessed as a dictionary, and any non-ASCII will# be converted to unicode:print('To:',msg['to'])print('From:',msg['from'])print('Subject:',msg['subject'])# If we want to print a preview of the message content, we can extract whatever# the least formatted payload is and print the first three lines.  Of course,# if the message has no plain text part printing the first three lines of html# is probably useless, but this is just a conceptual example.simplest=msg.get_body(preferencelist=('plain','html'))print()print(''.join(simplest.get_content().splitlines(keepends=True)[:3]))ans=input("View full message?")ifans.lower()[0]=='n':sys.exit()# We can extract the richest alternative in order to display it:richest=msg.get_body()partfiles={}ifrichest['content-type'].maintype=='text':ifrichest['content-type'].subtype=='plain':forlineinrichest.get_content().splitlines():print(line)sys.exit()elifrichest['content-type'].subtype=='html':body=richestelse:print("Don't know how to display{}".format(richest.get_content_type()))sys.exit()elifrichest['content-type'].content_type=='multipart/related':body=richest.get_body(preferencelist=('html'))forpartinrichest.iter_attachments():fn=part.get_filename()iffn:extension=os.path.splitext(part.get_filename())[1]else:extension=mimetypes.guess_extension(part.get_content_type())withtempfile.NamedTemporaryFile(suffix=extension,delete=False)asf:f.write(part.get_content())# again strip the <> to go from email form of cid to html form.partfiles[part['content-id'][1:-1]]=f.nameelse:print("Don't know how to display{}".format(richest.get_content_type()))sys.exit()withtempfile.NamedTemporaryFile(mode='w',delete=False)asf:f.write(magic_html_parser(body.get_content(),partfiles))webbrowser.open(f.name)os.remove(f.name)forfninpartfiles.values():os.remove(fn)# Of course, there are lots of email messages that could break this simple# minded program, but it will handle the most common ones.

Up to the prompt, the output from the above is:

To: Penelope Pussycat <penelope@example.com>, Fabrette Pussycat <fabrette@example.com>From: Pepé Le Pew <pepe@example.com>Subject: Pourquoi pas des asperges pour ce midi ?Salut!Cette recette [1] sera sûrement un très bon repas.

註解

[1]

Thanks to Matthew Dixon Cowles for the original inspiration and examples.