python smtplib库发送邮件过程分析

2020-11-17  本文已影响0人  SyKay

python进行邮件发送,主要使用到了以下两个库
smtplib库: 用于发送邮件,它对smtp协议进行了简单的封装。
email库: 用于构造邮件。

下面是来自阮一峰老师的代码案例

from email.header import Header
from email.mime.text import MIMEText
from email.utils import parseaddr, formataddr
import smtplib

def _format_addr(s):
    # 格式化一个邮件地址,注意不能简单地传入name <addr@example.com>
    # 因为如果包含中文,需要通过Header对象进行编码。
    name, addr = parseaddr(s)
    return formataddr((Header(name, 'utf-8').encode(), addr))

# 配置 邮件发件人、收件人信息
from_addr = "**@**.com"
password = "******"
to_addr = "**@**.com"  # 输入收件人地址,接收的是字符串而不是list,如果有多个邮件地址,用,分隔即可。
smtp_server = "mail.**.com"  # 设置邮箱服务器 mail.**.com

# 构造一封简单的文本邮件
msg = MIMEText('Hello, send by Python..', 'plain', 'utf-8')  # plain表示纯文本
msg['From'] = _format_addr("Python爱好者<%s>" % from_addr)
msg['To'] = _format_addr("管理员<%s>" % to_addr)
msg['Subject'] = Header('来着SMTP的问候', 'utf-8').encode()

# 进行邮箱登录、邮件发送
smtpObj = smtplib.SMTP(smtp_server, 25)
smtpObj.set_debuglevel(1)
smtpObj.starttls()
smtpObj.login(from_addr, password)
smtpObj.sendmail(from_addr, [to_addr], msg.as_string())
smtpObj.quit()

其中,用于发送邮件代码模块主要如下:

import smtplib
smtpObj = smtplib.SMTP(smtp_server, 25)
smtpObj.set_debuglevel(debuglevel=1)
smtpObj.starttls()
smtpObj.login(from_addr, password)
smtpObj.sendmail(from_addr, [to_addr], msg.as_string())
smtpObj.quit()

下面对各实现的代码进行简要分析

1、 smtpObj = smtplib.SMTP(smtp_server, 25)
本质是与邮箱服务器创建一个socket链接,后续会使用明文通讯,端口默认25;
此外,也可以使用smtplib.SMTP_SSL(smtp_server,465),后续通讯则会采用SSL加密方式,端口默认465
如下:连接上服务器后,响应码220,表示连接成功

retcode (220); Msg: b'Ex201602.zjft.com Microsoft ESMTP MAIL Service ready at Wed, 18 Nov 2020 10:29:53 +0800'

2、 smtpObj.set_debuglevel(debuglevel=1)
设置debug等级,用于调试(不设置不影响发邮件),设置了之后客户端和邮箱服务器通讯时,会输出通讯内容
debuglevel参数大于1,控制台输出通讯时间和通讯内容,否则仅输出通讯内容

3、 smtpObj.starttls()
告诉邮箱服务器使用 TLS安全协议 进行通信加密(TLS协议 前身是 SSL协议,简单理解 TLS协议 为 SSL协议 的升级版即可)。其中,部分服务器通讯强制需要TLS通讯加密

1、 有些邮箱服务器强制必须使用TLS协议,没使用starttls()的话,后面在登录邮箱时会报如下异常
>> No suitable authentication method found.

2、 有些邮箱服务器不支持使用TLS协议,使用了starttls()的话,会报错,如下
>> STARTTLS extension not supported by server.

3、有些邮箱服务器不强制使用TLS通讯加密,即使不使用starttls()都可以(我公司邮箱服务器就是这样子)。
- 综上所述,邮箱登录前需不需要使用 `starttls()`,这得实际测试后才知道

以下是执行smtpObj.starttls(),控制台输出的日志,可分为两部分

send: 'ehlo [192.168.42.1]\r\n'
reply: b'250-Ex201602.zjft.com Hello [10.1.1.85]\r\n'
reply: b'250-SIZE 52428800\r\n'
reply: b'250-PIPELINING\r\n'
reply: b'250-DSN\r\n'
reply: b'250-ENHANCEDSTATUSCODES\r\n'
reply: b'250-STARTTLS\r\n'
reply: b'250-X-ANONYMOUSTLS\r\n'
reply: b'250-AUTH NTLM LOGIN\r\n'
reply: b'250-X-EXPS GSSAPI NTLM\r\n'
reply: b'250-8BITMIME\r\n'
reply: b'250-BINARYMIME\r\n'
reply: b'250-CHUNKING\r\n'
reply: b'250 XRDST\r\n'
reply: retcode (250); Msg: b'Ex201602.zjft.com Hello [10.1.1.85]\nSIZE 52428800\nPIPELINING\nDSN\nENHANCEDSTATUSCODES\nSTARTTLS\nX-ANONYMOUSTLS\nAUTH NTLM LOGIN\nX-EXPS GSSAPI NTLM\n8BITMIME\nBINARYMIME\nCHUNKING\nXRDST'

send: 'STARTTLS\r\n'
reply: b'220 2.0.0 SMTP server ready\r\n'
reply: retcode (220); Msg: b'2.0.0 SMTP server ready'

4、 smtpObj.login(from_addr, password)
进行smtp认证,即我们常说的邮箱登录

send: 'ehlo [192.168.42.1]\r\n'
reply: b'250-Ex201602.zjft.com Hello [10.1.1.85]\r\n'
reply: b'250-SIZE 52428800\r\n'
reply: b'250-PIPELINING\r\n'
reply: b'250-DSN\r\n'
reply: b'250-ENHANCEDSTATUSCODES\r\n'
reply: b'250-STARTTLS\r\n'
reply: b'250-X-ANONYMOUSTLS\r\n'
reply: b'250-AUTH NTLM LOGIN\r\n'
reply: b'250-X-EXPS GSSAPI NTLM\r\n'
reply: b'250-8BITMIME\r\n'
reply: b'250-BINARYMIME\r\n'
reply: b'250-CHUNKING\r\n'
reply: b'250 XRDST\r\n'
reply: retcode (250); Msg: b'Ex201602.zjft.com Hello [10.1.1.85]\nSIZE 52428800\nPIPELINING\nDSN\nENHANCEDSTATUSCODES\nSTARTTLS\nX-ANONYMOUSTLS\nAUTH NTLM LOGIN\nX-EXPS GSSAPI NTLM\n8BITMIME\nBINARYMIME\nCHUNKING\nXRDST'

send: 'AUTH LOGIN c2tzdW5AempmdC5jb20=\r\n'
reply: b'334 UGFzc3dvcmQ6\r\n'
reply: retcode (334); Msg: b'UGFzc3dvcmQ6'

send: 'U1VOa2FpMTIyNQ==\r\n'
reply: b'235 2.7.0 Authentication successful\r\n'
reply: retcode (235); Msg: b'2.7.0 Authentication successful'

5、 smtpObj.sendmail(from_addr, [to_addr], msg.as_string())
发送邮件,传入参数

send: 'mail FROM:<12345@**.com> size=133\r\n'
reply: b'250 2.1.0 Sender OK\r\n'
reply: retcode (250); Msg: b'2.1.0 Sender OK'
send: 'rcpt TO:<123456@qq.com>\r\n'
reply: b'250 2.1.5 Recipient OK\r\n'
reply: retcode (250); Msg: b'2.1.5 Recipient OK'
send: 'data\r\n'
reply: b'354 Start mail input; end with <CRLF>.<CRLF>\r\n'
reply: retcode (354); Msg: b'Start mail input; end with <CRLF>.<CRLF>'
data: (354, b'Start mail input; end with <CRLF>.<CRLF>')
send: b'Content-Type: text/plain; charset="utf-8"\r\nMIME-Version: 1.0\r\nContent-Transfer-Encoding: base64\r\n\r\nSGVsbG8sIHNlbmQgYnkgUHl0aG9uLi4=\r\n.\r\n'
reply: b'250 2.6.0 <bcbef1ba-1733-442c-a69e-ea3a5feb48b3@ex201601.zjft.com> [InternalId=38594576122924, Hostname=Ex201601.zjft.com] 1343 bytes in 0.141, 9.276 KB/sec Queued mail for delivery\r\n'
reply: retcode (250); Msg: b'2.6.0 <bcbef1ba-1733-442c-a69e-ea3a5feb48b3@ex201601.zjft.com> [InternalId=38594576122924, Hostname=Ex201601.zjft.com] 1343 bytes in 0.141, 9.276 KB/sec Queued mail for delivery'
data: (250, b'2.6.0 <bcbef1ba-1733-442c-a69e-ea3a5feb48b3@ex201601.zjft.com> [InternalId=38594576122924, Hostname=Ex201601.zjft.com] 1343 bytes in 0.141, 9.276 KB/sec Queued mail for delivery')

从日志可以看出,客户端先给服务器发送发件人账户信息,命令是mail FROM:

6、 smtpObj.quit()
退出与服务器的连接,关闭连接通道。

send: 'quit\r\n'
reply: b'221 2.0.0 Service closing transmission channel\r\n'
reply: retcode (221); Msg: b'2.0.0 Service closing transmission channel'
上一篇下一篇

猜你喜欢

热点阅读