Email via Python
1 MIME - Multipurpose Internet Mail Extensions 2 SMTP - Simple Message Transport Protocol 3 4 邮件的发送, 5 例子 - send email 6 import smtplib 7 from email.mime.text import MIMEText 8 from email.utils import formatdate, make_msgid 9 mess = 'hello there' 10 mesgobj = MIMEText(mess) 11 mesgobj['To'] = 'testrecevier@example.com' 12 mesgobj['From'] = 'testsender@example.com' 13 mesgobj['Subject'] = 'Greeting' 14 mesgobj['Date'] = formatdate(localtime=1) # Date header, formatdate() 生成 email 专门日期格式 15 mesgobj['Message-ID'] = make_msgid() # make_msgid() 方法生成唯一的 Message-ID, 16 print(mesgobj.as_string()) 17 print(mesgobj.as_bytes()) 18 19 s = smtplib.SMTP('SMTP-SERVER-ADDR') 20 s.send_message(mesgobj) 21 s.quit() 22 23 邮件的解析, 24 例子 - parsing email, 25 from email.parser import Parser 26 headers = Parser().parsestr(mesgobj.as_string()) # mesgobj 为上例中对象. 27 for h,v in headers.items(): 28 print("Header[%s] - " % h,v) 29 print('Emial content - ', mesgobj.get_payload()) # 打印邮件正文. 30 31 带有附件的邮件发送处理 - Packing MIME, 32 邮件的附件有 音频, 图像, 压缩文件等等, 通过 python doc 的上的例子看一下儿 33 对邮件的附件的处理方式, 34 例子 - send email with attachments, 35 * an example of how to send the entire contents of a directory as an email message 36 37 import os 38 import sys 39 import smtplib 40 # For guessing MIME type based on file name extension 41 import mimetypes 42 from optparse import OptionParser 43 from email import encoders 44 from email.message import Message 45 from email.mime.audio import MIMEAudio 46 from email.mime.base import MIMEBase 47 from email.mime.image import MIMEImage 48 from email.mime.multipart import MIMEMultipart 49 from email.mime.text import MIMEText 50 51 COMMASPACE = ', ' 52 53 def main(): 54 parser = OptionParser(usage='''\ 55 Send the contents of a directory as a MIME message. 56 57 Usage: %prog [options] 58 59 Unless the -o option is given, the email is sent by forwarding to your local 60 SMTP server, which then does the normal delivery process. Your local machine 61 must be running an SMTP server. 62 ''') 63 parser.add_option('-d', '--directory', 64 type='string', action='store', 65 help='''Mail the contents of the specified directory, 66 otherwise use the current directory. Only the regular 67 files in the directory are sent, and we don't recurse to 68 subdirectories.''') 69 parser.add_option('-o', '--output', 70 type='string', action='store', metavar='FILE', 71 help='''Print the composed message to FILE instead of 72 sending the message to the SMTP server.''') 73 parser.add_option('-s', '--sender', 74 type='string', action='store', metavar='SENDER', 75 help='The value of the From: header (required)') 76 parser.add_option('-r', '--recipient', 77 type='string', action='append', metavar='RECIPIENT', 78 default=[], dest='recipients', 79 help='A To: header value (at least one required)') 80 opts, args = parser.parse_args() 81 if not opts.sender or not opts.recipients: 82 parser.print_help() # SMTP server 只处理地址有效的 sender 跟 recipients 83 sys.exit(1) 84 directory = opts.directory 85 if not directory: 86 directory = '.' # 之前的代码的功能是通过 OptionParser 没款完成对命令行参数的解析 87 # Create the enclosing (outer) message 88 outer = MIMEMultipart() # 构建一个带附件的 email 对象 89 outer['Subject'] = 'Contents of directory %s' % os.path.abspath(directory) 90 outer['To'] = COMMASPACE.join(opts.recipients) 91 outer['From'] = opts.sender 92 outer.preamble = 'You will not see this in a MIME-aware mail reader.\n' 93 94 for filename in os.listdir(directory): # 按附件类型做相应的处理 95 path = os.path.join(directory, filename) 96 if not os.path.isfile(path): 97 continue 98 # Guess the content type based on the file's extension. Encoding 99 # will be ignored, although we should check for simple things like 100 # gzip'd or compressed files. 101 ctype, encoding = mimetypes.guess_type(path) # 附件类型的判断 102 if ctype is None or encoding is not None: 103 # No guess could be made, or the file is encoded (compressed), so 104 # use a generic bag-of-bits type. 105 ctype = 'application/octet-stream' # zip, pdf 等附件的类型 106 maintype, subtype = ctype.split('/', 1) 107 if maintype == 'text': # 处理的 文本text 类型的附件 108 fp = open(path) 109 # Note: we should handle calculating the charset 110 msg = MIMEText(fp.read(), _subtype=subtype) 111 fp.close() 112 elif maintype == 'image': # 处理的 图像image 类型的附件 113 fp = open(path, 'rb') 114 msg = MIMEImage(fp.read(), _subtype=subtype) 115 fp.close() 116 elif maintype == 'audio': # 处理的 音频audio 类型的附件 117 fp = open(path, 'rb') 118 msg = MIMEAudio(fp.read(), _subtype=subtype) 119 fp.close() 120 else: 121 fp = open(path, 'rb') # 处理的其他类型的附件:如 zip, pdf 等 122 msg = MIMEBase(maintype, subtype) 123 msg.set_payload(fp.read()) 124 fp.close() 125 # Encode the payload using Base64 126 encoders.encode_base64(msg) 127 # Set the filename parameter 128 msg.add_header('Content-Disposition', 'attachment', filename=filename) 129 outer.attach(msg) 130 # Now send or store the message 131 composed = outer.as_string() 132 if opts.output: # 保存到文件 store 133 fp = open(opts.output, 'w') 134 fp.write(composed) 135 fp.close() 136 else: 137 s = smtplib.SMTP('localhost') # SMTP server 发送处理 138 s.sendmail(opts.sender, opts.recipients, composed) 139 s.quit() 140 141 if __name__ == '__main__': 142 main() 143 144 MIME 的解包 - Unpacking MIME 145 146 接收方对收到的 MIMEMultipart 的处理, 147 '''Unpack a MIME message into a directory of files.''' 148 149 import os 150 import sys 151 import email 152 import errno 153 import mimetypes 154 from optparse import OptionParser 155 def main(): 156 parser = OptionParser(usage='''\ 157 Unpack a MIME message into a directory of files. 158 159 Usage: %prog [options] msgfile 160 ''') 161 parser.add_option('-d', '--directory', 162 type='string', action='store', 163 help='''Unpack the MIME message into the named 164 directory, which will be created if it doesn't already 165 exist.''') 166 opts, args = parser.parse_args() 167 if not opts.directory: 168 parser.print_help() 169 sys.exit(1) 170 171 try: 172 msgfile = args[0] 173 except IndexError: 174 parser.print_help() 175 sys.exit(1) 176 177 try: 178 os.mkdir(opts.directory) 179 except OSError as e: 180 # Ignore directory exists error 181 if e.errno != errno.EEXIST: 182 raise 183 184 fp = open(msgfile) 185 msg = email.message_from_file(fp) 186 fp.close() 187 188 counter = 1 189 for part in msg.walk(): 190 # multipart/* are just containers 191 if part.get_content_maintype() == 'multipart': 192 continue 193 # Applications should really sanitize the given filename so that an 194 # email message can't be used to overwrite important files 195 filename = part.get_filename() 196 if not filename: 197 ext = mimetypes.guess_extension(part.get_content_type()) 198 if not ext: 199 # Use a generic bag-of-bits extension 200 ext = '.bin' 201 filename = 'part-%03d%s' % (counter, ext) 202 counter += 1 203 fp = open(os.path.join(opts.directory, filename), 'wb') 204 fp.write(part.get_payload(decode=True)) 205 fp.close() 206 207 if __name__ == '__main__': 208 main() 209 210 Reference, 211 https://docs.python.org/3/library/email.html