Home > other >  Encoding issue when sending an email with SMTP
Encoding issue when sending an email with SMTP

Time:08-15

When sending an email with SMTP, I'm getting encoding issues. I've checked many different SO posts and methods of fixing the issue and nothing is working for me.

Here's my code with all personal information removed:

import requests
import pandas as pd
import smtplib
import datetime


STOCK = "AAPL"
COMPANY = 'Apple'
TODAY = datetime.date.today()
MY_EMAIL = 
PASSWORD = 
RECEIVER = 


API_KEY = 

parameters = {
    'function': 'TIME_SERIES_DAILY',
    'symbol': STOCK,
    'interval': '60min',
    'apikey': API_KEY,
}

response = requests.get(url='https://www.alphavantage.co/query', params=parameters)
response.raise_for_status()

data = response.json()
stock_data = data['Time Series (Daily)']

today = list(stock_data.values())[0]
yesterday = list(stock_data.values())[1]

prices = [float(today['4. close']), float(yesterday['4. close'])]

price_series = pd.Series(prices)
perc_change = price_series.pct_change()[1]


API_KEY2 = 

parameters2 = {
    'apiKey': API_KEY2,
    'q': COMPANY,
    'searchIn': 'title',
    'from': TODAY_DATE,
    'totalResults': 3,
    'language': 'en',
    'sortBy': 'popularity',
    'pageSize': 3,
    'page': 1,
}

response2 = requests.get(url='https://newsapi.org/v2/everything', params=parameters2)
response2.raise_for_status()

data = response2.json()
article_data = data['articles']

articles = [article_data[num] for num in range(3)]


if perc_change > 0.025 or perc_change < 0.025:
    if perc_change >= 0:
        symbol = '▲'
    else:
        symbol = '▼'

    subject = f'{STOCK}: {symbol} {round(perc_change, 3)}'

    message = f'''
    \033[1mAuthor\033[0m: {list(articles[0].values())[0]['name']}
    \033[1mBrief\033[0m: {list(articles[0].values())[3]}\n
    \033[1mAuthor\033[0m: {list(articles[1].values())[0]['name']}
    \033[1mBrief\033[0m: {list(articles[1].values())[3]}\n
    \033[1mAuthor\033[0m: {list(articles[2].values())[0]['name']}
    \033[1mBrief\033[0m: {list(articles[2].values())[3]}
    '''
    
    with smtplib.SMTP('smtp.gmail.com') as connection:
            connection.starttls()
            connection.login(user=MY_EMAIL, password=PASSWORD)
            connection.sendmail(from_addr=MY_EMAIL, to_addrs=RECEIVER, msg=f"Subject:{subject.encode('utf-8')}\n\n{message.encode('utf-8')}")

This has given me the best result so far:

msg=f"Subject:{subject.encode('utf-8')}\n\n{message.encode('utf-8')}"

This is what the email ends up looking like:

Subject: b'AAPL: \xe2\x96\xbc-0.021'

Body: b"\n \x1b[1mHeadline\x1b[0m: The Verge\n \x1b[1mBrief\x1b[0m: Samsung just announced the Galaxy Watch 5 Pro for outdoor athletes and Apple is also expected to release a rugged version of the Apple Watch this fall. But smartwatches can\xe2\x80\x99t rely too heavily on touchscreens to win over athletes.\n\n \x1b[1mHeadline\x1b[0m: Ars Technica\n \x1b[1mBrief\x1b[0m: Dealmaster also has the Xbox Series S, Apple Watch Series 7, and Nvidia Shield TV.\n\n \x1b[1mHeadline\x1b[0m: Boing Boing\n \x1b[1mBrief\x1b[0m: One Strange Thing is a podcast that investigates unexplainable news stories. I began listening to it last night on Apple Podcasts, and ended up playing 5 episodes. You can listen to a selection of free episodes on the podcast's website, too. \xe2\x80\x94 Read the rest\n " 

What am I doing wrong? I've tried using EmailMessage, Header, MIMEText, etc. I've also tried sending to multiple different email providers as well just in case and that didn't change anything.

CodePudding user response:

You need to encode the entire message body, rather than encoding parts and then inserting them into a string.

For example, this code (using only some of the data):

 with smtplib.SMTP('localhost', 1025) as connection:
      connection.sendmail(
          from_addr=MY_EMAIL,
          to_addrs=RECEIVER,
          msg=f"Subject:{subject}\n\n{message}".encode('utf-8'),
      ) 

Produces this output in the aiosmtpd debug server:

---------- MESSAGE FOLLOWS ----------
Subject:APPL: ▲ 1.234
X-Peer: ('::1', 40952, 0, 0)


Author: The Verge
Brief: Samsung just announced the Galaxy Watch 5 Pro for outdoor athletes and Apple is also expected to release a rugged version of the Apple Watch this fall. But smartwatches can’t rely too heavily on touchscreens to win over athletes.

Author: Ars Technica
Brief: Dealmaster also has the Xbox Series S, Apple Watch Series 7, and Nvidia Shield TV.


------------ END MESSAGE ------------

In my terminal "Author" and "brief" are in bold, since the output is in a terminal and your message includes ANSI escapes. But are your emails going to be read in a terminal? If you want to use formatting like bold in an email it's more usual to send an HTML email, like this:

from email.message import EmailMessage
import smtplib

msg = EmailMessage()
msg['Subject'] = subject
msg['From'] = MY_EMAIL
msg['To'] = RECEIVER

# Create a plain text message (no formatting)
msg.set_content(
    f"""\
Author: {name1}
Brief: {brief1}\n
Author: {name2}
Brief: {brief2}\n
"""
)

# Create an HTML message.
msg.add_alternative(
    f"""\
<html>
  <head></head>
  <body>
    <p><b>Author</b>: {name1}</p>
    <p><b>Brief</b>: {brief1}</p>
    <p><b>Author</b>: {name2}</p>
    <p><b>Brief</b>: {brief2}</p>
  </body>
</html>
""",
    subtype='html',
)


with smtplib.SMTP('localhost', 1025) as s:
    s.send_message(msg)

Which generates this message:

---------- MESSAGE FOLLOWS ----------
Subject: APPL: =?utf-8?b?4pay?= 1.234
From: [email protected]
To: [email protected]
MIME-Version: 1.0
Content-Type: multipart/alternative;
 boundary="===============6181735321118528857=="
X-Peer: ('::1', 51216, 0, 0)

--===============6181735321118528857==
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable

Author: The Verge
Brief: Samsung just announced the Galaxy Watch 5 Pro for outdoor athletes and=
 Apple is also expected to release a rugged version of the Apple Watch this f=
all. But smartwatches can=E2=80=99t rely too heavily on touchscreens to win o=
ver athletes.

Author: Ars Technica
Brief: Dealmaster also has the Xbox Series S, Apple Watch Series 7, and Nvidi=
a Shield TV.


--===============6181735321118528857==
Content-Type: text/html; charset="utf-8"
Content-Transfer-Encoding: quoted-printable
MIME-Version: 1.0

<html>
  <head></head>
  <body>
    <p><b>Author</b>: The Verge</p>
    <p><b>Brief</b>: Samsung just announced the Galaxy Watch 5 Pro for outdoo=
r athletes and Apple is also expected to release a rugged version of the Appl=
e Watch this fall. But smartwatches can=E2=80=99t rely too heavily on touchsc=
reens to win over athletes.</p>
    <p><b>Author</b>: Ars Technica</p>
    <p><b>Brief</b>: Dealmaster also has the Xbox Series S, Apple Watch Serie=
s 7, and Nvidia Shield TV.</p>
  </body>
</html>

--===============6181735321118528857==--
------------ END MESSAGE ------------
  • Related