Pythonでメールを送信する方法は?


169

このコードは機能し、問題なくメールを送信します。

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()

ただし、次のような関数でラップしようとすると、

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

誰かが私に理由を理解するのを手伝ってくれる?


2
どのように関数を呼び出しますか?
Jochen Ritzel、2011年

投稿したインデントはファイルと同じですか?
gddc

@gddc何私はそれを貼り付けただけの方法であること、適切にインデントしてください行われません。
cloud311

この関数をメインモジュールにインポートし、定義したパラメーターを渡して関数を呼び出します。
cloud311 2011年

4
@Arrietaが提案する電子メールパッケージの使用がこれを解決する最善の方法ですが、あなたのアプローチは機能します。2つのバージョンの違いは文字列にあります:(1)@NickODellが指摘するように、関数バージョンには先行空白があります。ヘッダーには、先頭のスペースがあってはなりません(ラップされていない場合)。(2)TEXTに先頭の空白行が含まれていない限り、ヘッダーと本文の間の区切り文字が失われています。
Tony Meyer

回答:


191

標準パッケージemailsmtplib一緒にメールを送信することをお勧めします。次の例(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()

複数の宛先に電子メールを送信する場合は、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()

ご覧のとおりToMIMETextオブジェクトのヘッダーは、コンマで区切られた電子メールアドレスで構成される文字列である必要があります。一方、sendmail関数の2番目の引数は文字列のリストでなければなりません(各文字列は電子メールアドレスです)。

あなたは3つの電子メールアドレスを持っているのであれば、: 、person1@example.comperson2@example.comそしてperson3@example.com、あなたが行うことができますように(明白なセクションは省略)次のとおりです。

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のどこかに行きたいならそれは必需品です-ドキュメントは標準ライブラリにとってほとんど優れています。


1
これは関数内から非常にうまく機能しますが、複数の受信者に送信するにはどうすればよいですか?msg [to]は辞書のキーのように見えるため、msg [to]をセミコロンで区切ってみましたが、機能しないようです。
cloud311 2011年

1
@ cloud311は、コードに記述されているとおりです。これは、カンマで区切られた文字列を望んでいる: ", ".join(["a@example.com", "b@example.net"])
ティム・マクナマラ

3
To:ヘッダーは、エンベロープ受信者とは意味が異なることにも注意してください。たとえば、「:Tony Meyer」<tony.meyer@gmail.com>」をTo:ヘッダーのアドレスとして使用できますが、エンベロープ受信者は「tony.meyer@gmail.com」のみにする必要があります。「素敵な」To:アドレスを作成するには、email.utils.formataddr( "Tony Meyer"、 "tony.meyer@gmail.com")のようなemail.utils.formataddrを使用します。
Tony Meyer

1
小さな改善:ファイルを使用してオープンする必要がありますwithwith open(textfile, 'rb') as fp:with内部でエラーが発生した場合でもブロックがファイルを閉じるため、明示的なクローズを削除できます。
jpmc26 2014

1
この回答に固有ではありませんが、制御できないSMTPサーバーに接続する場合は、到達不能で低速で接続を拒否するなどの可能性を考慮する必要があります。コード的には例外が発生しますが、後で送信を再試行する方法を見つける必要があります。独自のsendmail / postfixと話すと、再送信が行われます。
ラルフボルトン

66

さて、あなたは最新で現代的な答えを求めています。

これが私の答えです:

Pythonでメールを送信する必要がある場合は、メールガン API を使用します。これにより、メールの送信を整理することができます。彼らは無料で毎月10,000通のメールを送信できる素晴らしいapp / apiを持っています。

メールの送信は次のようになります。

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!"})

また、イベントなどを追跡することもできます。クイックスタートガイドをご覧ください。

これがお役に立てば幸いです。


3
これは確かにはるかに「この日付の」ものです。外部APIを使用していますが。個人的には、内部で維持するためにyagmailのようなものを使用します。
PascalVKooten

6
@PascalvKooten yagmailの絶え間ない広告をフォローすることは絶対におもしろい(そうです、サー、私は次の機会に検討します、サー;)。しかし、私はほとんど誰もOPの問題を気にしていないようで非常に混乱しているが、むしろ多くの異なる解決策を提案している。それはまるで2009年に電球をスマートに交換する方法を尋ねているようなもので、答えは次のとおりです。本物のメルセデスを購入する...
フラシュビエ

私の使命には、いくつかの面白さがあります。
PascalVKooten 2016

4
@flaschbier OPの問題について誰も気にしない理由は、タイトルが間違っているためです。「Pythonでメールを送信する方法?」人々がその質問をクリックすると見に来る実際の理由であり、yagmailが提供できる答えを期待しています。どうぞ。もっとyagmailの広告。
PascalVKooten

1
Mailgunの顧客にとって、Webサービスによるメールの送信は、SMTPを介するよりも(特に添付ファイルを使用する場合)よりも帯域幅に優しいと言えます。
ラルフボルトン

44

yagmailパッケージに助言して、メールの送信を手伝いたいと思います(私はメンテナーです。広告を申し訳ありませんが、本当に役立つと思います!)。

あなたのための全体のコードは次のようになります:

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

すべての引数にデフォルトを提供していることに注意してください。たとえば、自分に送信し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


4
これが一番の解決策です。
Richard de Ree

1
OMG、それはスイートでシンプルなメールモジュールです。ありがとう!
pepoluan 2017年

1
yagmailgmail以外でも使えますか?自分のSMTPサーバーに使用しようとしています。
Anirban Nag 'tintinmj' 2017年

@dtgqご心配いただきありがとうございます。個人的には、攻撃経路はわかりません。誰かが送信したいパスの下にあるファイルを変更する場合、Attachmentクラスがあるかどうかは関係ありません。それはまだ同じことです。彼らがあなたのコードを変更することができるなら、彼らはとにかく彼らがやりたいことを何でもすることができます(ルートの有無にかかわらず、それは電子メール送信と同じです)。これは典型的な「便利で魔法のように安全性が低いはず」のように思えます。あなたが目にする本当の脅威は何ですか?
PascalVKooten 2017年

14

インデントの問題があります。以下のコードは動作します:

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()


3
@geotheory SERVER関数に渡される変数はユーザー資格情報です。
ユーザー

単純な文字列補間を使用して有効なSMTPメッセージを手動でつなぎ合わせるのはお勧めできません。回答には、これを完全に示すバグがあります。(これが有効なメッセージであるためには、本文が空行で始まる必要があります。)1980年代のプレーンテキスト以外の文字セットを含むすべての場合、7ビットの英語のみのUS-ASCII(添付ファイル、国際化、およびその他のMIMEサポート)本当にこれを扱うライブラリを使いたいです。Python emailライブラリは(特に3.6以降)これを適切に実行しますが、それでもあなたがやっていることをある程度理解する必要があります。
トリプルリー

7

ここでは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.')

この関数を呼び出します:

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

以下は中国のユーザーのみを対象としています。

126/163、ウェブ易邮箱を使用する場合は、以下のように「客户端授权密码」を設定する必要があります。

ここに画像の説明を入力してください

ref:https : //stackoverflow.com/a/41470149/2803344 https://docs.python.org/3/library/email.examples.html#email-examples


4

関数でコードをインデントしている間(問題ありません)、生のメッセージ文字列の行もインデントしました。ただし、先頭の空白は、ヘッダー行の折りたたみ(連結)を意味します。これは、RFC 2822-インターネットメッセージ形式のセクション2.2.3および3.2.3で説明されています。

各ヘッダーフィールドは、論理的には、フィールド名、コロン、およびフィールド本文で構成される1行の文字です。ただし、便宜上、1行あたりの998/78文字の制限に対処するために、ヘッダーフィールドのフィールド本体部分を複数行の表現に分割できます。これは「折りたたみ」と呼ばれます。

sendmail呼び出しの関数形式では、すべての行が空白で始まっているため、「展開」(連結)され、送信しようとしています。

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

これらの名前は行の先頭でのみ認識されるため、私たちの心が示唆する以外にsmtplibTo:およびSubject:ヘッダーは理解できなくなります。代わりsmtplibに、非常に長い送信者の電子メールアドレスを想定します。

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

これは機能しないため、例外が発生します。

解決策は簡単ですmessage。以前と同じように文字列を保持するだけです。これは、関数(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(オプション)に従ってインクルードを関数の外側に配置したことにも注意してください。


3

それはおそらくあなたのメッセージにタブを入れています。sendMailに渡す前にメッセージを印刷します。


1

送信者と受信者の両方が電子メールアカウントで不明なソース(外部ソース)から電子メールを送信および受信する権限を付与していることを確認してください。

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)

ここに画像の説明を入力してください


msgは有効なSMTPメッセージではなく、メールサーバーがそれを受け入れると、etherに表示されなくなるように見えます。
トリプルリー

0

これがどのように機能するかを理解したばかりなので、ここに2つのビットを入れます。

SERVER接続設定でポートが指定されていないようです。デフォルトのポート25を使用していないSMTPサーバーに接続しようとしたときに、少し影響を受けました。

smtplib.SMTPのドキュメントによると、ehloまたはheloリクエスト/レスポンスは自動的に処理されるため、これについて心配する必要はありません(ただし、他のすべてが失敗したかどうかを確認する必要があるかもしれません)。

自問するもう1つのことは、SMTPサーバー自体でのSMTP接続を許可しているかどうかです。GMAILやZOHOなどの一部のサイトでは、実際にアクセスして、メールアカウント内でIMAP接続をアクティブにする必要があります。メールサーバーで、「localhost」からのSMTP接続が許可されていない可能性がありますか?調べるべき何か。

最後に、TLSで接続を開始してみてください。現在、ほとんどのサーバーでこのタイプの認証が必要です。

2つのTOフィールドがメールに詰まっているのがわかります。msg ['TO']およびmsg ['FROM'] msg辞書の項目を使用すると、メールのヘッダーに正しい情報が表示されます。これは、メールの受信側の[宛先] / [送信元]フィールドに表示されます(ここにReply Toフィールドを追加することもできます。TOおよびFROMフィールド自体がサーバーに必要なものです。適切なメールヘッダーがない場合にメールを拒否するメールサーバーがあることは知っています。

これは、関数で使用したコードで、ローカルコンピューターとリモートSMTPサーバー(図のようにZOHO)を使用して* .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()

0

SMTPモジュールはコンテキストマネージャをサポートしているため、手動でquit()を呼び出す必要がないことに注意する必要があります。これにより、例外がある場合でも常に呼び出されることが保証されます。

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

-1

あなたのコードに関する限り、それを除いて根本的に問題はないようですが、実際にその関数をどのように呼び出しているかは不明です。私が考えることができるのは、サーバーが応答していないときに、このSMTPServerDisconnectedエラーが発生することだけです。smtplibでgetreply()関数を検索すると(以下の抜粋)、アイデアがわかります。

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.
    """

https://github.com/rreddy80/sendEmails/blob/master/sendEmailAttachments.pyの例を確認してください。これは、関数呼び出しを使用してメールを送信することです(それが目的の場合)(DRYアプローチ)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.