目录
最近有个需求,把每天自动生成的Excel 报告通过邮件发送的时候,在正文里也要展示出来。emmm…
这就只能使用exchangelib的HTMLBody来发送正文了。
接下来就上网狗哥一下excel转html,看到有人写过excel转html的方法,但是没有合并单元格和隐藏行的情况,没D用。既然没现成的,那就自己写一个出来吧,搞起来!
运行环境
- MacOS Big Sur
- Python 3.7.3rc1
第三方库
- openpyxl
准备工作
写好一份HTML模版,主要写好固定的表头和各种样式,放到一个html文件里读取,或者直接放到py文件中。
干货
这里主要是写tbody部分
准备了个函数
用来每读取一次单元格,合并一次html string。
table_html = '<tbody>'
def join_html(html_: str):
global table_html
table_html = '\n'.join((table_html, html_))
return table_html
带隐藏行的版本
from openpyxl import load_workbook
from openpyxl.worksheet.cell_range import MultiCellRange, CellRange
# 加载Excel文件,读取sheet
wb = load_workbook(EXCEL_FP)
sht = wb[SHT_NAME]
# 获取合并单元格的dict数据
a: MultiCellRange = sht.merged_cells
merged_celld = {i.coord.split(':')[0]: i for i in a.ranges}
# 遍历每一行, row_ : int
for row_ in range(3, sht.max_row):
# 如果当前行隐藏,则set hidden_row_
hidden_row_ = True if sht.row_dimensions[row_].hidden else False
join_html(''.join(('<tr', ' class="hide"' if hidden_row_ else '', f' ln={row_}', '>')))
# 遍历当前行每一格, col_ : 'A'
for col_ in map(chr, range(65, 65 + sht.max_column)):
# 开始写行内容
# A1 B1
coord_ = f"{col_}{row_}"
v_ = sht[coord_].value
# N列有空格子,把None转化成空str,防止误会
if col_ == 'N' and v_ is None:
v_ = ''
# EIM列数据是百分比格式
elif col_ in 'EIM' and v_ is not None:
v_ = f'{v_*100:.2f}%'
# 这里用not None,因为会有int 0
if v_ is not None:
# 如果有值,则查看是否是合并单元格, 如果是合并的单元格,其值为None,并且已经被span,不再需要处理
is_merged: CellRange = merged_celld.get(coord_)
# 如果是合并单元格,则获取合并单元格长度和宽度
if is_merged:
merged_size_c = is_merged.size.get('columns')
merged_size_r = is_merged.size.get('rows')
else:
# 初始化变量为0
merged_size_c = 0
merged_size_r = 0
td_ = ''.join(('<td',
f' rowspan="{merged_size_r}"' if merged_size_r else '',
f' colspan="{merged_size_c}"' if merged_size_c else '',
f'>{v_}</td>'
))
join_html(td_)
else:
# 遍历完该行后
join_html('</tr>')
# 遍历完所有行后,tbody收尾
table_html = join_html('</tbody>')
这个时候的table_html已经是想要的html版本表格了。
但是这个时候发现了个问题,我是在Google Chrome中调试的css样式,给想要隐藏的行添加了样式,给内容特别多的单元格设置了对齐(默认是居中)和溢出滑动。
.hide {
visibility: collapse
}
.fitem {
text-align: left;
vertical-align: top;
overflow: scroll;
}
效果很好,就和Excel表中的效果一模一样。不料拿到Safari中,隐藏的行如果有被合并的单元格,这一行就只会隐藏当前行被合并单元格之外的单元格,并且留白,并不上移与上一行对接。这不是Excel展示的效果。css了解不多,所以我就考虑隐藏的行就不要了,于是有了下面改动👇
自动过滤掉hide行
from openpyxl import load_workbook
from openpyxl.worksheet.cell_range import MultiCellRange, CellRange
# 加载Excel文件,读取sheet
wb = load_workbook(EXCEL_FP)
sht = wb[SHT_NAME]
# 获取合并单元格的dict数据
a: MultiCellRange = sht.merged_cells
merged_celld = {i.coord.split(':')[0]: i for i in a.ranges}
# 遍历每一行, row_ : int
for row_ in range(3, sht.max_row):
# 如果当前行隐藏,则set hidden_row_
hidden_row_ = True if sht.row_dimensions[row_].hidden else False
if hidden_row_:
continue
join_html(''.join(('<tr', f' ln={row_}', '>')))
# 遍历当前行每一格, col_ : 'A'
for col_ in map(chr, range(65, 65 + sht.max_column)):
# 开始写行内容
# A1 B1
coord_ = f"{col_}{row_}"
v_ = sht[coord_].value
# N列有空格子,把None转化成空str,防止误会
if col_ == 'N' and v_ is None:
v_ = ''
# EIM列数据是百分比格式
elif col_ in 'EIM' and v_ is not None:
v_ = f'{v_*100:.2f}%'
# 这里用not None,因为会有int 0
if v_ is not None:
# 如果有值,则查看是否是合并单元格, 如果是合并的单元格,其值为None,并且已经被span,不再需要处理
is_merged: CellRange = merged_celld.get(coord_)
# 如果是合并单元格,则获取合并单元格长度和宽度
if is_merged:
merged_size_c = is_merged.size.get('columns')
merged_size_r = is_merged.size.get('rows')
# 这里需要获取到合并单元格的最后一行行号
merged_bottom = is_merged.max_row
for cell_r in range(row_, merged_bottom+1):
# 然后开始过滤合并的行,发现一行隐藏,rowspan-1,最后得出的是正确的rowspan
if sht.row_dimensions[cell_r].hidden:
merged_size_r -= 1
else:
# 初始化变量为0
merged_size_c = 0
merged_size_r = 0
td_ = ''.join(('<td',
f' rowspan="{merged_size_r}"' if merged_size_r else '',
f' colspan="{merged_size_c}"' if merged_size_c else '',
f'>{v_}</td>'
))
join_html(td_)
else:
# 遍历完该行后
join_html('</tr>')
# 遍历完所有行后,tbody收尾
table_html = join_html('</tbody>')
最后看起来大致是想要的效果了,但是还是有个小问题:
我原本是设置了表头列宽,内容特别多的表格会以该列宽进行撑高,把所有内容都显示出来。这个时候overflow: auto/scroll/hidden都不管用了,因为它没溢出😭。
经过n次排除法找问题,发现只有当前行下有隐藏行,那么这些行所共有的合并单元格内容才会溢出,尝试改变td的display属性,可以溢出,但是只要display不是table-cell,整个表格就塌的像迈阿密的公寓一样。
这是我用的一些样式:
table {
table-layout: fixed;
border-collapse: collapse;
}
thead {
height: 21px;
color: white;
font-size: 12.0px;
font-weight: 900;
text-align: center;
vertical-align: middle;
background: #595959;
}
td {
word-break: break-all;
word-wrap: break-word;
border: 1px solid black
}
.hide {
visibility: collapse
}
.fitem {
text-align: left;
vertical-align: top;
overflow: scroll;
}
如果有前端玩的比较好的,可以尝试帮忙改改样式,可以让表格不塌,内容也能溢出。不胜感激。
声明
本文版权所属 @大耳朵图图画画 如需非商业性转载,请保留署名。如需商业性转载出版,请直接和我联系。