如何使用TLS/SSL和Python发送Office365的SMTP电子邮件

33
我正在尝试使用Python从我的Office365公司帐户发送电子邮件。我对Python不熟悉。在使用我的Hotmail帐户时,此代码以前可以正常工作,但是现在我需要发送机密信息,因此必须使用我的公司电子邮件。
我尝试了几个方法:
- 验证我的用户名和密码是否正确。 - 使用Python2和Python3。两者都会出现相同的错误:535 5.7.3 Authentication unsuccessful - 在出现上述错误时,我之前使用了mailserver.starttls(),然后经过一些研究,我尝试传递一个证书。mailserver.starttls(certfile='office365.cer') 我对证书部分不清楚,但我的步骤包括,在网上查找如何导出证书。使用Chrome浏览器,microsoftonline.com有一个链式证书。我可以导出根和根下面的级别,但无法导出最后一级。我不知道如何传递这两个文件,所以我只传递了根证书。此时,我收到以下错误:ssl.SSLError: [SSL] PEM lib (_ssl.c:3309) 我在这一点上卡住了。感谢任何帮助。
以下是使用的代码:
import smtplib

mailserver = smtplib.SMTP('smtp.office365.com',587)
mailserver.ehlo()
mailserver.starttls(certfile='office365.cer')
mailserver.ehlo()
mailserver.login('user@company.co', 'password')
mailserver.sendmail('user@company.co','user@company.co','python email')
mailserver.quit()
6个回答

49

好的,你已经接近成功了。以下代码可以解决问题:

import smtplib

mailserver = smtplib.SMTP('smtp.office365.com',587)
mailserver.ehlo()
mailserver.starttls()
mailserver.login('user@company.co', 'password')
#Adding a newline before the body text fixes the missing message body
mailserver.sendmail('user@company.co','user@company.co','\npython email')
mailserver.quit()

查看以下链接获取更多信息:

Python: 通过 Office 365 发送电子邮件

https://docs.python.org/3/library/smtplib.html

https://gist.github.com/jasonjoh/3ec367594c3fa662ee983a617bdc7deb


6
注意:使用smtplib.SMTP()而不是smtplib.SMTP_SSL()!使用smtplib.SMTP_SSL()仍然会出现"[SSL: WRONG_VERSION_NUMBER]"错误。将其替换为smtplib.SMTP()对我有用。 - Jan Wilmans
mailserver.ehlo() 不是必需的。根据 starttls() 的文档:“如果在此会话中没有先前的 EHLO 或 HELO 命令,则此方法首先尝试 ESMTP EHLO。” - tomatoeshift
6
也许这很显而易见,但如果您的企业账户需要多因素认证,用户名和密码是不足以进行身份验证的。 - zhark
嗨,我有一个奇怪的问题,当我在Python解释器终端上执行代码序列时,它完全正常。然而,如果我将代码放到.PY文件中并在VS Code或Mac终端上运行它,它就会卡住! :( 有什么建议吗? - Deepak V
不是@GalSilberman,我保证,这是同样的代码复制粘贴到.py文件中:(这就是为什么我要抓狂。当我将其作为Python程序运行时,代码停留在以下位置:mailserver = smtplib.SMTP('smtp.office365.com',587)终端继续输出空白行,执行等待... - Deepak V
显示剩余4条评论

15

我找到了一個對我有用的庫:

https://github.com/O365/python-o365

https://pypi.python.org/pypi/O365

使用PIP安装它,然后:

from O365 import Message
o365_auth = ('YourAccount@office365.com','YourPassword')
m = Message(auth=o365_auth)
m.setRecipients('reciving@office365.com')
m.setSubject('I made an email script.')
m.setBody('Talk to the computer, cause the human does not want to hear it any more.')
m.sendMessage()

4
我想指出,未来有人看到这条信息时,虽然这个库可以使用,但它并不使用SMTP。它使用Microsoft Outlook/Graph API来访问您的Office 365帐户并发送电子邮件。如果您想使用SMTP,请参见Gal的回答。 - BigGerman
1
@BigGerman,您能为我们这些凡人解释一下吗?使用这段代码与上面有什么不同? - Umar.H
6
@Datanovice SMTP是一种用于发送电子邮件的协议。使用smtplib,您可以直接使用SMTP协议将电子邮件发送到Office365邮件服务器。使用nacho-parra的答案,您将使用一个Python模块(O365),该模块会向Microsoft Graph API发送HTTP请求,然后发送电子邮件。它基本上实现了相同的结果(发送电子邮件),但使用不同的方法。最初的问题是关于如何使用SMTP发送电子邮件的,因此gal-silberman的答案实际上展示了如何完成这个过程。 - BigGerman
@BigGerman,你真是个牛人,编辑得非常简洁明了,干杯。我想知道哪种资源更昂贵,或者API是否有电子邮件限制。 - Umar.H
2
@Datanovice SMTP发送基本电子邮件肯定更简单,但Graph API除了发送电子邮件之外还可以做更多的事情。就API的速率限制而言,它们非常慷慨,但是它们确实有一些限流。我遇到限流的唯一时候是从电子邮件收件箱中检索10,000多封邮件时。SMTP服务器也可以由提供者设置其自己的限制,请参见此处 - BigGerman
@BigGerman,这非常符合我的使用情况。我正在研究一种通过几个Python脚本自动化合规性,并将此内容发送给我的门店经理(总共约450个)的方法。我认为我可以应对限制。 - Umar.H

8

对我来说,@Prometheus提供的答案会导致出现“RuntimeError: No auth token found. Authentication Flow needed”的错误,正如评论中所述。可能是因为我的公司电子邮件启用了2fa。因此,我必须遵循https://github.com/janscas/pyo365#authenticationhttps://pypi.org/project/O365/#authentication提供的步骤。

要使用OAuth,您首先需要在Microsoft应用程序注册门户上注册您的应用程序。

  1. 登录Microsoft Application Registration Portal
  2. 创建应用程序,注意您的应用程序ID(client_id)
  3. 在“应用程序秘密”部分下生成新密码(client_secret),在“平台”部分下添加一个新的Web平台并将https://login.microsoftonline.com/common/oauth2/nativeclient设置为重定向URL
  4. 进入API权限>添加权限>Microsoft Graph>委派权限,添加以下权限:
    • IMAP.AccessAsUser.All

    • Mail.Send

    • POP.AccessAsUser.All

    • User.Read

    • SMTP.Send

    • offline_access # 如果您希望在o365_token.txt中刷新令牌,请使用此选项。否则,您需要每1小时获取访问令牌。 enter image description here

  5. 运行以下Python脚本以获取访问令牌,该令牌将存储在当前目录中的名为o365_token.txt的文件中
from O365 import Account

scopes =  ["IMAP.AccessAsUser.All", "POP.AccessAsUser.All", "SMTP.Send", "Mail.Send", "offline_access"]

account = Account(credentials=('client_id_of_azureapp', 'client_secret_of_azureapp'))
result = account.authenticate(scopes=scopes)  # request a token for this scopes
  1. 使用以下脚本,通过提供在前一步骤中生成的authtoken文件来发送电子邮件。
from O365 import Account
from O365.utils.token import FileSystemTokenBackend
tk = FileSystemTokenBackend(token_path=".", token_filename="o365_token.txt")

credentials = ('client_id', 'client_secret')

account = Account(credentials, auth_flow_type = 'public',token_backend=tk)
m = account.new_message()
m.to.add('user@company.com')
m.subject = 'Testing!'
m.body = "George Best quote: I've stopped drinking, but only while I'm asleep."
m.send()

注意: 如果您拥有管理员同意或者是管理员或Azure账户,您可以跳过第4、5、6步骤,直接使用以下代码。
from O365 import Account
from O365.utils.token import FileSystemTokenBackend
tk = FileSystemTokenBackend(token_path=".", token_filename="o365_token.txt")

credentials = ('client_id', 'client_secret') # from step 2,3
account = Account(credentials, auth_flow_type = 'credentials', tenant_id="your_app_tenant_id") # tenant_id (required) available just below client_id in azure

if account.authenticate():
    print('Authenticated!')

mailbox = account.mailbox("user@company.com") # Your email (required) from which you want to send email (your app should have permission to this email)
m = mailbox.new_message()
m.to.add('touser@company.com')
m.subject = 'Testing!'
m.body = "George Best quote: I've stopped drinking, but only while I'm asleep."
m.send()

3

问题很可能不在你的代码中,而是在Exchange Online配置中。

我敢打赌,535 5.7.3身份验证失败是因为在您的Exchange Online组织中禁用了经过身份验证的SMTP(SMTP AUTH协议)。

在这里您可以找到我的答案,解释如何为您发送电子邮件的用户启用SMTP AUTH。您必须是Office 365组织管理员才能这样做,或者可以请求管理员帮助。

之后,mailserver.starttls()应该可以正常工作。请注意,在该调用中不需要指定证书。


3
代码已经稍作更改。上面的代码无法运行,请使用下面的代码。 参考链接
from O365 import Account

credentials = ('client_id', 'client_secret')

account = Account(credentials)
m = account.new_message()
m.to.add('to_example@example.com')
m.subject = 'Testing!'
m.body = "George Best quote: I've stopped drinking, but only while I'm asleep."
m.send()

7
在使用此功能时,我遇到了以下错误:“运行时错误:未找到身份验证令牌。需要进行身份验证流程。” - Shashi Shankar Singh
@ShashiShankarSingh 你在Azure中设置客户端令牌了吗? - Daniel W.
@Shashi Shankar Singh,请查看我的答案 https://dev59.com/W1YO5IYBdhLWcg3wN_Cf#71148071 我已经成功解决了这个问题。 - A Yashwanth

1

以下是对@Gal的答案更为简洁的替代方案,对我很有效:

    msg = MIMEText(f'Pubblicati i risultati per la ricerca: `{search_result}`\n\nPer maggiori dettagli, visita: {url}')  # replace with your email message
    msg['Subject'] = f"Pubblicazione criteri di valutazione {search_result.split(' ')[2]}"
    msg['From'] = from_email
    msg['To'] = to_email
    with smtplib.SMTP('smtp.office365.com', 587) as smtp:
        smtp.starttls()
        smtp.login(from_email, password)
        smtp.send_message(msg)

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