一、背景
在实际工作中,有时需要写个定时脚本,每天自动发报表、监控等数据。有时需要在邮件的正文展示表格。 如果使用简单的html语法,例如代码1发邮件(代码在本文最后),则发出的表格如下图: 可以看出表格不太美观且数据杂乱。 基于此需求,开发了美化后的表格,且支持排序、合并单元格等操作。使用代码2可实现下图的表格效果
二、总体思路
相邻单元格中的值如果相同则会自动合并单元格。例如下图中的红框位置:
三、如何使用
以下是代码2中的方法,代码2见文章最后面
3.1 排序
方法:sort_data(data, sortlist)
参数【data】要排序的二维数据
参数【sortlist】排序的标准。data的第一列会按照sortlist[0]的顺序来排序,第二列按照sortlist[1]的顺序来排序。
例如 sortlist=[[‘初一’, ‘初二’], [‘语文’, ‘数学’]],那么 第一列 会按照初一、初二的顺序来排序,第二列 会按照语文、数学的顺序来排序。
3.2 数据转html表格
方法:data_to_html(data, titles, head=None, datacol=0, rowspan=False, colspan=False)
参数【data】数据二维列表
参数【titles】表头二维列表,例如[['年级', '学科']]
参数【head】表格标题,默认为None
参数【datacol】datacol=2表示从第二列开始是统计数据,统计数据不会进行合并,只有第二列之前会合并
参数【rowspan】是否进行垂直合并,默认否
参数【colspan】是否进行水平合并,默认否
3.2.1 常规表格
html1 = data_to_html(data, titles, head)
3.2.2 排序
data = sort_data(data, [sorted_grade, sorted_subject])
html2 = data_to_html(data, titles, head)
3.2.3 垂直合并表格
data = sort_data(data, [sorted_grade, sorted_subject]) #不需要排序时可以去掉这行
html3 = data_to_html(data, titles, head, 2, True, False)
3.2.4 水平合并表格
data = sort_data(data, [sorted_grade, sorted_subject]) #不需要排序时可以去掉这行
html4 = data_to_html(data, titles, head, 2, False, True)
3.2.5 垂直水平合并表格
data = sort_data(data, [sorted_grade, sorted_subject]) #不需要排序时可以去掉这行
html5 = data_to_html(data, titles, head, 2, True, True)
3.2.6 自定义宽度
data = sort_data(data, [sorted_grade, sorted_subject]) #不需要排序时可以去掉这行
html5 = data_to_html(data, titles, head, 2, True, True, [160, 120, 0, 50])
#前四列宽度依次为160 120 80 50,第五列后面的默认80,0也会转化为80
3.2.7 自定义颜色字号
data = sort_data(data, [sorted_grade, sorted_subject]) #不需要排序时可以去掉这行
html7 = data_to_html(data, titles, head, 2, True, True, [], True)
代码1
def data_to_html(data, title):
alarm_html = '<table border="1" cellpadding="5"><tr>'
for item in title:
alarm_html += '<td>%s</td>' % item
alarm_html += '</tr>'
for row in data:
alarm_html += '<tr>'
for item in row:
alarm_html += '<td>%s</td>' % item
alarm_html += "</tr>"
alarm_html += "</table>"
return alarm_html
if __name__ == '__main__':
titles = ['表头1', '表头2', '表头3', '表头4', '表头5']
data = [
['小学', '语文', 1, 1, 3],
['小学', '数学', 1, 5, 1],
['小学', '语文', 1, 1, 33],
['初中', '数学', 13, 1, 15],
['高中', '数学', 1, 1, 1],
['小学', '英语', 1, 8, 1],
['小学汇总', '小学汇总', 1, 1, 1],
['初中', '数学', 13, 1, 15],
['小学', '语文', 1, 1, 33],
['高中汇总', '高中汇总', 13, 1, 15]
]
html1 = data_to_html(data, titles)
mail_to = '123@qq.com'
subject = '邮件主题'
msg_txt = html1
send_mail(mail_to, subject, msg_txt)
代码2
def get_sort_list(l_type):
if l_type == '年级':
return ['小学', '初中', '高中']
if l_type == '学科':
return ['语文', '数学', '英语', '物理', '化学']
def sort_data(data, sortlist):
def get_sort_num(sort_dict, item):
for k in sort_dict:
if k in item:
return sort_dict[k]
return len(sort_dict)
for i in range(len(sortlist) - 1, -1, -1):
sort_mode = sortlist[i]
sort_dict = {}
sort_len = len(sort_mode)
for j in range(sort_len):
sort_dict[sort_mode[j]] = j
data = sorted(data, key=lambda x: get_sort_num(sort_dict, x[i]))
return data
def format_data(data_c):
data = copy.deepcopy(data_c)
for i in range(len(data)):
for j in range(len(data[i])):
data[i][j] = [data[i][j], 1, 1, '', '']
return data
def rowspan_data(data, datacol):
datacol = min(len(data), datacol)
last_split = [0, len(data)]
for j in range(datacol):
next_split = [0]
for spliti in range(len(last_split) - 1):
item_num = {}
item_list = []
for i in range(last_split[spliti], last_split[spliti + 1]):
item = data[i][j][0]
if item not in item_num:
item_list.append(item)
item_num[item] = 0
item_num[item] = item_num[item] + 1
item_in = set()
for i in range(last_split[spliti], last_split[spliti + 1]):
item = data[i][j][0]
if item not in item_in:
data[i][j][1] = item_num[item]
item_in.add(item)
else:
data[i][j][1] = 0
num_list = []
for item in item_list:
num_list.append(item_num[item])
last_num = next_split[-1]
for num in num_list:
next_split.append(last_num + num)
last_num = next_split[-1]
last_split = next_split
return data
def colspan_data(data, datacol):
datacol = min(len(data[0]), datacol)
for i in range(len(data)):
item_num = {}
item_list = []
for j in range(datacol):
item = data[i][j][0]
if item not in item_num:
item_list.append(item)
item_num[item] = 0
item_num[item] = item_num[item] + 1
item_in = set()
for j in range(datacol):
item = data[i][j][0]
if item not in item_in:
data[i][j][2] = item_num[item]
item_in.add(item)
else:
data[i][j][2] = 0
return data
def font_data(data, args):
'''
method:可根据需求对此函数进行修改
'''
colors = ["#00BB00", "#FF8000", "#FF0000", '#FF00FF']
sizes = ['2', '3', '4']
for i in range(len(data)):
for j in range(len(data[i])):
data[i][j][3] = colors[i % len(colors)]
data[i][j][4] = sizes[j % len(sizes)]
return data
def get_sub_html(h_type, args=[]):
colorcode = {'gray': '#ECEDF2;'}
if h_type == 'table0':
html = "<br><table border='1' cellspacing='0' cellpadding='0' style=\" border: 1px solid #CECFD4; border-collapse: collapse; font-size: 12px; " + \
"font-family: 'Helvetica Neue',Helvetica,'PingFang SC','Hiragino Sans GB','Microsoft YaHei','微软雅黑',Arial,sans-serif;\">\n"
elif h_type == 'table1':
html = "</table><br>\n"
elif h_type == 'tr0':
back_color = args[0] if len(args) > 0 else ''
back_color = colorcode.get(str(back_color), '')
html = '<tr style="text-align: center; vertical-align: middle; height: 30px; background-color: %s">\n' % (
back_color)
elif h_type == 'tr1':
html = '</tr>\n'
elif h_type == 'th0':
rowspan = args[0] if len(args) > 0 else 1
colspan = args[1] if len(args) > 1 else 1
width = args[2] if len(args) > 2 else 80
width = 80 if width == 0 else width
html = '<th colspan="%s" rowspan="%s" style="border: 1px solid #CECFD4; width:%spx;" align="center">' % (
colspan, rowspan, width)
elif h_type == 'th1':
html = '</th>\n'
elif h_type == 'td0':
rowspan = args[0] if len(args) > 0 else 1
colspan = args[1] if len(args) > 1 else 1
width = args[2] if len(args) > 2 else 80
width = 80 if width == 0 else width
html = '<td colspan="%s" rowspan="%s" style="border: 1px solid #CECFD4; width:%spx;" align="center">' % (
colspan, rowspan, width)
elif h_type == 'td1':
html = '</td>\n'
elif h_type == 'font0':
color = args[0] if len(args) > 1 else ''
size = args[1] if len(args) > 1 else ''
html = '<font color="%s" size="%s">' % (color, size)
elif h_type == 'font1':
html = '</font>\n'
else:
html = ''
return html
def get_html_data(data, titles, head, widths=[]):
html = get_sub_html('table0')
if head is not None:
html += get_sub_html('tr0', ['gray']) + \
get_sub_html('th0', [1, len(titles[0])]) + str(head) + get_sub_html('th1') + \
get_sub_html('tr1')
for row in titles:
html += get_sub_html('tr0', ['gray'])
for i in range(len(row)):
cell, rowspan, colspan, color, size = row[i]
width = widths[i] if len(widths) > i else 80
if rowspan > 0 and colspan > 0:
html += get_sub_html('th0', [rowspan, colspan, width]) + get_sub_html('font0', [color, size]) + str(
cell) + get_sub_html('font1') + get_sub_html('th1')
html += get_sub_html('tr1')
data_colors = ['gray', 'None']
color_i = 0
for row in data:
if row[0][1] > 0 and row[0][2] > 0:
color_i += 1
this_color = data_colors[color_i % len(data_colors)]
html += get_sub_html('tr0', [this_color])
for i in range(len(row)):
cell, rowspan, colspan, color, size = row[i]
width = widths[i] if len(widths) > i else 80
if rowspan > 0 and colspan > 0:
html += get_sub_html('td0', [rowspan, colspan, width]) + get_sub_html('font0', [color, size]) + str(
cell) + get_sub_html('font1') + get_sub_html('td1')
html += get_sub_html('tr1')
html += get_sub_html('table1')
return html
def data_to_html(data, titles, head=None, datacol=0, rowspan=False, colspan=False, widths=[], font=False):
'''
data:数据
type:二维数组
titles:表头
type:二维数组
如果只有一行表头,可以写成[['表头1','表头2']]
head:表格标题
type:string
默认为None,没有标题。
datacol:数据起始列
type:int
数据列不会排序&合并,例如datacol=3,则除了前三列往后都是数据,合并时也不会进行合并
rowspan:是否垂直合并
value:True or False
合并前请先排序,调用sort_data方法即可
colspan:是否水平合并
value:True or False
widths:每列宽度
type:一维数组
数组的每个值分别代表了每一列的宽度,默认80,写0也是80
font:是否需要自定义字号颜色
value:True or False
为True时表示,需要根据需求改变字号和颜色,需要修改font_data方法
'''
titles = format_data(titles)
data = format_data(data)
if rowspan == True and datacol > 0:
titles = rowspan_data(titles, len(titles[0]))
data = rowspan_data(data, datacol)
if colspan == True and datacol > 0:
titles = colspan_data(titles, len(titles[0]))
data = colspan_data(data, datacol)
if font == True:
data = font_data(data, [])
html = get_html_data(data, titles, head, widths)
return html
def get_example_data():
head = '表格标题'
titles = [['大表头1', '大表头1', '大表头2', '大表头3', '大表头3'],
['小表头1', '小表头2', '小表头3', '小表头2', '小表头5']]
data = [
['小学', '语文', 1, 1, 3],
['小学', '数学', 1, 5, 1],
['小学', '语文', 1, 1, 33],
['初中', '数学', 13, 1, 15],
['高中', '数学', 1, 1, 1],
['小学', '英语', 1, 8, 1],
['小学汇总', '小学汇总', 1, 1, 1],
['初中', '数学', 13, 1, 15],
['小学', '语文', 1, 1, 33],
['高中汇总', '高中汇总', 13, 1, 15]
]
return head, titles, data
def run_example():
sorted_grade = get_sort_list('年级')
sorted_subject = get_sort_list('学科')
head, titles, data = get_example_data()
html1 = data_to_html(data, titles, head)
head, titles, data = get_example_data()
data = sort_data(data, [sorted_grade, sorted_subject])
html2 = data_to_html(data, titles, head)
head, titles, data = get_example_data()
data = sort_data(data, [sorted_grade, sorted_subject])
html3 = data_to_html(data, titles, head, 2, True, False)
head, titles, data = get_example_data()
data = sort_data(data, [sorted_grade, sorted_subject])
html4 = data_to_html(data, titles, head, 2, False, True)
head, titles, data = get_example_data()
data = sort_data(data, [sorted_grade, sorted_subject])
html5 = data_to_html(data, titles, head, 2, True, True)
head, titles, data = get_example_data()
data = sort_data(data, [sorted_grade, sorted_subject])
html6 = data_to_html(data, titles, head, 2, True, True, [160, 120, 0, 50])
head, titles, data = get_example_data()
data = sort_data(data, [sorted_grade, sorted_subject])
html7 = data_to_html(data, titles, head, 2, True, True, [], True)
head, titles, data = get_example_data()
data = sort_data(data, [sorted_grade, sorted_subject])
html8 = data_to_html(data, titles, head, 2, True, True, [160, 120, 0, 50, 200], True)
mail_to = '123@qq.com'
subject = '邮件主题'
msg_txt = html1 + html2 + html3 + html4 + html5 + html6 + html7 + html8
send_mail(mail_to, subject, msg_txt)
if __name__ == '__main__':
run_example()
|