使用 Python 根据磁盘使用情况在 HTML table 电子邮件 body 中添加 header 和文本颜色

Add header and text color in HTML table email body based on disk usage condition using Python

我正在使用 python 在电子邮件 body 中发送 html table。 html table 包含磁盘使用情况,当磁盘使用率超过 80% 时,我需要在 table 中添加 header(第一行)和红色文本。

这是我正在使用的代码,它可以通过为文本着色来获取电子邮件,但它不包括 headers(服务器、总大小、总数据、使用百分比):

me = 'noreply@automationtest.com'

server = 'some smtp server'
you = 'email@someautomation.com'

text = """
{table}
"""

html = """
<html>
<head>
<style> 
  table, th, td {{ border: 1px solid black; border-collapse: collapse; }}
  th, td {{ padding: 5px; }}
</style>
</head>
<body><p style="font-family:verdana">Hi,</p>
<p style="font-family:verdana">sometext</p>
{table}
<p style="font-family:verdana">sometext</p>
<p style="font-family:verdana">Regards</p>
<p style="font-family:verdana">someme</p>
</body></html>
""" 

with open('files/file.csv') as input_file:
    reader = DictReader(input_file)
    data = list(reader)
    for row in data:
      row['Usage in %'] = pd.to_numeric(row['Usage in %'])
      if row['Usage in %'] >= 80:
      row['Usage in %'] = "<p style='color:red'>%s</p>"%row['Usage in %']


text = text.format(table=tabulate(data, headers="firstrow", tablefmt="grid"))

html = html.format(table=tabulate(data, headers="firstrow", tablefmt="unsafehtml"))
message = MIMEMultipart("alternative", None, [MIMEText(text), MIMEText(html,'html')])
print(html)


message['From'] = me
message['To'] = you
server = smtplib.SMTP(server)
server.ehlo()
server.starttls()
server.login(me, password)
server.sendmail(me, you, message.as_string())
server.quit()

我得到以下带有文本颜色但没有 header:

的输出

预期输出:

非常感谢任何帮助。

在 pandas 1.3.0 及更新版本中,最合适的方法是使用 pandas Table Visualization and create a Subclass

创建文件夹“templates”和两个文件“myhtml.tpl”和“mystyles.tpl”

在 myhtml.tpl 中放入任何额外的 HTML 所需代码:

{% extends "html_table.tpl" %}
{% block table %}
<p style="font-family:verdana">Hi,</p>
<p style="font-family:verdana">sometext</p>
{{ super() }}
<p style="font-family:verdana">sometext</p>
<p style="font-family:verdana">Regards</p>
<p style="font-family:verdana">someme</p>
{% endblock table %}

在 mystyles.tpl 中添加任何其他样式:

{% extends "html_style.tpl" %}
{% block style %}
{{ super() }}
<style>
    table, th, td {
        border: 1px solid black;
        border-collapse: collapse;
    }

    th, td {
        padding: 5px;
    }
</style>
{% endblock style %}

(生成这个文件结构的代码在这个答案的末尾)


我们现在可以用 from_custom_template

生成一个 Styler 子类
import numpy as np
import pandas as pd
from pandas.io.formats.style import Styler

# Build Styler Subclass from templates
MyStyler = Styler.from_custom_template(
    "templates",  # Folder to Search
    html_table="myhtml.tpl",  # HTML Template
    html_style='mystyles.tpl'  # CSS Template
)
# trim extra whitespace from HTML
MyStyler.env.trim_blocks = True

# Read in CSV
df = pd.read_csv('files/file.csv')

# Add styles using Styler apply and render to_html
html = MyStyler(df).apply(
    lambda s: np.where(s >= 80, 'color: red', None), subset='Usage in %'
).format(
    # Apply format string (remove insignificant zeros)
    formatter='{:g}', subset='Usage in %'
).hide_index().to_html(doctype_html=True)

print(html)

生成的 html 字符串类似于:

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">

  <style type="text/css">
    #T_22fdb_row2_col3,
    #T_22fdb_row3_col3 {
      color: red;
    }
  </style>

  <style>
    table,
    th,
    td {
      border: 1px solid black;
      border-collapse: collapse;
    }
    
    th,
    td {
      padding: 5px;
    }
  </style>
</head>

<body>

  <p style="font-family:verdana">Hi,</p>
  <p style="font-family:verdana">sometext</p>
  <table id="T_22fdb_">
    <thead>
      <tr>
        <th class="col_heading level0 col0">Server</th>
        <th class="col_heading level0 col1">Total size</th>
        <th class="col_heading level0 col2">Total Data in</th>
        <th class="col_heading level0 col3">Usage in %</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td id="T_22fdb_row0_col0" class="data row0 col0">A</td>
        <td id="T_22fdb_row0_col1" class="data row0 col1">100</td>
        <td id="T_22fdb_row0_col2" class="data row0 col2">25</td>
        <td id="T_22fdb_row0_col3" class="data row0 col3">25</td>
      </tr>
      <tr>
        <td id="T_22fdb_row1_col0" class="data row1 col0">B</td>
        <td id="T_22fdb_row1_col1" class="data row1 col1">100</td>
        <td id="T_22fdb_row1_col2" class="data row1 col2">20</td>
        <td id="T_22fdb_row1_col3" class="data row1 col3">20</td>
      </tr>
      <tr>
        <td id="T_22fdb_row2_col0" class="data row2 col0">C</td>
        <td id="T_22fdb_row2_col1" class="data row2 col1">100</td>
        <td id="T_22fdb_row2_col2" class="data row2 col2">85</td>
        <td id="T_22fdb_row2_col3" class="data row2 col3">85.6</td>
      </tr>
      <tr>
        <td id="T_22fdb_row3_col0" class="data row3 col0">D</td>
        <td id="T_22fdb_row3_col1" class="data row3 col1">100</td>
        <td id="T_22fdb_row3_col2" class="data row3 col2">90</td>
        <td id="T_22fdb_row3_col3" class="data row3 col3">90.8</td>
      </tr>
    </tbody>
  </table>

  <p style="font-family:verdana">sometext</p>
  <p style="font-family:verdana">Regards</p>
  <p style="font-family:verdana">someme</p>
</body>

</html>


解决此问题的更简单的方法是直接使用格式化操作 html 字符串,但是,这可能会导致 html:

格式错误
import numpy as np
import pandas as pd

html = """
<html>
<head>
<style> 
  table, th, td {{ border: 1px solid black; border-collapse: collapse; }}
  th, td {{ padding: 5px; }}
</style>
</head>
<body><p style="font-family:verdana">Hi,</p>
<p style="font-family:verdana">sometext</p>
{table}
<p style="font-family:verdana">sometext</p>
<p style="font-family:verdana">Regards</p>
<p style="font-family:verdana">someme</p>
</body></html>
"""

# Read in CSV
df = pd.read_csv('files/file.csv')

# Add styles using Styler apply and render to_html
table_html = df.style.apply(
    lambda s: np.where(s >= 80, 'color: red', None), subset='Usage in %'
).format(
    # Apply format string (remove insignificant zeros)
    formatter='{:g}', subset=['Total Data in', 'Usage in %']
).hide_index().to_html()

html = html.format(table=table_html)

print(html)

结果如下 html(注意样式元素放错地方):

<html>
<head>
<style> 
  table, th, td { border: 1px solid black; border-collapse: collapse; }
  th, td { padding: 5px; }
</style>
</head>
<body><p style="font-family:verdana">Hi,</p>
<p style="font-family:verdana">sometext</p>
<style type="text/css">
#T_139a3_row2_col3, #T_139a3_row3_col3 {
  color: red;
}
</style>
<table id="T_139a3_">
  <thead>
    <tr>
      <th class="col_heading level0 col0" >Server</th>
      <th class="col_heading level0 col1" >Total size</th>
      <th class="col_heading level0 col2" >Total Data in</th>
      <th class="col_heading level0 col3" >Usage in %</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td id="T_139a3_row0_col0" class="data row0 col0" >A</td>
      <td id="T_139a3_row0_col1" class="data row0 col1" >100</td>
      <td id="T_139a3_row0_col2" class="data row0 col2" >25</td>
      <td id="T_139a3_row0_col3" class="data row0 col3" >25</td>
    </tr>
    <tr>
      <td id="T_139a3_row1_col0" class="data row1 col0" >B</td>
      <td id="T_139a3_row1_col1" class="data row1 col1" >100</td>
      <td id="T_139a3_row1_col2" class="data row1 col2" >20</td>
      <td id="T_139a3_row1_col3" class="data row1 col3" >20</td>
    </tr>
    <tr>
      <td id="T_139a3_row2_col0" class="data row2 col0" >C</td>
      <td id="T_139a3_row2_col1" class="data row2 col1" >100</td>
      <td id="T_139a3_row2_col2" class="data row2 col2" >85</td>
      <td id="T_139a3_row2_col3" class="data row2 col3" >85.6</td>
    </tr>
    <tr>
      <td id="T_139a3_row3_col0" class="data row3 col0" >D</td>
      <td id="T_139a3_row3_col1" class="data row3 col1" >100</td>
      <td id="T_139a3_row3_col2" class="data row3 col2" >90</td>
      <td id="T_139a3_row3_col3" class="data row3 col3" >90.8</td>
    </tr>
  </tbody>
</table>

<p style="font-family:verdana">sometext</p>
<p style="font-family:verdana">Regards</p>
<p style="font-family:verdana">someme</p>
</body></html>


构建模板文件夹和两个模板文件的简单脚本:

import pathlib


# Code to generate the Templates and folder
pathlib.Path('./templates').mkdir(exist_ok=True)
with open('./templates/myhtml.tpl', 'w') as f:
    f.write('''
{% extends "html_table.tpl" %}
{% block table %}
<p style="font-family:verdana">Hi,</p>
<p style="font-family:verdana">sometext</p>
{{ super() }}
<p style="font-family:verdana">sometext</p>
<p style="font-family:verdana">Regards</p>
<p style="font-family:verdana">someme</p>
{% endblock table %}
'''.strip())
with open('./templates/mystyles.tpl', 'w') as f:
    f.write('''
{% extends "html_style.tpl" %}
{% block style %}
{{ super() }}
<style>
    table, th, td {
        border: 1px solid black;
        border-collapse: collapse;
    }

    th, td {
        padding: 5px;
    }
</style>
{% endblock style %}
'''.strip())

files/file.csv内容:

Server,Total size,Total Data in,Usage in %
A,100,25,25
B,100,20,20
C,100,85,85.6
D,100,90,90.8