Python使用SMTP发送电子邮件时出现SSL错误

5

我想使用Outlook发送电子邮件。代码如下:

import smtplib
from email.message import EmailMessage

msg = EmailMessage()
msg['From'] = '*******'
msg['Subject'] = 'Some subject here'
msg['To'] = '********'
        
msg.set_content('Some text here')

with smtplib.SMTP_SSL('smtp-mail.outlook.com', 587) as smtp:
    smtp.login('******', '****')
    smtp.send_message(msg)
    print('Email sent!')

我遇到了如下错误:

---------------------------------------------------------------------------
SSLError                                  Traceback (most recent call last)
<ipython-input-8-4d5956f55c88> in <module>
      6 msg.set_content('Some text here')
      7 
----> 8 with smtplib.SMTP_SSL('smtp-mail.outlook.com', 587) as smtp:
      9     smtp.login('sender_email', 'password')
     10     smtp.send_message(msg)

~/anaconda/envs/quant2/lib/python3.6/smtplib.py in __init__(self, host, port, local_hostname, keyfile, certfile, timeout, source_address, context)
   1029             self.context = context
   1030             SMTP.__init__(self, host, port, local_hostname, timeout,
-> 1031                     source_address)
   1032 
   1033         def _get_socket(self, host, port, timeout):

~/anaconda/envs/quant2/lib/python3.6/smtplib.py in __init__(self, host, port, local_hostname, timeout, source_address)
    249 
    250         if host:
--> 251             (code, msg) = self.connect(host, port)
    252             if code != 220:
    253                 self.close()

~/anaconda/envs/quant2/lib/python3.6/smtplib.py in connect(self, host, port, source_address)
    334         if self.debuglevel > 0:
    335             self._print_debug('connect:', (host, port))
--> 336         self.sock = self._get_socket(host, port, self.timeout)
    337         self.file = None
    338         (code, msg) = self.getreply()

~/anaconda/envs/quant2/lib/python3.6/smtplib.py in _get_socket(self, host, port, timeout)
   1037                     self.source_address)
   1038             new_socket = self.context.wrap_socket(new_socket,
-> 1039                                                   server_hostname=self._host)
   1040             return new_socket
   1041 

~/anaconda/envs/quant2/lib/python3.6/ssl.py in wrap_socket(self, sock, server_side, do_handshake_on_connect, suppress_ragged_eofs, server_hostname, session)
    405                          suppress_ragged_eofs=suppress_ragged_eofs,
    406                          server_hostname=server_hostname,
--> 407                          _context=self, _session=session)
    408 
    409     def wrap_bio(self, incoming, outgoing, server_side=False,

~/anaconda/envs/quant2/lib/python3.6/ssl.py in __init__(self, sock, keyfile, certfile, server_side, cert_reqs, ssl_version, ca_certs, do_handshake_on_connect, family, type, proto, fileno, suppress_ragged_eofs, npn_protocols, ciphers, server_hostname, _context, _session)
    815                         # non-blocking
    816                         raise ValueError("do_handshake_on_connect should not be specified for non-blocking sockets")
--> 817                     self.do_handshake()
    818 
    819             except (OSError, ValueError):

~/anaconda/envs/quant2/lib/python3.6/ssl.py in do_handshake(self, block)
   1075             if timeout == 0.0 and block:
   1076                 self.settimeout(None)
-> 1077             self._sslobj.do_handshake()
   1078         finally:
   1079             self.settimeout(timeout)

~/anaconda/envs/quant2/lib/python3.6/ssl.py in do_handshake(self)
    687     def do_handshake(self):
    688         """Start the SSL/TLS handshake."""
--> 689         self._sslobj.do_handshake()
    690         if self.context.check_hostname:
    691             if not self.server_hostname:

SSLError: [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:852)

我更新了我的回答。顺便问一下,你是使用免费的Outlook账户还是企业账户? - Life is complex
3个回答

3

在发送电子邮件时,Microsoft Outlook使用STARTTLS。因此,您需要将smtplib.SMTP_SSL替换为smtplib.SMTP,并且需要调用starttls

参考:smtplib.SMTP.starttls

import smtplib
from email.message import EmailMessage

sender = 'somename@outlook.com'
recipient = 'somename@gmail.com'

msg = EmailMessage()
msg.set_content('this is a test')
msg['From'] = 'somename@outlook.com'
msg['To'] = 'somename@gmail.com'
msg['Subject'] = 'test email'

with smtplib.SMTP('smtp.office365.com', 587) as server:
    server.ehlo()
    server.starttls()
    server.ehlo()
    server.ehlo()
    server.login('your_login', "your_password", initial_response_ok=True) 
    server.ehlo()
    server.sendmail(sender, recipient, msg.as_string())
    print('Email sent!')
    server.close()

这是我在Gmail账户中的Outlook邮件。

enter image description here

enter image description here

我注意到我必须更改我的Outlook密码,因为它包含了一个\n,而Python将其读作换行符。

----------------------------------------
My system information
----------------------------------------
Platform:       macOS
OS Version:     10.15.7
Python Version: 3.9
----------------------------------------

你的问题没有说明你有哪种Outlook账户。

  1. 免费账户
  2. 公司账户

根据您在下面的评论中提到的错误消息请询问您的电子邮件管理员。,我从未在免费账户上看到这个消息,所以我假设您可能拥有一个公司账户。如果您确实拥有后者,请查看此启用或禁用已验证的客户端SMTP提交,因为电子邮件管理员需要在您的企业帐户上启用经过身份验证的SMTP


它抛出了一个错误,说“无法验证”,并要求您的电子邮件管理员。 - Echchama Nayak
@EchchamaNayak 我更新了我的答案。我能够从我的Outlook帐户多次向我的Gmail帐户发送电子邮件。 - Life is complex
我确实在使用公司账户,很抱歉忘记提到了。 - Echchama Nayak
是的,那是一个重要的细节,可以减少这个赏金问题上的工作量。希望你能说服管理员为你启用这个功能。 - Life is complex

2
用户“Life is complex”的答案几乎是正确的。我想从我的角度补充一些东西,这可能有所帮助。我不太确定你在这里使用哪个python版本。我猜测它是Python 3.X。您需要根据您使用的实际python版本进行检查。
从Python smtplib文档中,链接:link。请根据以下指南检查您使用的python版本。
class smtplib.SMTP_SSL(host='', port=0, local_hostname=None, keyfile=None, certfile=None, [timeout, ]context=None, source_address=None)。SMTP_SSL实例的行为与SMTP实例完全相同。SMTP_SSL应该用于需要从连接开始就需要SSL而且使用starttls()不合适的情况。如果未指定主机,则使用本地主机。如果端口为零,则使用标准的SMTP-over-SSL端口(465)。可选参数local_hostname、timeout和source_address与SMTP类中的含义相同。context也是可选的,可以包含SSLContext并允许配置安全连接的各个方面。请阅读最佳实践的安全注意事项。
keyfile和certfile是上下文的传统替代方案,可以指向PEM格式的私钥和证书链文件以进行SSL连接。
版本3.3中的更改:添加了上下文。
版本3.3中的更改:添加了source_address参数。
版本3.4中的更改:该类现在支持使用ssl.SSLContext.check_hostname和Server Name Indication(请参见ssl.HAS_SNI)进行主机名检查。
自版本3.6起已弃用:keyfile和certfile已弃用,建议使用context。请改为使用ssl.SSLContext.load_cert_chain(),或让ssl.create_default_context()为您选择系统的受信任CA证书。
版本3.9中的更改:如果将timeout参数设置为零,它将引发ValueError以防止创建非阻塞套接字。
您需要检查服务器端是否启用了SSLV3。首先查看此链接,以查看哪个客户端版本可以连接到哪个服务器版本:SSL Version Compatibility
import smtplib
import ssl
context = ssl.SSLContext(ssl.PROTOCOL_TLS) 
#Above line is required to switch default SSLV3 protocol to TLS, recommended by python docs
# If you want to use SSLV3 and you are sure that it is enabled on server end then use 
#context = ssl.SSLContext(ssl.PROTOCOL_SSLv3)
connection = smtplib.SMTP('smtp-mail.outlook.com', 587)
connection.ehlo()
connection.starttls(context=context)
connection.ehlo()
connection.login('now_your_real_login_data@outlook.com', 'otherwise_SMTPServerDisconnect')

我修改了我的答案。我删除了SSLContext部分,并将我的密码更改为不包含换行符的密码。我成功地发送了一封电子邮件,没有出现任何错误。 - Life is complex

0

对我来说,被接受的答案不起作用,我一直收到这个错误:

smtplib.SMTPNotSupportedError: STARTTLS extension not supported by server.

我也尝试了SMTP_SSL而不是SMTP:

context = ssl.SSLContext(ssl.PROTOCOL_TLS) 
with smtplib.SMTP_SSL('smtp-mail.outlook.com', 587, context=context) as server:
    server.ehlo()
    server.starttls(context=context)

但是出现了以下错误:

[SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1131)

幸运的是,这个帖子(感谢@dhruv-gami)帮助我找到了问题所在。

我发现我的主机名(在Windows上使用WSL)是大写的,但Outlook服务器不接受大写的主机名。

解决方案就是在ehlo命令中添加一个小写字符串:

with smtplib.SMTP('smtp.office365.com', 587) as server:
    server.ehlo('lowercase')
    server.starttls()
    server.ehlo('lowercase')
    server.login('your_login', "your_password", initial_response_ok=True) 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接