使用 Python 根据磁盘使用情况在 HTML 表格电子邮件正文中添加标题和文本颜色

Posted

技术标签:

【中文标题】使用 Python 根据磁盘使用情况在 HTML 表格电子邮件正文中添加标题和文本颜色【英文标题】:Add header and text color in HTML table email body based on disk usage condition using Python 【发布时间】:2022-01-13 01:09:56 【问题描述】:

我正在使用 python 在电子邮件正文中发送一个 html 表格。 html 表由磁盘使用情况组成,当磁盘使用率高于 80% 时,我需要在表中添加标题(第一行)和红色文本。

这是我正在使用的代码,用于获取带有文本颜色的电子邮件,但它不包括标题(服务器、总大小、总数据、使用百分比):

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()

我得到以下带有文本颜色但没有标题的输出:

预期输出:

非常感谢任何帮助。

【问题讨论】:

【参考方案1】:

在pandas 1.3.0 及更新版本中,最合适的方式是使用pandas Table Visualization 并创建一个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

【讨论】:

嗨@Henry,非常感谢您的回复。这很棒并且按预期工作。但是,当我在 csv 文件中添加小数时(例如:85.5 和 90.7),我得到了额外的零,我进入了 html 85.500000 和 90.700000。如果我想排除零并将值保留为 csv 文件,我不确定如何实现。 默认 Styler 精度为小数点后 6 位 @chiko 我已更新我的答案以包含 format 字符串以删除无关紧要的 0。您可以通过将subsetsubset='Usage in %' 更改为列表subset=['Total Data in', 'Usage in %'] 来包含多个列(例如)。

以上是关于使用 Python 根据磁盘使用情况在 HTML 表格电子邮件正文中添加标题和文本颜色的主要内容,如果未能解决你的问题,请参考以下文章

Linux根据UUID自动挂载磁盘分区

Linux服务器CPU内存磁盘空间负载情况查看python脚本

python监控主机磁盘

第一章 Python入门

PyX python脚本将文件写入磁盘但不写入屏幕

Shell脚本完成用户磁盘空间监测报告