web-dev-qa-db-ja.com

boto3 send_emailまたはsend_raw_emailを使用してHTMLテキストと添付ファイルを送信する方法

boto SES send_emailクライアントを使用して画像の添付ファイルを送信するにはどうすればよいですか?

send_raw_emailを使用して添付ファイルを送信できることはわかっていますが、html dataを使用してメッセージ本文を送信する必要があります。これが不可能な場合、boto3.ses.send_raw_email()を使用してHTMLデータを含むメールを送信するにはどうすればよいですか?

11
DKo

" Amazon SESを使用してHTMLメールを送信する方法 "の恥知らずなコピーの例これは、典型的なメールデータコンテンツの例です。

 message_dict = { 'Data':
  'From: ' + mail_sender + '\n'
  'To: ' + mail_receivers_list + '\n'
  'Subject: ' + mail_subject + '\n'
  'MIME-Version: 1.0\n'
  'Content-Type: text/html;\n\n' +
  mail_content}

boto3.ses.send_raw_email を使用して添付ファイルとHTMLテキストを送信する場合は、上記のメッセージ辞書を使用して渡すだけです。 (htmlテキストをmail_contentの下に置くだけです)

response = client.send_raw_email(
    Destinations=[
    ],
    FromArn='',
    RawMessage=message_dict,
    ReturnPathArn='',
    Source='',
    SourceArn='',
)

実際、生の添付ファイルヘッダーはsend_email()とsend_raw_email()の両方で機能するはずです。 send_mailを除き、Textではなくhtml内に添付ファイルを配置する必要があります。

4
mootmoot

他のSO質問、ブログ、Pythonのドキュメントなど)を含むいくつかのソースを調べた後、以下のコードを思いつきました。

テキストおよび/またはhtmlメールおよび添付ファイルを可能にします。

Boto3の代わりにSMTPクライアントで電子メールを送信するなど、他の目的でMIMEを再利用する場合に備えて、MIMEとboto3の部分を分離しました。

import os
import boto3
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication


def create_multipart_message(
        sender: str, recipients: list, title: str, text: str=None, html: str=None, attachments: list=None)\
        -> MIMEMultipart:
    """
    Creates a MIME multipart message object.
    Uses only the Python `email` standard library.
    Emails, both sender and recipients, can be just the email string or have the format 'The Name <[email protected]>'.

    :param sender: The sender.
    :param recipients: List of recipients. Needs to be a list, even if only one recipient.
    :param title: The title of the email.
    :param text: The text version of the email body (optional).
    :param html: The html version of the email body (optional).
    :param attachments: List of files to attach in the email.
    :return: A `MIMEMultipart` to be used to send the email.
    """
    multipart_content_subtype = 'alternative' if text and html else 'mixed'
    msg = MIMEMultipart(multipart_content_subtype)
    msg['Subject'] = title
    msg['From'] = sender
    msg['To'] = ', '.join(recipients)

    # Record the MIME types of both parts - text/plain and text/html.
    # According to RFC 2046, the last part of a multipart message, in this case the HTML message, is best and preferred.
    if text:
        part = MIMEText(text, 'plain')
        msg.attach(part)
    if html:
        part = MIMEText(html, 'html')
        msg.attach(part)

    # Add attachments
    for attachment in attachments or []:
        with open(attachment, 'rb') as f:
            part = MIMEApplication(f.read())
            part.add_header('Content-Disposition', 'attachment', filename=os.path.basename(attachment))
            msg.attach(part)

    return msg


def send_mail(
        sender: str, recipients: list, title: str, text: str=None, html: str=None, attachments: list=None) -> dict:
    """
    Send email to recipients. Sends one mail to all recipients.
    The sender needs to be a verified email in SES.
    """
    msg = create_multipart_message(sender, recipients, title, text, html, attachments)
    ses_client = boto3.client('ses')  # Use your settings here
    return ses_client.send_raw_email(
        Source=sender,
        Destinations=recipients,
        RawMessage={'Data': msg.as_string()}
    )


if __name__ == '__main__':
    sender_ = 'The Sender <[email protected]>'
    recipients_ = ['Recipient One <[email protected]>', '[email protected]']
    title_ = 'Email title here'
    text_ = 'The text version\nwith multiple lines.'
    body_ = """<html><head></head><body><h1>A header 1</h1><br>Some text."""
    attachments_ = ['/path/to/file1/filename1.txt', '/path/to/file2/filename2.txt']

    response_ = send_mail(sender_, recipients_, title_, text_, body_, attachments_)
    print(response_)
13
Joao Coelho

これは私が添付ファイルを送信するのに役立ちました:

from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

import boto.ses


AWS_ACCESS_KEY = 'HEREYOURACCESSKEY'
AWS_SECRET_KEY = 'HEREYOURSECRETKEY'

class Email(object):

    def __init__(self, to, subject):
        self.to = to
        self.subject = subject
        self.text = None
        self.attachment = None


    def text(self, text):
        self.text = text

    def add_attachment(self, attachment):
        self.attachment = attachment

    def send(self, from_addr=None, file_name = None):

        connection = boto.ses.connect_to_region(
            'us-east-1',
            aws_access_key_id=AWS_ACCESS_KEY,
            aws_secret_access_key=AWS_SECRET_KEY
        )
        msg = MIMEMultipart()
        msg['Subject'] = self.subject
        msg['From'] = from_addr
        msg['To'] = self.to

        part = MIMEApplication(self.attachment)
        part.add_header('Content-Disposition', 'attachment', filename=file_name)
        part.add_header('Content-Type', 'application/vnd.ms-Excel; charset=UTF-8')

        msg.attach(part)

        # the message body
        part = MIMEText(self.text)
        msg.attach(part)

        return connection.send_raw_email(msg.as_string(),source=from_addr,destinations=self.to)

if __name__ == "__main__":
    email = Email(to='[email protected]', subject='Your subject!')
    email.text('This is a text body.')
    #you could use StringIO.StringIO() to get the file value
    email.add_attachment(yourFileValue)
    email.send(from_addr='[email protected]',file_name="yourFile.txt")
3
Ezequiel Salas

@adklの答えを拡張するために、Amazonの独自の例では、古いPython=メールと添付ファイルの処理方法を使用しています。これに問題はありません。これらのモジュールに関する現在のドキュメントは包括的ではなく、私のような新しいユーザー。

CSVが添付されたメッセージを作成する簡単な例を次に示します。

_from email.message import EmailMessage


def create_email_message(sender: str, recipients: list, title: str, text: str,
                         attachment: BytesIO, file_name: str) -> EmailMessage:
    msg = EmailMessage()
    msg["Subject"] = title
    msg['From'] = sender
    msg['To'] = ', '.join(recipients)
    msg.set_content(text)
    data = attachment.read()
    msg.add_attachment(
        data,
        maintype="text",
        subtype="csv",
        filename=file_name
    )
    return msg

# Client init, attachment file creation here

message = create_email_message(...)
try:
    ses.send_raw_email(
        Source=sender,
        Destinations=recipients,
        RawMessage={'Data': message.as_string()}
    )
except ClientError as e:
    logger.exception(f"Cannot send email report to {recipients}: {e}")
else:
    logger.info("Sent report successfully")
_

この例では、BytesIOオブジェクトを添付ファイルのソースとして使用していますが、read()メソッドをサポートするファイルのようなオブジェクトを使用できます。

3
Oleksii Donoha

2019年3月

[〜#〜] updated [〜#〜]公式ドキュメント( https:// docs .aws.Amazon.com/ses/latest/DeveloperGuide/send-email-raw.html ):

import os
import boto3
from botocore.exceptions import ClientError
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication

# Replace [email protected] with your "From" address.
# This address must be verified with Amazon SES.
SENDER = "Sender Name <[email protected]>"

# Replace [email protected] with a "To" address. If your account 
# is still in the sandbox, this address must be verified.
RECIPIENT = "[email protected]"

# Specify a configuration set. If you do not want to use a configuration
# set, comment the following variable, and the 
# ConfigurationSetName=CONFIGURATION_SET argument below.
CONFIGURATION_SET = "ConfigSet"

# If necessary, replace us-west-2 with the AWS Region you're using for Amazon SES.
AWS_REGION = "us-west-2"

# The subject line for the email.
SUBJECT = "Customer service contact info"

# The full path to the file that will be attached to the email.
ATTACHMENT = "path/to/customers-to-contact.xlsx"

# The email body for recipients with non-HTML email clients.
BODY_TEXT = "Hello,\r\nPlease see the attached file for a list of customers to contact."

# The HTML body of the email.
BODY_HTML = """\
<html>
<head></head>
<body>
<h1>Hello!</h1>
<p>Please see the attached file for a list of customers to contact.</p>
</body>
</html>
"""

# The character encoding for the email.
CHARSET = "utf-8"

# Create a new SES resource and specify a region.
client = boto3.client('ses',region_name=AWS_REGION)

# Create a multipart/mixed parent container.
msg = MIMEMultipart('mixed')
# Add subject, from and to lines.
msg['Subject'] = SUBJECT 
msg['From'] = SENDER 
msg['To'] = RECIPIENT

# Create a multipart/alternative child container.
msg_body = MIMEMultipart('alternative')

# Encode the text and HTML content and set the character encoding. This step is
# necessary if you're sending a message with characters outside the ASCII range.
textpart = MIMEText(BODY_TEXT.encode(CHARSET), 'plain', CHARSET)
htmlpart = MIMEText(BODY_HTML.encode(CHARSET), 'html', CHARSET)

# Add the text and HTML parts to the child container.
msg_body.attach(textpart)
msg_body.attach(htmlpart)

# Define the attachment part and encode it using MIMEApplication.
att = MIMEApplication(open(ATTACHMENT, 'rb').read())

# Add a header to tell the email client to treat this part as an attachment,
# and to give the attachment a name.
att.add_header('Content-Disposition','attachment',filename=os.path.basename(ATTACHMENT))

# Attach the multipart/alternative child container to the multipart/mixed
# parent container.
msg.attach(msg_body)

# Add the attachment to the parent container.
msg.attach(att)
#print(msg)
try:
    #Provide the contents of the email.
    response = client.send_raw_email(
        Source=SENDER,
        Destinations=[
            RECIPIENT
        ],
        RawMessage={
            'Data':msg.as_string(),
        },
        ConfigurationSetName=CONFIGURATION_SET
    )
# Display an error if something goes wrong. 
except ClientError as e:
    print(e.response['Error']['Message'])
else:
    print("Email sent! Message ID:"),
    print(response['MessageId'])
2
adkl

これは、pythonバージョン2.7.xを使用して実装することもできます。

以下はこれのための作業コードです-

[注-追加する「送信者」と「受信者」は、AWS SESで確認する必要があります。 OR SESは「サンドボックス」状態から「本番」状態に移行する必要があります

import os
import boto3
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication


def create_multipart_message(email_metadata):        
    sender = email_metadata['sender_']
    recipients = email_metadata['recipients_']
    title = email_metadata['title_']
    text = email_metadata['text_']
    html = email_metadata['body_']
    attachments = email_metadata['attachments_']

    multipart_content_subtype = 'alternative' if text and html else 'mixed'
    msg = MIMEMultipart(multipart_content_subtype)
    msg['Subject'] = title
    msg['From'] = sender
    msg['To'] = ', '.join(recipients)

    # Record the MIME types of both parts - text/plain and text/html.
    # According to RFC 2046, the last part of a multipart message, in this case the HTML message, is best and preferred.
    if text:
        part = MIMEText(text, 'plain')
        msg.attach(part)
    if html:
        part = MIMEText(html, 'html')
        msg.attach(part)

    # Add attachments
    for attachment in attachments or []:
        with open(attachment, 'rb') as f:
            part = MIMEApplication(f.read())
            part.add_header('Content-Disposition', 'attachment', filename=os.path.basename(attachment))
            msg.attach(part)

    return msg


def send_mail(email_metadata):
        #sender: str, recipients: list, title: str, text: str=None, html: str=None, attachments: list=None) -> dict:
    """
    Send email to recipients. Sends one mail to all recipients.
    The sender needs to be a verified email in SES.
    """
    msg = create_multipart_message(email_metadata)
    ses_client = boto3.client('ses')  # Use your settings here
    return ses_client.send_raw_email(
        Source=email_metadata['sender_'],
        Destinations=email_metadata['recipients_'],
        RawMessage={'Data': msg.as_string()}
    )


if __name__ == '__main__':
    email_metadata = {
    "sender_" : "The Sender <[email protected]>",
    "recipients_" : ['Recipient One <[email protected]>','Recipient two <[email protected]>'],
    "title_" : "Email title here",
    "text_" : "The text version\nwith multiple lines.",
    "body_" : "<html><head></head><body><h1>A header 1</h1><br>Some text.",
    "attachments_" : ['/path/to/file1/filename1.txt','/path/to/file2/filename2.txt']
    }

    response_ = send_mail(email_metadata)
    print(response_)
0
Aayush Dalvi

これが私が最終的に使用したクラスです。 Lambdaファイルにドロップして使用します。

添付ファイルのファイル名のリストを受け入れます。 HTMLメールを送信します。 \n<br />の変更

以下のクラス[1]をemailer.pyとして保存し、次のように使用します。

from emailer import Emailer

def lambda_handler(event, context):
  # ...
  emailer = Emailer()
  emailer.send(
    to=email_recipients_list, 
    subject=subject_string, 
    fromx=from_address_string, 
    body=email_body_string, 
    attachments=attachments_list
  )

[1]

import boto3
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart


class Emailer(object):
    """ send email with attachments """

    def send(self, to, subject, fromx, body=None, content=None, attachments=[]):
        """ sends email with attachments

        Parameters:
            * to (list or comma separated string of addresses): recipient(s) address
            * fromx (string): from address of email
            * body (string, optional): Body of email ('\n' are converted to '< br/>')
            * content (string, optional): Body of email specified as filename
            * attachments (list, optional): list of paths of files to attach
        """

        self.to = to
        self.subject = subject
        self.fromx = fromx
        self.attachment = None
        self.body = body
        self.content = content
        self.attachments = attachments

        if type(self.to) is list:
            self.to = ",".join(self.to)


        message = MIMEMultipart()

        message['Subject'] = self.subject
        message['From'] = self.fromx
        message['To'] = self.to
        if self.content and os.path.isfile(self.content):
            part = MIMEText(open(str(self.content)).read().replace("\n", "<br />"), "html")
            message.attach(part)
        Elif self.body:
            part = MIMEText(self.body.replace("\\n", "<br />").replace("\n", "<br />"), "html")
            message.attach(part)


        for attachment in self.attachments:
            part = MIMEApplication(open(attachment, 'rb').read())
            part.add_header('Content-Disposition', 'attachment', filename=attachment.split("/")[-1])
            message.attach(part)

        ses = boto3.client('ses', region_name='us-east-1')

        response = ses.send_raw_email(
            Source=message['From'],
            Destinations=message['To'].split(","),
            RawMessage={
                'Data': message.as_string()
            }
        )
0
arod