免责声明:由于这个问题的广泛性(见下文;-),我对标题犹豫不决,其他选项包括:
首先,一点上下文: 为了学习,我正在建立一个具有用户注册功能的网站。这个想法是,注册后用户将收到一封带有激活链接的电子邮件。我想编写并发送来自Python代码的电子邮件,这是我想要求澄清的部分。
我的理解,在我开始之前(显然天真=)可以这样说明(假设有一个合法的电子邮件地址[email protected]):
在搜索了例子之后,我在stackoverflow(1,2,3,4)上遇到了一些问题和答案。从这些我已经提炼出以下片段,撰写并发送来自Python代码的电子邮件:
import smtplib
from email.message import EmailMessage
message = EmailMessage()
message.set_content('Message content here')
message['Subject'] = 'Your subject here'
message['From'] = '[email protected]'
message['To'] = '[email protected]'
smtp_server = smtplib.SMTP('smtp.server.address:587')
smtp_server.send_message(message)
smtp_server.quit()
接下来(明显的)问题是传递给smtplib.SMTP()
而不是'smtp.server.address:587'
。从评论到this answer,我发现本地SMTP服务器(仅用于测试目的)可以通过python3 -m smtpd -c DebuggingServer -n localhost:1025
启动,然后smtp_server = smtplib.SMTP('smtp.server.address:587')
可以更改为smtp_server = smtplib.SMTP('localhost:1025')
,所有发送的电子邮件将显示在控制台中(从那里执行python3 -m smtpd -c DebuggingServer -n localhost:1025
命令) ),足以进行测试 - 这不是我想要的(我的目标是 - 能够使用Python代码从本地机器向'真实世界'电子邮件地址发送邮件)。
因此,下一步是设置一个本地SMTP服务器,能够向外部“真实世界”的电子邮件地址发送电子邮件(因为我想从Python代码中完成所有操作,因此服务器本身最好在Python也)。我回忆起在一些杂志(2000年初)中读取垃圾邮件发送者使用本地服务器发送邮件(特定文章是关于Sambar,其开发已于2007年结束,而且不是用Python编写的:-)我想到了应该是一些具有类似功能的现今解决方案。所以我开始搜索,我希望找到(在stackoverflow或其他地方)一个相当短的代码片段,它将做我想要的。我还没有找到这样的代码片段,但是我遇到了一个标题为(Python) Send Email without Mail Server(使用chilkat API)的片段,尽管我需要的(据说)就在那里,在代码的注释中,第一行明确说明:
是否真的可以在不连接邮件服务器的情况下发送电子邮件?并不是的。
以下几行:
以下是声称不需要邮件服务器的其他组件内部发生的情况:组件使用预期收件人的电子邮件地址执行DNS MX查找,以查找该域的邮件服务器(即SMTP服务器)。然后它连接到该服务器并发送电子邮件。您仍然连接到SMTP服务器 - 而不是您的服务器。
阅读这些,让我理解 - 我显然缺乏一些细节(我在上面的图片中反映过程)。为了纠正这个问题,我已经阅读了整个RFC on SMTP。
根据这种理解,我想澄清一些实际问题:
host -t mx gmail.com
命令(由this answer建议),我能够检索以下内容:
gmail.com mail is handled by 10 alt1.gmail-smtp-in.l.google.com.
gmail.com mail is handled by 20 alt2.gmail-smtp-in.l.google.com.
gmail.com mail is handled by 40 alt4.gmail-smtp-in.l.google.com.
gmail.com mail is handled by 30 alt3.gmail-smtp-in.l.google.com.
gmail.com mail is handled by 5 gmail-smtp-in.l.google.com.
但在official docs中没有提及这些(那里有:smtp-relay.gmail.com
,smtp.gmail.com
,aspmx.l.google.com
)smtp.gmail.com
提交邮件,你需要,无论收件人是否有@gmail
地址(如docs所述):
您需要完整的Gmail或G Suite电子邮件地址进行身份验证。
但是,如果将[email protected]
的电子邮件提交给不属于gmail的SMTP服务器,则会将其重定向到其中一个gmail服务器(直接或通过网关/中继)。在这种情况下(我假设)电子邮件的发件人只需要在邮件提交上进行身份验证,那么之后gmail服务器将接受邮件而不进行身份验证?
如果是,是什么阻止我“假装”成为这样的网关/中继并将电子邮件直接交给他们指定的SMTP?然后,编写“代理SMTP”也很容易,它只是通过MX查找搜索适当的服务器,并直接向其发送电子邮件。aspmx.l.google.com
服务器,它不需要身份验证,但是:
邮件只能发送给Gmail或G Suite用户。
话虽如此,我假设以下代码片段应该有效,用于向[email protected]
邮箱提交邮件:
import smtplib
from email.message import EmailMessage
message = EmailMessage()
message.set_content('Message test content')
message['Subject'] = 'Test mail!'
message['From'] = '[email protected]'
message['To'] = '[email protected]'
smtp_server = smtplib.SMTP('aspmx.l.google.com:25')
smtp_server.send_message(message)
smtp_server.quit()
运行时,上面的代码(用有效邮件替换[email protected]
)会抛出OSError: [Errno 65] No route to host
。我想在此确认的是,在代码中正确处理了与aspmx.l.google.com
的通信。您对邮件如何工作的理解大致正确。一些额外的注释可能会让事情变得清晰:
为了发送邮件,您至少需要SMTP的第二部分:与另一个MTA交谈以传递邮件的能力。
发送邮件的典型方法是在应用程序中撰写邮件,然后将其发送到MTA邮件服务器进行传递。根据您的设置,MTA既可以安装在运行Python代码的同一台机器上(localhost),也可以是更“中央”的邮件服务器(可能需要身份验证)。
“你的”MTA将负责处理邮件的所有令人讨厌的细节,例如:
当然,您可以在自己的Python代码中重新实现所有这些功能,并有效地将MTA与您的应用程序相结合,但我强烈反对它。邮件令人惊讶地难以正确...
结论:尝试通过SMTP将邮件发送到提供商的邮件服务器或其他邮件服务。如果这是不可能的:如果你想运行自己的邮件服务器,请认真考虑。被标记为垃圾邮件发送者很容易发生;从垃圾邮件列表中删除更加困难。不要在应用程序中重新实现SMTP代码。
感谢这些答案,还有我的其他问题:1,2,3,以及其他人的这两个问题(和答案):one,two - 我想我现在准备好回答我发布的问题,在我的问题上拥有。
我将逐一解决这些问题:
smtp-relay.gmail.com
,smtp.gmail.com
,aspmx.l.google.com
不会被host -t mx gmail.com
命令归还?”。 another answer对这个问题几乎涵盖了这一点。这里要掌握的要点是:
MX查找返回的服务器负责接收域的电子邮件(在这种情况下为gmail)
gmail docs中列出的服务器用于邮件发送(即gmail用户想要发送给其他gmail用户或其他方式的邮件,提交给那些服务器)25
(这是邮件接收服务器的默认端口)的出站连接,以防止这种“直接”邮件发送
在接收服务器方面采取了许多措施,主要是为了防止发送垃圾邮件,但结果也可能阻止这种“直接”邮件发送(例如:DNSBL - 阻止列表IP,DKIM - 是一种电子邮件身份验证方法,用于检测电子邮件中的伪造发件人地址(如果您没有自己的,合法的邮件服务器,您将使用其他人的域名来访问From
字段,这是您可能被DKIM攻击的地方)尽管如此,代码片段:
import smtplib
from email.message import EmailMessage
message = EmailMessage()
message.set_content('Message content here')
message['Subject'] = 'Your subject here'
message['From'] = '[email protected]'
message['To'] = '[email protected]'
smtp_server = smtplib.SMTP('smtp.server.address:25')
smtp_server.send_message(message)
smtp_server.quit()
鉴于this question是合法的服务器并且ISP和/或smtp.server.address:25
方面没有阻止,实际上会发送一封电子邮件(参见smtp.server.address
,用于真实世界,工作示例)。