简体   繁体   中英

Send formatted html email via AWS SES with Python and boto3

I'm trying to setup an email function to send emails with formatted data. I have the following which works to send a pandas dataframe in an email, but the formatting used to convert the pandas dataframe into html isn't coming through in the email with the user defined number of spaces.

#install the AWS SES SDK for Python (boto3) from the command line
#pip install boto3

import boto3
import pandas as pd
import numpy as np

df = pd.DataFrame(np.random.randn(10, 4), columns=['A', 'B', 'C', 'D']) * 10000

def email(bodytext = 'No data...check your function arguments', dftoconvert = None):
    region = 'us-west-2'
    user = '' #insert your access key to use when creating the client
    pw = '' #insert the secret key to use when creating the client
    client = boto3.client(service_name = 'ses', 
                          region_name = region, 
                          aws_access_key_id = user, 
                          aws_secret_access_key = pw)
    me = 'me@example.com'
    you = ['you@example.com']
    subject = 'testSUBJECT'
    COMMASPACE = ', '
    you = COMMASPACE.join(you)
    #Build email message parts

    #Build and send email
    destination = { 'ToAddresses' : [you],
                    'CcAddresses' : [],
                    'BccAddresses' : []}
    try:
        bodyhtml = dftoconvert.to_html(float_format = lambda x: '({:15,.2f})'.format(abs(x)) if x < 0 else '+{:15,.2f}+'.format(abs(x)))
        message = {'Subject' : {'Data' : subject},
                   'Body': {'Html' : {'Data' : bodyhtml}}}
    except NoneType: #If there is no data to convert to html
        message = {'Subject' : {'Data' : subject},
                   'Body': {'Text' : {'Data' : bodytext}}}
    except Exception as e:
        print(e)
    result = client.send_email(Source = me, 
                               Destination = destination, 
                               Message = message)    
    return result if 'ErrorResponse' in result else ''

print(df)
email(dftoconvert = df)

For some reason, spaces are ignored. I've changed the significant digits identifier to be very small and very large and the html output looks correct with the correct number of leading spaces for the floats in Python. See below.

import pandas as pd
import numpy as np

df = pd.DataFrame(np.random.randn(10, 4), columns=['A', 'B', 'C', 'D']) * 10000


df

              A             B             C             D
0   1071.786803  -4354.776685  -3541.261466   2653.522461
1    489.865060 -12567.822512  13888.890274  14243.027303
2    471.995980   9473.174725  -1776.897694   5085.236174
3   5932.486256 -12558.720083 -17714.593696  -3503.574051
4  -8886.624311 -10286.622739  -4513.326771   2714.793954
5   5965.944055  10207.608141  19224.094501   4748.746867
6  14189.480430 -13826.251008   9847.711830  -1241.976560
7  -9044.406158 -14337.121541  19572.135090 -18146.356528
8   3057.233113 -14410.383480   -931.179975 -16273.711970
9 -14492.047676  -1150.506849  -1892.032700   -797.596310

df.to_html(float_format = lambda x: '({:15,.2f})'.format(abs(x)) if x < 0 else '+{:15,.2f}+'.format(abs(x)))

Out[3]: '<table border="1" class="dataframe">\\n <thead>\\n <tr style="text-align: right;">\\n <th></th>\\n <th>A</th>\\n <th>B</th>\\n <th>C</th>\\n <th>D</th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <th>0</th>\\n <td>+ 1,071.79+</td>\\n <td>( 4,354.78)</td>\\n <td>( 3,541.26)</td>\\n <td>+ 2,653.52+</td>\\n </tr>\\n <tr>\\n <th>1</th>\\n <td>+ 489.87+</td>\\n <td>( 12,567.82)</td>\\n <td>+ 13,888.89+</td>\\n <td>+ 14,243.03+</td>\\n </tr>\\n <tr>\\n <th>2</th>\\n <td>+ 472.00+</td>\\n <td>+ 9,473.17+</td>\\n <td>( 1,776.90)</td>\\n <td>+ 5,085.24+</td>\\n </tr>\\n <tr>\\n <th>3</th>\\n <td>+ 5,932.49+</td>\\n <td>( 12,558.72)</td>\\n <td>( 17,714.59)</td>\\n <td>( 3,503.57)</td>\\n </tr>\\n <tr>\\n <th>4</th>\\n <td>( 8,886.62)</td>\\n <td>( 10,286.62)</td>\\n <td>( 4,513.33)</td>\\n <td>+ 2,714.79+</td>\\n </tr>\\n <tr>\\n <th>5</th>\\n <td>+ 5,965.94+</td>\\n <td>+ 10,207.61+</td>\\n <td>+ 19,224.09+</td>\\n <td>+ 4,748.75+</td>\\n </tr>\\n <tr>\\n <th>6</th>\\n <td>+ 14,189.48+</td>\\n <td>( 13,826.25)</td>\\n <td>+ 9,847.71+</td>\\n <td>( 1,241.98)</td>\\n </tr>\\n <tr>\\n <th>7</th>\\n <td>( 9,044.41)</td>\\n <td>( 14,337.12)</td>\\n <td>+ 19,572.14+</td>\\n <td>( 18,146.36)</td>\\n </tr>\\n <tr>\\n <th>8</th>\\n <td>+ 3,057.23+</td>\\n <td>( 14,410.38)</td>\\n <td>( 931.18)</td>\\n <td>( 16,273.71)</td>\\n </tr>\\n <tr>\\n <th>9</th>\\n <td>( 14,492.05)</td>\\n <td>( 1,150.51)</td>\\n <td>( 1,892.03)</td>\\n <td>( 797.60)</td>\\n </tr>\\n </tbody>\\n</table>'

but the email seems to ignore any leading spaces and displays the html the same way no matter how many leading spaces are. I can't figure out how to make the html formatting display in the email the same way the html appears in the Python output (with the leading spaces). Is there a better way? Is there a better format than html to email formatted dataframes?

  • Python 3.5.2
  • Pandas 0.18.1
  • Numpy 1.11.1

I assume you want to use leading spaces in table cells to format the table in the email. This won't work because the blanks will be merged.

You can replace two spaces with no-break space and it will make the mail look a bit different, not ideal though.

def email(bodytext = 'No data...check your function arguments', dftoconvert = None, replace=False):
    region = 'us-west-2'
    user = '' #insert your access key to use when creating the client
    pw = '' #insert the secret key to use when creating the client
    client = boto3.client(service_name = 'ses', 
                          region_name = region, 
                          aws_access_key_id = user, 
                          aws_secret_access_key = pw)
    me = 'me@example.com'
    you = ['you@example.com']
    subject = 'testSUBJECT'
    COMMASPACE = ', '
    you = COMMASPACE.join(you)
    #Build email message parts

    #Build and send email
    destination = { 'ToAddresses' : [you],
                    'CcAddresses' : [],
                    'BccAddresses' : []}
    try:
        bodyhtml = dftoconvert.to_html(float_format = lambda x: '({:15,.2f})'.format(abs(x)) if x < 0 else '+{:15,.2f}+'.format(abs(x)))
        # use no-break space instead of two spaces next to each other
        if replace:
            bodyhtml = bodyhtml.replace('  ', '&nbsp;')
        message = {'Subject' : {'Data' : subject},
                   'Body': {'Html' : {'Data' : bodyhtml}}}
    except NoneType: #If there is no data to convert to html
        message = {'Subject' : {'Data' : subject},
                   'Body': {'Text' : {'Data' : bodytext}}}
    except Exception as e:
        print(e)
    result = client.send_email(Source = me, 
                               Destination = destination, 
                               Message = message)    
    return result if 'ErrorResponse' in result else ''

email(dftoconvert=df)
email(dftoconvert=df, replace=True)

The result looks like this:

原版的

and with space replaced:

空间置换

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM