从(Python)代码实际发送邮件的正确方法是什么? [英] What is the proper way to ACTUALLY SEND mail from (Python) code?
问题描述
免责声明:由于该问题的广泛性(请参阅下文;-),我对标题犹豫不决,还包括其他选项:
Disclaimer: I hesitated on the title, due to the broad nature of this question (see below ;-), other options included:
- 如何仅使用Python代码从本地主机发送邮件?
- 如何在不使用外部SMTP服务器的情况下从Python代码发送电子邮件?
- 是否可以仅使用localhost和Python直接将电子邮件发送到目的地?
首先,一些上下文:
为了学习,我正在建立一个具有用户注册功能的网站.这个想法是注册后用户将收到一封带有激活链接的电子邮件.我想撰写&从Python代码发送电子邮件,这是我想澄清的部分.
First, a little bit of context:
For the sake of learning I am building a website with user registration feature. The idea is that after registration user will receive an email with activation link. I would like to compose & send email from Python code, and that is the part where I would like to ask for some clarifications.
我的理解,在我开始之前(显然是天真=),可以这样说明(假设有一个合法的电子邮件地址 user@example.com ):
My understanding, before I began (obviously naive =) can be illustrated like this (given that there is a legitimate email address user@example.com):
搜索示例后,我遇到了一些问题&关于stackoverflow的答案( 1 ,2 , 3 , 4 ).从这些代码中,我提取了以下代码段,以编写并发送来自Python代码的电子邮件:
After searching for examples, I bumped into some questions & answers on stackoverflow (1, 2, 3, 4). From these I've distilled the following snippet, to compose and send an email from Python code:
import smtplib
from email.message import EmailMessage
message = EmailMessage()
message.set_content('Message content here')
message['Subject'] = 'Your subject here'
message['From'] = 'me@example.com'
message['To'] = 'user@example.com'
smtp_server = smtplib.SMTP('smtp.server.address:587')
smtp_server.send_message(message)
smtp_server.quit()
接下来(很明显)的问题是传递给smtplib.SMTP()
而不是'smtp.server.address:587'
的内容.从对此答案的评论中,我发现可以通过smtp_server = smtplib.SMTP('smtp.server.address:587')
更改为smtp_server = smtplib.SMTP('localhost:1025')
,所有发送的电子邮件将显示在控制台中(从中执行python3 -m smtpd -c DebuggingServer -n localhost:1025
命令),足以进行测试-这不是我想要的(我的目标是—能够仅使用Python代码从本地计算机向真实世界"的电子邮件地址发送邮件.
Next (obvious) question was what to pass to smtplib.SMTP()
instead of 'smtp.server.address:587'
. From the comments to this answer, I discovered that local SMTP server (just for testing purposes though) could be started via python3 -m smtpd -c DebuggingServer -n localhost:1025
, then smtp_server = smtplib.SMTP('smtp.server.address:587')
could be changed to smtp_server = smtplib.SMTP('localhost:1025')
and all the sent emails will be displayed in the console (from where python3 -m smtpd -c DebuggingServer -n localhost:1025
command was executed), being enough for testing — it was not what I wanted (my aim was — the ability to send a mail to 'real-world' email address from local machine, using Python code only).
因此,下一步将是设置本地SMTP服务器,该服务器能够将电子邮件发送到外部真实世界"的电子邮件地址(因为我想通过Python代码来完成所有操作,因此服务器本身会更好也可以在Python中实现).我记得在某杂志上读过的文章(2000年初),垃圾邮件发送者使用本地服务器发送邮件(该特定文章在谈论(Python)发送的代码段不带邮件服务器的电子邮件(使用 chilkat API),尽管我需要做所有(据说) )就在代码注释中,第一行明确指出:
So, the next step would be to setup a local SMTP server, capable of sending an email to external 'real-world' email-address (as I wanted to do it all from Python code, so the server itself would better be implemented in Python too). I recalled reading in some magazine (in early 2000), that spammers use local servers for sending mails (that particular article was talking about Sambar, development for which have ended in 2007, and which was not written in Python :-) I thought there should be some present-day solution with similar functionality. So I started searching, my hope was to find (on stackoverflow or elsewhere) a reasonably short code snippet, which will do what I wanted. I haven't found such a code snippet, but I came across a snippet titled (Python) Send Email without Mail Server (which uses chilkat API), though all I needed (supposedly) was right there, in the comments to code, the first line clearly stated:
真的可以在不连接邮件服务器的情况下发送电子邮件吗?不是.
Is it really possible to send email without connecting to a mail server? Not really.
以及以下几行:
以下是声称不需要邮件服务器的其他组件内部发生的情况:该组件使用目标收件人的电子邮件地址进行DNS MX查找,以查找该域的邮件服务器(即SMTP服务器).然后,它连接到该服务器并发送电子邮件.您仍在连接到SMTP服务器,而不仅仅是您的服务器.
Here's what happens inside those other components that claim to not need a mail server: The component does a DNS MX lookup using the intended recipient's email address to find the mail server (i.e. SMTP server) for that domain. It then connects to that server and delivers the email. You're still connecting to an SMTP server — just not YOUR server.
读到这些,使我理解了-很明显,我对过程的理解(在上图中反映出)缺少一些细节.为了解决这个问题,我已经阅读了整个 SMTP上的RFC .
Reading that, made me understand — I, clearly, was lacking some details in my understanding (reflected on picture above) of the process. To correct this I have read the whole RFC on SMTP.
阅读RFC后,我对过程的了解可能如下所示:
After reading the RFC, my improved understanding of the process, might be pictured like this:
基于这种理解,我想澄清实际问题:
From this understanding, came the actual questions I'd like to clarify:
- 从总体上看,我的"理解力增强"可以被认为是正确的吗?
-
MX查找返回的确切地址是什么?
- Can my "improved understanding" be considered correct, as a general picture?
What addresses, exactly, are returned by MX lookup?
-
使用
host -t mx gmail.com
命令(由此答案建议),我能够检索以下内容:
using
host -t mx gmail.com
command (suggested by this answer), I was able to retrieve the following:
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.
smtp-relay.gmail.com
,smtp.gmail.com
,aspmx.l.google.com
)
smtp-relay.gmail.com
, smtp.gmail.com
, aspmx.l.google.com
)是否始终需要身份验证才能将电子邮件传递到已建立的邮件服务(例如gmail)的SMTP服务器?
Is authentication always needed to pass an email to SMTP-server of an established mail service (say gmail)?
I understand that to use, say
smtp.gmail.com
for mail submission, you'll need, regardless if the recipient has a@gmail
address or not (as it stated in docs):
您需要完整的Gmail或G Suite电子邮件地址才能进行身份验证.
Your full Gmail or G Suite email address is required for authentication.
但是,如果将发给user@gmail.com
的电子邮件提交给不属于gmail的SMTP服务器,则该电子邮件将被重定向到其中一个gmail服务器(直接或通过网关/中继).在这种情况下(我假设),电子邮件的发件人只需要在邮件提交时进行身份验证,那么在此之后,gmail服务器将接受未经身份验证的邮件吗?
But, if an email to user@gmail.com
is submitted to SMTP-server not owned by gmail, then it'll be redirected to one of the gmail servers (directly or via gateway/relay). In this case (I assume) sender of an email will only need to authenticate on mail submission, so after that gmail server will accept the mail without authentication?
- 如果是,是什么使我无法假装"成为这样的网关/中继并将电子邮件直接移交给其指定的SMTP?然后,编写代理SMTP"也应该非常容易,该代理将仅通过MX查找来搜索适当的服务器,然后直接向其发送电子邮件.
有关gmail SMTP的文档,还提到了
Documentation on gmail SMTP, also mentions aspmx.l.google.com
server, which does not require authentication, though:
邮件只能发送给Gmail或G Suite用户.
Mail can only be sent to Gmail or G Suite users.
话虽如此,我认为下面的代码片段应该起作用,以便将邮件提交到ExistingUser@gmail.com
邮箱:
With that being said, I assume the following snippet should work, for submitting a mail to ExistingUser@gmail.com
mailbox:
import smtplib
from email.message import EmailMessage
message = EmailMessage()
message.set_content('Message test content')
message['Subject'] = 'Test mail!'
message['From'] = 'me@whatever.com'
message['To'] = 'ExistingUser@gmail.com'
smtp_server = smtplib.SMTP('aspmx.l.google.com:25')
smtp_server.send_message(message)
smtp_server.quit()
运行时,上面的代码(用有效的邮件替换ExistingUser@gmail.com
)抛出OSError: [Errno 65] No route to host
.我只想确认的是,与aspmx.l.google.com
的通信已通过代码正确处理.
When ran, the code above (with ExistingUser@gmail.com
replaced by the valid mail) throws OSError: [Errno 65] No route to host
. All I want to confirm here is that the communication to aspmx.l.google.com
is handled correctly in code.
推荐答案
感谢这些答案,以及我的其他问题: 1 , 2 , 3 以及其他两个人的问题(和答案): —我想我现在可以自己回答自己发布的问题了.
Thanks to these answers, to my additional questions: 1, 2, 3, as well as these two questions (and answers) of other people: one, two — I think I am now ready to answer the questions I have posted, on my own.
我将一一解答这些问题:
I will address the questions one by one:
-
是的,作为一般图片,可以这样描述电子邮件的发送:
Yes, as a general picture, sending of an email can be portrayed like this:
MX查找返回接收到发往指定域的电子邮件的服务器的地址.
MX lookup returns address(es) of server(s) which receive email destined to the specified domain.
- 关于为什么
host -t mx gmail.com
命令不返回smtp-relay.gmail.com
,smtp.gmail.com
,aspmx.l.google.com
?".关于此问题的另一个答案中已涵盖了这一点.这里要掌握的要点是:-
MX查找返回的
- 服务器负责接收域电子邮件(在这种情况下为gmail) gmail文档中列出的
- 服务器是为邮件发送(即gmail用户想要发送给其他gmail用户的邮件,或其他方式已提交到这些服务器的邮件)
- As to "Why
smtp-relay.gmail.com
,smtp.gmail.com
,aspmx.l.google.com
are not returned byhost -t mx gmail.com
command?". This point is, pretty much, covered in another answer to this question. The main points to grasp here are:- servers returned by MX lookup are responsible for receiving of emails for the domain (gmail, in this particular case)
- servers listed in gmail docs are meant for the mail sending (i.e. mails that gmail user wants to send, to other gmail user or otherwise, are submitted to those servers)
不需要身份验证.
- 有几件事可以防止此类服务器被滥用:
- 许多ISP阻止到端口
25
(这是邮件接收服务器的默认端口)的出站连接,以阻止此类"直接"邮件发送 - 接收服务器方面采取了许多措施,主要是为了防止垃圾邮件,但是结果很可能也将阻止此类"直接"邮件的发送. (一些示例为: DNSBL -阻止的IP列表,
- There are a couple things that prevent such servers from being abused:
- many ISPs block outbound connections to port
25
(which is default port for mail receiving servers), to prevent such "direct" mail sending - there are numerous measures taken on the side of receiving servers, which are, mainly, intended to prevent spamming, but as a result will, likely, prevent such "direct" mail sending, as well (some examples are: DNSBL — list of blocked IPs, DKIM — is an email authentication method designed to detect forged sender addresses in emails (if you do not have your own, legitimate, mail server, you will use someone other's domain for
From
field, that is where you might be hit by DKIM)
代码段可以.该错误很可能是由于ISP一方的阻塞而产生的.
Code snippet is OK. The error is produced, in all probability, due to the blocking on the ISP's side.
话虽如此,代码片段:
With all that being said, code snippet:
import smtplib from email.message import EmailMessage message = EmailMessage() message.set_content('Message content here') message['Subject'] = 'Your subject here' message['From'] = 'me@example.com' message['To'] = 'user@example.com' smtp_server = smtplib.SMTP('smtp.server.address:25') smtp_server.send_message(message) smtp_server.quit()
实际上会发送电子邮件(请参阅此问题(对于实际情况,是一个工作示例)),因为
smtp.server.address:25
是合法服务器,并且在ISP和/或smtp.server.address
方面没有任何阻塞.will actually send an email (see this question, for real-world, working example) given that
smtp.server.address:25
is legitimate server and there are no blockings on ISP's and/orsmtp.server.address
side.这篇关于从(Python)代码实际发送邮件的正确方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- many ISPs block outbound connections to port
- 许多ISP阻止到端口