web-dev-qa-db-ja.com

(Python)コードから実際にメールを送信する適切な方法は何ですか?

免責事項:この質問の幅広い性質のため、タイトルをためらいました(以下をご覧ください;-)、他のオプションが含まれています:

  • Pythonコードのみを使用して、localhostからメールを送信する方法
  • 外部SMTPサーバーを使用せずにPythonコードからメールを送信する方法
  • LocalhostとPythonのみを使用して、宛先に直接メールを送信することはできますか?

まず、コンテキストの少し
学習のために、ユーザー登録機能を備えたWebサイトを構築しています。登録後、ユーザーはアクティベーションリンクが記載されたメールを受け取ります。私はPythonコードからメールを作成して送信したいと思います。これは、いくつかの説明を求めたい部分です。


私が始める前の理解(明らかにナイーブ=)は次のように説明できます(正当なメールアドレスがある場合[email protected]):

My naive understanding

例を検索した後、stackoverflow( 12 、、 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()
_

次の(明白な)質問は、_'smtp.server.address:587'_ではなくsmtplib.SMTP()に何を渡すかでした。 この回答 へのコメントから、ローカル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 について話していました。 Python :-)同様の機能を備えた現在のソリューションがいくつかあるはずだと思いました。それで私は検索を始めました。私の希望は、(stackoverflowまたは他の場所で)かなり短いコードスニペットを見つけることでした。そのようなコードスニペットは見つかりませんでしたが、 (Python)Send Email without Mail Server というタイトルのスニペットに遭遇しました(これは chilkat APIを使用しています)。 (おそらく)そこにあり、コードへのコメントでは、最初の行が明確に述べられていました:

メールサーバーに接続せずにメールを送信することは本当に可能ですか?あんまり。

以下の数行:

メールサーバーを必要としないと主張する他のコンポーネントの内部での処理は次のとおりです。コンポーネントは、目的の受信者のメールアドレスを使用してDNS MXルックアップを実行し、そのドメインのメールサーバー(SMTPサーバー)を見つけます。次に、そのサーバーに接続してメールを配信します。あなたはまだあなたのサーバーではなく、SMTPサーバーに接続しています。

それを読んで理解させられました—私は明らかに、プロセスの理解(上記の画像に反映されています)に詳細が欠けていました。これを修正するために RFC on SMTP 全体を読みました。


RFCを読んだ後、プロセスの私の理解の向上は、次のように示されるかもしれません: My improved understanding


この理解から、実際の質問が明らかになりました:

  1. 理解の向上」は、全体像として正しいと見なすことができますか?
  2. MXルックアップによって正確にどのアドレスが返されますか?

    • _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.
      _
    • しかし、これらはどれも 公式ドキュメント で言及されていません(存在するもの:_smtp-relay.gmail.com_、_smtp.gmail.com_、_aspmx.l.google.com_)
  3. 確立されたメールサービス(たとえばgmail)のSMTPサーバーにメールを渡すために、認証は常に必要ですか?

    • メールの送信に_smtp.gmail.com_を使用するには、受信者が_@gmail_アドレスを持っているかどうかに関係なく( docs に記載されているように)必要であることを理解しています:

      認証には完全なGmailまたはG Suiteメールアドレスが必要です。

    • ただし、_[email protected]_へのメールがGmailが所有していないSMTPサーバーに送信されると、Gmailサーバーの1つにリダイレクトされます(直接またはゲートウェイ/リレー経由で)。この場合(私は)メールの送信者はメール送信時に認証するだけでよいので、その後、Gmailサーバーは認証なしでメールを受け入れますか?

      • はいの場合、そのようなゲートウェイ/リレーであるふりをして、指定されたSMTPに直接電子メールを渡すのを妨げているのは何ですか?次に、MXルックアップを介して適切なサーバーを検索し、それに直接メールを送信する「プロキシSMTP」を作成するのもかなり簡単です。
  4. Gmail SMTPのドキュメント 、ただし、認証を必要としない_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_への通信がコードで正しく処理されていることだけです。

17
user8554766

これらの回答と私の追加の質問に感謝します: 12 、、および他の人々のこれら2つの質問(および回答) : onetwo —これで、投稿した質問に自分で回答する準備ができたと思います。


質問を1つずつ取り上げます。

  1. はい、全体像として、メールの送信は次のように表現できます: My improved understanding

  2. MXルックアップは、指定されたドメイン宛ての電子メールを受信するサーバーのアドレスを返します。

    • smtp-relay.gmail.comsmtp.gmail.comaspmx.l.google.comHost -t mx gmail.comコマンドで返されない理由」については、この点については、ほぼ、この質問の 別の回答 で説明しています。ここで把握しておくべき主なポイントは次のとおりです。
      • mXルックアップによって返されたサーバーは、ドメイン(この特定のケースではgmail)の電子メールの受信を担当します
      • gmail docs にリストされているサーバーは、メールsending(つまり、Gmailユーザーが他のGmailユーザーに送信したいメール)を対象としていますまたはそうでなければ、それらのサーバーに送信されます)
  3. メールを受信するサーバー(MXルックアップによって返されるサーバーなど)の場合、認証は必要ありません。

    • このようなサーバーが悪用されるのを防ぐには、いくつかの点があります。
      • 多くのISPは、このような "direct"メール送信を防ぐために、ポート25(メール受信サーバーのデフォルトポート)への送信接続をブロックします。
      • 受信サーバー側では、主にスパムの防止を目的とした多数の対策が講じられていますが、結果として、このような「直接」のメール送信が防止される可能性があります。同様に(いくつかの例は次のとおりです: [〜#〜] dnsbl [〜#〜] —ブロックされたIPのリスト、 [〜#〜] dkim [〜#〜] —は、メール内の偽造された送信者アドレスを検出するように設計されたメール認証方法です(独自の正当なメールサーバーがない場合、Fromフィールドに他の誰かのドメインを使用します。 DKIM)
  4. コードスニペットはOKです。 ISP側のブロッキングが原因で、エラーが発生する可能性があります。


それがすべて言われて、コードスニペット:

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

smtp.server.address:25が正当なサーバーであり、ISPやsmtp.server.addressがブロックされていない場合、実際にメールを送信します(実際の例については この質問 を参照)。側。

3
Filipp W.

メールがどのように機能するかについてのあなたの理解はおおよそ正しいです。物事を明確にするかもしれないいくつかの追加のメモ:

  • SMTPは2つの異なる目的で使用されます。あなたはこれら二つを混同しているようです。

    • 通常は「送信」と呼ばれる最初の用途は、MUA(メールユーザーエージェント、メールプログラム、Outlook、Thunderbirdなど)からMTA(通常は「メールサーバー」と呼ばれるメール転送エージェント)にメールを送信することです。 。 MTAは [〜#〜] isp [〜#〜] によって、またはGMailなどのメールプロバイダーによって実行されます。通常、これらの使用は、IPアドレス(前述のISPの顧客のみが使用できる)またはユーザー名/パスワードのいずれかによって制限されます。

    • 2番目の用途は、あるMTAから別のMTAにメールを送信することです。おそらく誰からのインバウンド・メールも受け入れるつもりなので、この部分は通常、広く開かれています。ここは、スパム対策が講じられている場所でもあります。

メールを送信するには、少なくともSMTPの2番目の部分、つまり別のMTAと通信してメールを配信する機能が必要です。

メールを送信する一般的な方法は、アプリケーションでメールを作成し、MTAメールサーバーに送信して配信することです。設定に応じて、そのMTAは、Pythonコードが(localhost)で実行されている)と同じマシンにインストールするか、より「中央」のメールサーバーにすることができます(おそらく認証が必要です)。 。

"Your" MTAは、次のようなメール配信の厄介な詳細をすべて処理します。

  • DNSルックアップを実行して、メールをリレーするために接続するMTAを見つけます。これにはMXルックアップが含まれますが、 A-records などの他のフォールバックメカニズムも含まれます。

  • 最初の試行が一時的に失敗した場合の配信の再試行

  • メッセージが永続的に失敗した場合のバウンスメッセージの生成

  • 異なるドメインに複数の受信者がいる場合は、メッセージの複数のコピーを作成します

  • [〜#〜] dkim [〜#〜] でメッセージに署名すると、スパムとしてマークされる可能性が低くなります。

  • ...

もちろん、これらすべての機能を独自のPythonコード内に再実装し、MTAをアプリケーションと効果的に組み合わせることができますが、私はそれを強くお勧めします。メールを正しく取得するのは驚くほど難しい...

結論:SMTP経由でプロバイダーのメールサーバーまたは別のメールサービスにメールを送信してみてください。それが不可能な場合:独自のメールサーバーを実行する場合は、十分に検討してください。スパマーとしてマークされることは簡単に起こります。スパムリストから削除されることははるかに困難です。アプリケーションでSMTPコードを再実装しないでください。

5
Niobos