簡體   English   中英

如何用 Python 發送 email?

[英]How to send an email with Python?

此代碼有效並向我發送 email 就好了:

import smtplib
#SERVER = "localhost"

FROM = 'monty@python.com'

TO = ["jon@mycompany.com"] # must be a list

SUBJECT = "Hello!"

TEXT = "This message was sent with Python's smtplib."

# Prepare actual message

message = """\
From: %s
To: %s
Subject: %s

%s
""" % (FROM, ", ".join(TO), SUBJECT, TEXT)

# Send the mail

server = smtplib.SMTP('myserver')
server.sendmail(FROM, TO, message)
server.quit()

但是,如果我嘗試將其包裝在 function 中,如下所示:

def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
    import smtplib
    """this is some test documentation in the function"""
    message = """\
        From: %s
        To: %s
        Subject: %s
        %s
        """ % (FROM, ", ".join(TO), SUBJECT, TEXT)
    # Send the mail
    server = smtplib.SMTP(SERVER)
    server.sendmail(FROM, TO, message)
    server.quit()

並稱之為我收到以下錯誤:

 Traceback (most recent call last):
  File "C:/Python31/mailtest1.py", line 8, in <module>
    sendmail.sendMail(sender,recipients,subject,body,server)
  File "C:/Python31\sendmail.py", line 13, in sendMail
    server.sendmail(FROM, TO, message)
  File "C:\Python31\lib\smtplib.py", line 720, in sendmail
    self.rset()
  File "C:\Python31\lib\smtplib.py", line 444, in rset
    return self.docmd("rset")
  File "C:\Python31\lib\smtplib.py", line 368, in docmd
    return self.getreply()
  File "C:\Python31\lib\smtplib.py", line 345, in getreply
    raise SMTPServerDisconnected("Connection unexpectedly closed")
smtplib.SMTPServerDisconnected: Connection unexpectedly closed

誰能幫我理解為什么?

我建議您使用標准包emailsmtplib一起發送 email。 請看下面的例子(轉載自Python 文檔)。 請注意,如果您采用這種方法,“簡單”任務確實很簡單,而更復雜的任務(如附加二進制對象或發送純/HTML 多部分消息)可以非常快速地完成。

# Import smtplib for the actual sending function
import smtplib

# Import the email modules we'll need
from email.mime.text import MIMEText

# Open a plain text file for reading.  For this example, assume that
# the text file contains only ASCII characters.
with open(textfile, 'rb') as fp:
    # Create a text/plain message
    msg = MIMEText(fp.read())

# me == the sender's email address
# you == the recipient's email address
msg['Subject'] = 'The contents of %s' % textfile
msg['From'] = me
msg['To'] = you

# Send the message via our own SMTP server, but don't include the
# envelope header.
s = smtplib.SMTP('localhost')
s.sendmail(me, [you], msg.as_string())
s.quit()

要將 email 發送到多個目的地,您還可以按照Python 文檔中的示例進行操作:

# Import smtplib for the actual sending function
import smtplib

# Here are the email package modules we'll need
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart

# Create the container (outer) email message.
msg = MIMEMultipart()
msg['Subject'] = 'Our family reunion'
# me == the sender's email address
# family = the list of all recipients' email addresses
msg['From'] = me
msg['To'] = ', '.join(family)
msg.preamble = 'Our family reunion'

# Assume we know that the image files are all in PNG format
for file in pngfiles:
    # Open the files in binary mode.  Let the MIMEImage class automatically
    # guess the specific image type.
    with open(file, 'rb') as fp:
        img = MIMEImage(fp.read())
    msg.attach(img)

# Send the email via our own SMTP server.
s = smtplib.SMTP('localhost')
s.sendmail(me, family, msg.as_string())
s.quit()

如您所見,MIMEText object 中的MIMEText To必須是由逗號分隔的 email 地址組成的字符串。 另一方面, sendmail function 的第二個參數必須是一個字符串列表(每個字符串都是一個 email 地址)。

So, if you have three email addresses: person1@example.com , person2@example.com , and person3@example.com , you can do as follows (obvious sections omitted):

to = ["person1@example.com", "person2@example.com", "person3@example.com"]
msg['To'] = ",".join(to)
s.sendmail(me, to, msg.as_string())

",".join(to)部分從列表中生成一個字符串,用逗號分隔。

根據您的問題,我了解到您還沒有閱讀 Python 教程- 如果您想在 Python 中找到任何地方,這是必須的 - 該文檔對於標准庫來說非常出色。

當我需要在 Python 中發送郵件時,我會使用郵件槍 API ,它在發送郵件時會遇到很多麻煩。 他們有一個很棒的應用程序/api,可讓您每月發送 5,000 封免費電子郵件。

發送 email 如下所示:

def send_simple_message():
    return requests.post(
        "https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages",
        auth=("api", "YOUR_API_KEY"),
        data={"from": "Excited User <mailgun@YOUR_DOMAIN_NAME>",
              "to": ["bar@example.com", "YOU@YOUR_DOMAIN_NAME"],
              "subject": "Hello",
              "text": "Testing some Mailgun awesomness!"})

您還可以跟蹤事件等等,請參閱快速入門指南

我想通過向 yagmail package 提供建議來幫助您發送電子郵件(我是維護者,抱歉打廣告,但我覺得它真的很有幫助。)。

您的整個代碼將是:

import yagmail
yag = yagmail.SMTP(FROM, 'pass')
yag.send(TO, SUBJECT, TEXT)

請注意,我為所有 arguments 提供了默認值,例如,如果您想發送給自己,則可以省略TO ,如果您不想要主題,也可以省略它。

此外,目標還在於使附加 html 代碼或圖像(和其他文件)變得非常容易。

在放置內容的地方,您可以執行以下操作:

contents = ['Body text, and here is an embedded image:', 'http://somedomain/image.png',
            'You can also find an audio file attached.', '/local/path/song.mp3']

哇,發送附件多么容易; 如果沒有 yagmail,這將需要 20 行;)

此外,如果您設置一次,則無需再次輸入密碼(並將其安全存儲)。 在您的情況下,您可以執行以下操作:

import yagmail
yagmail.SMTP().send(contents = contents)

這更簡潔!

我邀請您查看github或直接使用pip install yagmail安裝它。

有縮進問題。 下面的代碼將起作用:

import textwrap

def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
    import smtplib
    """this is some test documentation in the function"""
    message = textwrap.dedent("""\
        From: %s
        To: %s
        Subject: %s
        %s
        """ % (FROM, ", ".join(TO), SUBJECT, TEXT))
    # Send the mail
    server = smtplib.SMTP(SERVER)
    server.sendmail(FROM, TO, message)
    server.quit()

這是 Python 3.x的示例,比2.x簡單得多:

import smtplib
from email.message import EmailMessage
def send_mail(to_email, subject, message, server='smtp.example.cn',
              from_email='xx@example.com'):
    # import smtplib
    msg = EmailMessage()
    msg['Subject'] = subject
    msg['From'] = from_email
    msg['To'] = ', '.join(to_email)
    msg.set_content(message)
    print(msg)
    server = smtplib.SMTP(server)
    server.set_debuglevel(1)
    server.login(from_email, 'password')  # user & password
    server.send_message(msg)
    server.quit()
    print('successfully sent the mail.')

稱之為 function:

send_mail(to_email=['12345@qq.com', '12345@126.com'],
          subject='hello', message='Your analysis has done!')

以下僅適用於中國用戶:

如果使用126/163、網易郵箱,需要設置“客戶端授權密碼”,如下圖:

在此處輸入圖像描述

參考: https://stackoverflow.com/a/41470149/2803344 https://docs.python.org/

嘗試這個:

def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
    import smtplib
    """this is some test documentation in the function"""
    message = """\
        From: %s
        To: %s
        Subject: %s
        %s
        """ % (FROM, ", ".join(TO), SUBJECT, TEXT)
    # Send the mail
    server = smtplib.SMTP(SERVER)
    "New part"
    server.starttls()
    server.login('username', 'password')
    server.sendmail(FROM, TO, message)
    server.quit()

它適用於 smtp.gmail.com

在 function 中縮進代碼時(沒關系),您還縮進了原始消息字符串的行。 但前導空白意味着 header 行的折疊(連接),如RFC 2822 - Internet Message Format的第 2.2.3 和 3.2.3 節所述:

每個 header 字段在邏輯上都是由字段名稱、冒號和字段正文組成的單行字符。 然而,為了方便起見,並處理每行 998/78 個字符的限制,header 字段的字段主體部分可以拆分為多行表示; 這稱為“折疊”。

在您的sendmail調用的 function 形式中,所有行都以空格開頭,因此“展開”(連接)並且您正在嘗試發送

From: monty@python.com    To: jon@mycompany.com    Subject: Hello!    This message was sent with Python's smtplib.

除了我們的想法之外, smtplib將不再理解To:Subject:標頭,因為這些名稱僅在行首被識別。 相反, smtplib將假定一個非常長的發送方 email 地址:

monty@python.com    To: jon@mycompany.com    Subject: Hello!    This message was sent with Python's smtplib.

這不起作用,所以你的例外。

解決方案很簡單:只保留以前的message字符串。 這可以通過 function (如 Zeeshan 建議)或立即在源代碼中完成:

import smtplib

def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
    """this is some test documentation in the function"""
    message = """\
From: %s
To: %s
Subject: %s

%s
""" % (FROM, ", ".join(TO), SUBJECT, TEXT)
    # Send the mail
    server = smtplib.SMTP(SERVER)
    server.sendmail(FROM, TO, message)
    server.quit()

現在展開不會發生,你發送

From: monty@python.com
To: jon@mycompany.com
Subject: Hello!

This message was sent with Python's smtplib.

這就是您的舊代碼的工作原理和所做的工作。

請注意,我還保留了標題和正文之間的空行以適應RFC的第 3.5 節(這是必需的),並根據 Python 樣式指南PEP-0008 (這是可選的)將包含放在 function 之外。

確保您已授予發送者和接收者發送 email 並從 Email 帳戶中的未知來源(外部來源)接收 email 的權限。

import smtplib

#Ports 465 and 587 are intended for email client to email server communication - sending email
server = smtplib.SMTP('smtp.gmail.com', 587)

#starttls() is a way to take an existing insecure connection and upgrade it to a secure connection using SSL/TLS.
server.starttls()

#Next, log in to the server
server.login("#email", "#password")

msg = "Hello! This Message was sent by the help of Python"

#Send the mail
server.sendmail("#Sender", "#Reciever", msg)

在此處輸入圖像描述

它可能會在您的消息中添加標簽。 在將消息傳遞給 sendMail 之前打印出消息。

因為我剛剛弄清楚這是如何工作的,所以我想我會在這里輸入我的兩個位。

看來您沒有在 SERVER 連接設置中指定端口,當我嘗試連接到未使用默認端口的 SMTP 服務器時,這對我產生了一點影響:25。

根據 smtplib.SMTP 文檔,您的 ehlo 或 helo 請求/響應應自動得到處理,因此您不必擔心這一點(但如果所有其他方法都失敗,則可能需要確認)。

要問自己的另一件事是您是否允許 SMTP 服務器本身上的 SMTP 連接? 對於像 GMAIL 和 ZOHO 這樣的網站,您實際上必須在 go 中激活 email 帳戶中的 IMAP 連接。 您的郵件服務器可能不允許不是來自“本地主機”的 SMTP 連接? 有什么要調查的。

最后一件事是您可能想嘗試在 TLS 上啟動連接。 大多數服務器現在都需要這種類型的身份驗證。

您會看到我在 email 中插入了兩個 TO 字段。 msg['TO'] 和 msg['FROM'] msg 字典項允許正確的信息顯示在 email 本身的標題中,可以在 email 的接收端的 To/From 字段中看到(您甚至可以在這里添加一個回復字段。TO 和 FROM 字段本身就是服務器所需要的。我知道我聽說一些 email 服務器如果沒有正確的 email 標頭會拒絕電子郵件。

這是我在 function 中使用的代碼,它適用於 email 使用我的本地計算機和遠程 ZC2239A92BDE29F0A9F9173193CC2FE00 服務器的 *.txt 文件的內容(如圖所示)

def emailResults(folder, filename):

    # body of the message
    doc = folder + filename + '.txt'
    with open(doc, 'r') as readText:
        msg = MIMEText(readText.read())

    # headers
    TO = 'to_user@domain.com'
    msg['To'] = TO
    FROM = 'from_user@domain.com'
    msg['From'] = FROM
    msg['Subject'] = 'email subject |' + filename

    # SMTP
    send = smtplib.SMTP('smtp.zoho.com', 587)
    send.starttls()
    send.login('from_user@domain.com', 'password')
    send.sendmail(FROM, TO, msg.as_string())
    send.quit()

使用 gmail 的另一個實現讓我們說:

import smtplib

def send_email(email_address: str, subject: str, body: str):
"""
send_email sends an email to the email address specified in the
argument.

Parameters
----------
email_address: email address of the recipient
subject: subject of the email
body: body of the email
"""

server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login("email_address", "password")
server.sendmail("email_address", email_address,
                "Subject: {}\n\n{}".format(subject, body))
server.quit()
import smtplib

s = smtplib.SMTP(your smtp server, smtp port) #SMTP session

message = "Hii!!!"

s.sendmail("sender", "Receiver", message) # sending the mail

s.quit() # terminating the session

值得注意的是,SMTP 模塊支持上下文管理器,因此無需手動調用 quit(),這樣可以保證即使出現異常也始終調用它。

    with smtplib.SMTP_SSL('smtp.gmail.com', 465) as server:
        server.ehlo()
        server.login(user, password)
        server.sendmail(from, to, body)
import smtplib, ssl

port = 587  # For starttls
smtp_server = "smtp.office365.com"
sender_email = "170111018@student.mit.edu.tr"
receiver_email = "professordave@hotmail.com"
password = "12345678"
message = """\
Subject: Final exam

Teacher when is the final exam?"""

def SendMailf():
    context = ssl.create_default_context()
    with smtplib.SMTP(smtp_server, port) as server:
        server.ehlo()  # Can be omitted
        server.starttls(context=context)
        server.ehlo()  # Can be omitted
        server.login(sender_email, password)
        server.sendmail(sender_email, receiver_email, message)
        print("mail send")

我對用於發送電子郵件的 package 選項不滿意,我決定制作並開源我自己的 email 發件人。 它易於使用並且能夠處理高級用例。

安裝:

pip install redmail

用法:

from redmail import EmailSender
email = EmailSender(
    host="<SMTP HOST ADDRESS>",
    port=<PORT NUMBER>,
)

email.send(
    sender="me@example.com",
    receivers=["you@example.com"],
    subject="An example email",
    text="Hi, this is text body.",
    html="<h1>Hi,</h1><p>this is HTML body</p>"
)

如果您的服務器需要用戶名和密碼,只需將用戶名和password傳遞EmailSender user_name

我在send方法中包含了很多特性:

  • 包括附件
  • 將圖像直接包含到 HTML 主體
  • Jinja 模板
  • 開箱即用的更漂亮的 HTML 表

文檔: https://red-mail.readthedocs.io/en/latest/

源碼: https://github.com/Miksus/red-mail

在對示例進行了大量擺弄之后,例如這里現在對我有用:

import smtplib
from email.mime.text import MIMEText

# SMTP sendmail server mail relay
host = 'mail.server.com'
port = 587 # starttls not SSL 465 e.g gmail, port 25 blocked by most ISPs & AWS
sender_email = 'name@server.com'
recipient_email = 'name@domain.com'
password = 'YourSMTPServerAuthenticationPass'
subject = "Server - "
body = "Message from server"

def sendemail(host, port, sender_email, recipient_email, password, subject, body):
    try:
        p1 = f'<p><HR><BR>{recipient_email}<BR>'
        p2 = f'<h2><font color="green">{subject}</font></h2>'
        p3 = f'<p>{body}'
        p4 = f'<p>Kind Regards,<BR><BR>{sender_email}<BR><HR>'
        
        message = MIMEText((p1+p2+p3+p4), 'html')  
        # servers may not accept non RFC 5321 / RFC 5322 / compliant TXT & HTML typos

        message['From'] = f'Sender Name <{sender_email}>'
        message['To'] = f'Receiver Name <{recipient_email}>'
        message['Cc'] = f'Receiver2 Name <>'
        message['Subject'] = f'{subject}'
        msg = message.as_string()

        server = smtplib.SMTP(host, port)
        print("Connection Status: Connected")
        server.set_debuglevel(1)
        server.ehlo()
        server.starttls()
        server.ehlo()
        server.login(sender_email, password)
        print("Connection Status: Logged in")
        server.sendmail(sender_email, recipient_email, msg)
        print("Status: Email as HTML successfully sent")

    except Exception as e:
            print(e)
            print("Error: unable to send email")

# Run
sendemail(host, port, sender_email, recipient_email, password, subject, body)
print("Status: Exit")

我寫了一個簡單的 function send_email()用於 email 發送與smtplibemail包(鏈接到我的文章)。 它還使用dotenv package 加載發件人 email 和密碼(請不要在代碼中保密。)。 我將 Gmail 用於 email 服務。 密碼是App Password (這里是Google docs on how to generate App Password )。

import os
import smtplib
from email.message import EmailMessage
from dotenv import load_dotenv
_ = load_dotenv()


def send_email(to, subject, message):
    try:
        email_address = os.environ.get("EMAIL_ADDRESS")
        email_password = os.environ.get("EMAIL_PASSWORD")

        if email_address is None or email_password is None:
            # no email address or password
            # something is not configured properly
            print("Did you set email address and password correctly?")
            return False

        # create email
        msg = EmailMessage()
        msg['Subject'] = subject
        msg['From'] = email_address
        msg['To'] = to
        msg.set_content(message)

        # send email
        with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp:
            smtp.login(email_address, email_password)
            smtp.send_message(msg)
        return True
    except Exception as e:
        print("Problem during send email")
        print(str(e))
    return False

上述方法對於簡單的 email 發送是可以的。 如果您正在尋找更高級的功能,例如 HTML 內容或附件 - 當然可以手動編碼,但我建議使用現有的包,例如yagmail

Gmail 每天限制為 500 封電子郵件。 對於每天發送大量電子郵件,請考慮事務性 email 服務提供商,例如 Amazon SES、MailGun、MailJet 或 SendGrid。

只是為了補充答案,以便您的郵件傳遞系統可以擴展。

我建議使用發件人的 email 配置、密碼和收件人的配置文件(可以是.json、.yml、.ini 等)。

通過這種方式,您可以根據需要創建不同的可定制項目。

下面是一個包含 3 個文件、配置、函數和主文件的小示例。 純文本郵件。

config_email.ini

[email_1]
sender = test@test.com
password = XXXXXXXXXXX
recipients= ["email_2@test.com", "email_2@test.com"]

[email_2]
sender = test_2@test.com
password = XXXXXXXXXXX
recipients= ["email_2@test.com", "email_2@test.com", "email_3@test.com"]

這些項目將從main.py ,這將返回它們各自的值。

具有函數functions_email.py的文件:

import smtplib,configparser,json
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

def get_credentials(item):
    parse = configparser.ConfigParser()
    parse.read('config_email.ini')
    sender = parse[item]['sender ']
    password = parse[item]['password']
    recipients= json.loads(parse[item]['recipients'])
    return sender,password,recipients

def get_msg(sender,recipients,subject,mail_body):
    msg = MIMEMultipart()
    msg['Subject'] = subject
    msg['From'] = sender 
    msg['To'] = ', '.join(recipients)       
    text = """\
    """+mail_body+""" """
    part1 = MIMEText(text, "plain")
    msg.attach(part1)
    return msg

def send_email(msg,sender,password,recipients):
    s = smtplib.SMTP('smtp.test.com')
    s.login(sender,password)
    s.sendmail(sender, recipients, msg.as_string())
    s.quit()    

文件main.py

from functions_email import *

sender,password,recipients = get_credenciales('email_2')
subject= 'text to subject'
mail_body = 'body....................'
msg = get_msg(sender,recipients ,subject,mail_body)    
send_email(msg,sender,password,recipients)

此致!

就您的代碼而言,它似乎沒有任何根本性的錯誤,只是不清楚您實際上是如何調用 function 的。 我能想到的是,當您的服務器沒有響應時,您將收到此 SMTPServerDisconnected 錯誤。 如果你在 smtplib 中查找 getreply() function(摘錄如下),你就會明白了。

def getreply(self):
    """Get a reply from the server.

    Returns a tuple consisting of:

      - server response code (e.g. '250', or such, if all goes well)
        Note: returns -1 if it can't read response code.

      - server response string corresponding to response code (multiline
        responses are converted to a single, multiline string).

    Raises SMTPServerDisconnected if end-of-file is reached.
    """

check an example at https://github.com/rreddy80/sendEmails/blob/master/sendEmailAttachments.py that also uses a function call to send an email, if that's what you're trying to do (DRY approach).

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM