美文网首页
python电子邮件系列(二)之SMTP发送邮件

python电子邮件系列(二)之SMTP发送邮件

作者: SimonJoe246 | 来源:发表于2018-12-07 21:58 被阅读0次

    邮件发送与接收的流程

    SMTP是发送邮件的协议,是MUA(Mail User Agent)发送到MTA(Mail Transfer Agent)和MTA之间传输的协议。

    编写程序来发送和接收邮件,本质上就是以下这样的过程:

    发件人->MUA->MTA->若干MTA->MDA->收件人

    其中,你是新浪的用户,就会先发到新浪的MTA上,经过若干MTA,会到达收件人的邮件服务商的MTA上,这个MTA会把邮件投递到最终目的地MDA(Mail Delivery Agent)邮件投递代理,Email到达MDA后,会静静地躺在邮件服务商的服务器上,等待用户开机联网通过MUA从MDA把邮件取到本地。

    几种邮件类型

    纯文本邮件/HTML文本邮件

    发送纯文本邮件:

    先构造邮件消息(用到email库):

    构造一个最简单的纯文本邮件:

    from email.mime.text import MIMEText
    msg = MIMEText('send by simon...', 'plain', 'utf-8')
    

    构造MIMEText对象时,第一个参数就是邮件正文,第二个是子格式,plain表示传入的是纯文本,另一个值是html

    charset值为utf-8,保证多语言兼容性。此值也将被添加到Content-Type头中去。

    此外,若charset值设定,则Content-Transfer-Encoding会被设定为对应值,如utf-8对应base64。关于Content-Transfer-Encoding内容在上一篇电子邮件系列(一)中已经详细介绍过。

    当我们print这个msg会发现其信件内容(不含header)已经是base64格式了。所以,当指定了charset后,会指定Content-Type-Encoding,信件内容相应的由字符编码为bytes,其本质是二进制,之后再将二进制转化为base64

    接下来,发送邮件(用到smtplib库):

    import smtplib
    
    
    from_addr = '1005121394@qq.com'
    to_addr = 'simonkindle@126.com'
    password = 'your password'
    smtp_server = 'smtp.qq.com'
    server_port = 465
    
    server = smtplib.SMTP_SSL(smtp_server, server_port)
    server.set_debuglevel(1)  #打印出所有和SMTP服务器交互的信息
    server.login(from_addr, password)
    server.sendmail(from_addr, [to_addr], msg.as_string())
    server.quit()
    

    对于QQ邮箱来说,其要求SMTP发送邮件时必须使用SSL加密,所以,使用SSL加密端口465,正常情况下不要求SSL加密的使用25端口,同时创建smtp对象时使用的是smtp.SMTP函数。

    sendmail的第二个参数to_addrs接收的是一个list,可以传入多个收件人地址。第三个是邮件内容,as_astring函数会将格式化的message对象(包含MIMEHeader以及内容)全部转化为str,这样才能在网络上传输(当然,网络上传输的是这些str的字节流bytes格式,这一过程未在代码中体现,个人猜测应该在sendmail中完成。)

    发送HTML格式邮件:

    只需要在构建message时将传入的文本格式改为html,并在传入html格式的文本即可。例如:

    msg = MIMEText('<html><body><h1>Hello</h1>' +
        '<p>send by <a href="http://www.python.org">Python</a>...</p>' +
        '</body></html>', 'html', 'utf-8')
    

    以上邮件我们还没有为其添加邮件头,也就是主题,发件人,收件人,这会造成一些问题,比如会提示不在收件人中,或收件人无

    尤其是,当你用网易邮箱发邮件时,会提示错误,原因就是你的sendmail函数中的发收件人与邮件头中的不匹配。

    添加邮件头:

    def _format_addr(s):
        name, addr = parseaddr(s)
        return formataddr((Header(name, 'utf-8').encode(), addr))
    
    from_addr = input('From: ')
    password = input('Password: ')
    to_addr = input('To: ')
    smtp_server = input('SMTP server: ')
    
    msg = MIMEText('hello, send by Python...', 'plain', 'utf-8')
    msg['From'] = _format_addr('Python爱好者 <%s>' % from_addr)
    msg['To'] = _format_addr('管理员 <%s>' % to_addr)
    msg['Subject'] = Header('来自SMTP的问候……', 'utf-8').encode()
    

    我们定义了一个_format_addr格式化邮件地址,不能直接传入name <addr@example.com>,因为name如果包含中文,需要对name进行编码。

    但是,subject经测试是可以直接传入中文字符的。

    同时支持HTML和plain格式的文本邮件

    如果我们发送HTML邮件,收件人通过浏览器或者Outlook之类的软件是可以正常浏览邮件内容的,但是,如果收件人使用的设备太古老,查看不了HTML邮件怎么办?

    可以在发送HTML文本的同时再附加一个纯文本,如果收件人的设备不支持查看HTML格式邮件,可以显示纯文本(两个只能显示一个)

    我们可以创建一个MIMEMultipart类型对象,subtype子类型是'alternative',在上一篇文章中我们已经讲过,这代表纯文本和HTML文本混合的内容。
    创建之后再向其中添加两个MIMTText对象即可。

    代码如下:

    msg = MIMEMultipart('alternative')
    msg['From'] = ...
    msg['To'] = ...
    msg['Subject'] = ...
    
    msg.attach(MIMEText('hello', 'plain', 'utf-8'))
    msg.attach(MIMEText('<html><body><h1>Hello</h1></body></html>', 'html', 'utf-8'))
    

    发送附件

    如何发送带附件比如像图片或mp3这种二进制文件的邮件呢?

    同样,我们可以先构造一个MIMEMultipart对象,向其中添加MIMEText对象作为邮件正文,再向其中添加MIMEBase作为附件即可。

    msg = MIMEMultipart()
    msg.attach(MIMEText('你好', 'plain', 'utf-8'))
    
    # 添加附件就是添加一个MIMEBase对象,注意文件要以二进制形式打开。
    with open('F://my pictures//beauty//222.JPG', 'rb') as f:
        mime = MIMEBase('image', 'jpeg', filename='222.JPG')
        # 添加文件头,此filename会显示在邮件附件中
        mime.add_header('Content-Disposition', 'attachment', filename='222.JPG')
        # 定义图片ID,以便在文本中引用
        mime.add_header('Content-ID', '<0>')
        # 读入附件内容
        mime.set_payload(f.read())
        # Encode the message's payload in Base64. 并添加Content-Transfer-Encoding头信息
        encoders.encode_base64(mime)
        msg.attach(mime)
    

    将图片嵌入邮件正文中

    如何把图片附件嵌入邮件正文中(邮件服务商一般不允许使用HTML图片外链,防止有害信息):

    要把图片嵌入到邮件正文中,我们只需按照发送附件的方式,先把邮件作为附件添加进去,然后,在HTML中通过引用src="cid:0"就可以把附件作为图片嵌入了。如果有多个图片,给它们依次编号,然后引用不同的cid:x即可。

    msg.attach(MIMEText('<html><body><h1>Hello</h1>' +
        '<p><img src="cid:0"></p>' +
        '</body></html>', 'html', 'utf-8'))
    

    再次发送,即可在正文中看到图片了。

    加密邮件

    使用标准的25端口连接SMTP服务器时,使用的是明文传输,发送邮件的整个过程可能会被窃听。要更安全地发送邮件,可以加密SMTP会话,实际上就是先创建SSL安全连接,然后再使用SMTP协议发送邮件

    比如腾讯qq邮箱要求SMTP服务必须要SSL加密。端口号是465

    我们上文所用的smtp.SMTP_SSL(server_addr, server_port)就是加密方式

    非加密方式使用的函数是smtp.SMTP(server_addr, 25)

    总结

    对于非ASCII编码的纯文本,由输入的字符到网络传输这一过程中的编码变化为:

    str->bytes->base64->str->bytes

    其中,前三步由MIMEText函数完成,base64->stras_string函数完成。最后一步不在代码中体现。

    对于二进制文件来说:

    binary code->base64->str->bytes

    其中,binary codeopen函数以rb模式读入,二进制转到base64encoders.encode_base64函数完成,之后与纯文本相同,由as_string完成message格式到str的转化。

    相关文章

      网友评论

          本文标题:python电子邮件系列(二)之SMTP发送邮件

          本文链接:https://www.haomeiwen.com/subject/fraxhqtx.html