SESとPythonでメールの送信者が文字化けしないようにする

Pythonを使ってSESでメールを送る際、送信者（From）に日本語を使うと文字化けします。

https://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/send-using-sdk-python.html 

↑の公式サンプルコードをもとにメールを送信してみます。

# 文字化けするパターン

まずは何も考えず、そのまま宛先に日本語をセットしたパターンです。

```python:ses_send.py
import boto3
from botocore.exceptions import ClientError

# Replace sender@example.com with your "From" address.
# This address must be verified with Amazon SES.
SENDER = "テスト <hoge+sender@example.com>"

# Replace recipient@example.com with a "To" address. If your account 
# is still in the sandbox, this address must be verified.
RECIPIENT = "fuga@example.com"

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

# The subject line for the email.
SUBJECT = "Amazon SES テスト (SDK for Python)"

# The email body for recipients with non-HTML email clients.
BODY_TEXT = ("Amazon SES テスト (Python)\r\n"
             "This email was sent with Amazon SES using the "
             "AWS SDK for Python (Boto)."
            )
            
# The HTML body of the email.
BODY_HTML = """<html>
<head></head>
<body>
  <h1>Amazon SES テスト (SDK for Python)</h1>
  <p>This email was sent with
    <a href='https://aws.amazon.com/ses/'>Amazon SES</a> using the
    <a href='https://aws.amazon.com/sdk-for-python/'>
      AWS SDK for Python (Boto)</a>.</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)

# Try to send the email.
try:
    #Provide the contents of the email.
    response = client.send_email(
        Destination={
            'ToAddresses': [
                RECIPIENT,
            ],
        },
        Message={
            'Body': {
                'Text': {
                    'Charset': CHARSET,
                    'Data': BODY_TEXT,
                },
            },
            'Subject': {
                'Charset': CHARSET,
                'Data': SUBJECT,
            },
        },
        Source=SENDER
    )
# 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'])
```

実行します。

```
>python ses_send.py
Email sent! Message ID:
0100016c23fd79b6-efee9a81-212b-4c27-a5b7-81249789bdb0-000000
```

受信したメール。

```
MIME-Version: 1.0
Date: Wed, 24 Jul 2019 21:38:32 +0900
From: =?utf-8?Q?=C6=B9=EF=BF=BD?= <hoge+sender@example.com>
Subject: =?utf-8?Q?Amazon_SES_=E3=83=86=E3=82=B9=E3=83=88_(SDK_for_Python)?=
Thread-Topic:
 =?utf-8?Q?Amazon_SES_=E3=83=86=E3=82=B9=E3=83=88_(SDK_for_Python)?=
To: "fuga@example.com" <fuga@example.com>
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset="utf-8"

Amazon SES =E3=83=86=E3=82=B9=E3=83=88 (Python)
This email was sent with Amazon SES using the AWS SDK for Python (Boto).
```

同じテストという文言がFromの部分だけエンコードが違っています。
これは、`テスト <hoge+sender@example.com>`という文字列全体にマルチバイトを考慮しないエンコードがかかることで引き起こされる現象です。

このためメーラーで開いたときに送信者だけが化けてしまいます。

# 文字化けしないパターン

Fromにセットするときに、送信者とメールアドレスを分けてエンコードがすると文字化けしません。

先ほどのスクリプトに手を加えます。

```python:ses_send.py
import boto3
from botocore.exceptions import ClientError
from email.header import Header # eemail.headerをインポート

# Replace sender@example.com with your "From" address.
# This address must be verified with Amazon SES.

# 送信者の名前とアドレスに変数を分ける
SENDER_NAME = "テスト"
SENDER_ADDR = "hoge+sender@example.com"

# Replace recipient@example.com with a "To" address. If your account 
# is still in the sandbox, this address must be verified.
RECIPIENT = "fuga@example.com"

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

# The subject line for the email.
SUBJECT = "Amazon SES テスト (SDK for Python)"

# The email body for recipients with non-HTML email clients.
BODY_TEXT = ("Amazon SES テスト (Python)\r\n"
             "This email was sent with Amazon SES using the "
             "AWS SDK for Python (Boto)."
            )
            
# The HTML body of the email.
BODY_HTML = """<html>
<head></head>
<body>
  <h1>Amazon SES テスト (SDK for Python)</h1>
  <p>This email was sent with
    <a href='https://aws.amazon.com/ses/'>Amazon SES</a> using the
    <a href='https://aws.amazon.com/sdk-for-python/'>
      AWS SDK for Python (Boto)</a>.</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)

# Try to send the email.
try:
    #Provide the contents of the email.
    response = client.send_email(
        Destination={
            'ToAddresses': [
                RECIPIENT,
            ],
        },
        Message={
            'Body': {
                'Text': {
                    'Charset': CHARSET,
                    'Data': BODY_TEXT,
                },
            },
            'Subject': {
                'Charset': CHARSET,
                'Data': SUBJECT,
            },
        },
        # Header関数を使いISO-2022-JPにエンコード
        Source='%s <%s>'%(Header(SENDER_NAME.encode('iso-2022-jp'),'iso-2022-jp').encode(),SENDER_ADDR)
    )
# 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'])
```

これで実行します。

```
>python ses_send.py
Email sent! Message ID:
0100016c241237ba-ca70ada5-4add-4641-8f97-dfaf904f8de7-000000
```

受信したメール。

```
MIME-Version: 1.0
Date: Wed, 24 Jul 2019 22:01:11 +0900
From: =?utf-8?Q?=E3=83=86=E3=82=B9=E3=83=88?= <hoge+sender@example.com>
Subject: =?utf-8?Q?Amazon_SES_=E3=83=86=E3=82=B9=E3=83=88_(SDK_for_Python)?=
Thread-Topic:
 =?utf-8?Q?Amazon_SES_=E3=83=86=E3=82=B9=E3=83=88_(SDK_for_Python)?=
To: "fuga@example.com" <fuga@example.com>
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset="utf-8"

Amazon SES =E3=83=86=E3=82=B9=E3=83=88 (Python)
This email was sent with Amazon SES using the AWS SDK for Python (Boto).
```

先ほどとFromのエンコードが変わり、メーラでも日本語が化けずに表示されます。