我搜索了网络上的方案,感觉把 Excel 表格转换为 HTML 再用 platwright 截图是比较顺畅的路径,因为有顺畅的工具链。如果使用的是 Windows 系统则不需要阅读此文,因为 win32com 库更方便。这篇文章中 Excel 转 HTML 的方案,主要弥补了网上其他方案中存在合并单元格的情况。代码为智谱清言帮助生成,有些变量控制还是需要自己改一下。
from openpyxl import load_workbook
from openpyxl.styles import Font, Border, Side, Alignment
from playwright.sync_api import sync_playwright
from datetime import datetime
# 打开浏览器并截图
def capture_table_screenshot( url, output_file, table_selector):
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
# 注意这里需要加协议
page.goto("file://" + url)
# 等待表格元素加载完成
page.wait_for_selector(table_selector)
page.wait_for_timeout(1000)
# 对表格元素进行截图
table_element = page.locator(table_selector)
table_element.screenshot(path=output_file)
browser.close()
# 默认合并单元格的文本内容是放在左上单元格的,如果不是,需要专门程序处理。
# 边框样式默认为1px solid
def read_excel(file_path):
# data_only 将 Excel 表格里的公式计算成数值读取出来。
wb = load_workbook( filename=file_path, data_only=True)
ws = wb.active # 读取活动工作表
data = []
merges = [] # 用于存储合并单元格的信息
cell_styles = []
# 读取合并单元格信息
for merged_range in ws.merged_cells.ranges:
start_row, start_col = merged_range.min_row, merged_range.min_col
end_row, end_col = merged_range.max_row, merged_range.max_col
merges.append((start_row-1, start_col-1, end_row-1, end_col-1))
for row in ws.iter_rows():
row_data = []
row_styles = []
for cell in row:
print(f"当前单元格的坐标:{cell.coordinate}")
if cell.coordinate in ws.merged_cells.ranges:
# 跳过合并单元格中的非起始单元格
continue
if cell.value is not None:
print(f"单元格的值:{cell.value}")
row_data.append(str(cell.value))
else:
row_data.append('') # 空单元格填充空字符串
# 读取单元格样式,提供默认值
font = cell.font if cell.font else Font()
border = cell.border if cell.border else Border()
alignment = cell.alignment if cell.alignment else Alignment()
print(f"单元格字体颜色:{font.color.index}")
print(f"单元格边框样式:{border.top.style}")
cell_style = {
'font': {
'name': font.name if font.name else 'Arial',
'size': font.size if font.size else 12,
'bold': font.bold if font.bold else False,
'italic': font.italic if font.italic else False,
'color': font.color.rgb if font.color and font.color.rgb else '#000000'
},
'border': {
'top': '1px solid' if border.top and border.top.style else None,
'left': '1px solid' if border.left and border.left.style else None,
'right': '1px solid' if border.right and border.right.style else None,
'bottom': '1px solid' if border.bottom and border.bottom.style else None
},
'alignment': {
'horizontal': alignment.horizontal if alignment.horizontal else None,
'vertical': alignment.vertical if alignment.vertical else None
}
}
row_styles.append(cell_style)
print(f"转换后的单元格样式:{cell_style}")
data.append(row_data)
cell_styles.append(row_styles)
return data, merges, cell_styles
# 该处默认只有同一行合并多列的情况。如果合并单元格占了两行,需要另外的处理。
def generate_html_table(data, merges, cell_styles):
print(f"合并单元格的信息:{merges}")
html = "<table style='border-collapse: collapse;'>\n"
for row_idx, row in enumerate(data):
print("-"*20)
print(f"当前行的数据:{row}")
html += "<tr>\n"
# 设置一个跳过非首个合并单元格的标记
skip_next_cell = 0
for col_idx,cell in enumerate(row):
if skip_next_cell > 0:
skip_next_cell -= 1
continue
# 行号、列号从0开始
print(f"当前单元格的值:{cell},行号:{row_idx},列号:{col_idx}")
# 如果当前单元格为1行4列,则修改cell值
if row_idx == 1 and col_idx == 4:
# 获取今天的日期
today = datetime.today()
cell = formatted_date_no_leading_zeros = "截止 " + today.strftime("%-m 月 %-d 日")
print(f"修改后的单元格值:{cell}")
# 去除单元格样式
style = cell_styles[row_idx][col_idx]
if style:
font_style = f"font-family:{style['font']['name']}; font-size:{style['font']['size']}pt; " \
f"font-weight:{'bold' if style['font']['bold'] else 'normal'}; " \
f"font-style:{'italic' if style['font']['italic'] else 'normal'};"
border_style = f"border-top:{style['border']['top']}; " \
f"border-left:{style['border']['left']}; " \
f"border-right:{style['border']['right']}; " \
f"border-bottom:{style['border']['bottom']};"
alignment_style = f"text-align:{style['alignment']['horizontal']}; " \
f"vertical-align:{style['alignment']['vertical']};"
if (row_idx, col_idx) in [(m[0], m[1]) for m in merges]: # 检查当前单元格是否是合并单元格的起始单元格
rowspan = [m[2] - m[0] + 1 for m in merges if m[0] == row_idx and m[1] == col_idx][0]
colspan = [m[3] - m[1] + 1 for m in merges if m[0] == row_idx and m[1] == col_idx][0]
if style:
html += f"<td style='{font_style} {border_style} {alignment_style}' rowspan={rowspan} colspan={colspan}>{cell}</td>"
else:
html += f"<td rowspan={rowspan} colspan={colspan}>{cell}</td>"
skip_next_cell = colspan - 1 # 跳过合并的列
else:
if style:
html += f"<td style='{font_style} {border_style} {alignment_style}' >{cell}</td>"
else:
html += f"<td>{cell}</td>"
html += "</tr>\n"
html += "</table>"
html = "<!DOCTYPE html><html><head><meta charset='UTF-8'><title>Excel Table</title></head><body>" + html + "</body></html>"
return html
def main():
current_dir = 'reer'
excel_file_path = current_dir + 'log/2re0207.xlsx' # 替换为你的Excel文件路径
html_file_path = current_dir + 'log/output.html'
screenshot_file_path = current_dir + 'log/table_screenshot.png'
data, merges, cell_styles = read_excel(excel_file_path)
html_table = generate_html_table(data, merges, cell_styles)
with open(html_file_path, 'w', encoding='utf-8') as file:
file.write(html_table)
# 调用函数,替换以下参数
url = html_file_path # 网页URL
output_file = screenshot_file_path # 输出文件路径
table_selector = 'table' # 表格的CSS选择器,根据实际情况调整
capture_table_screenshot(url, output_file, table_selector)
if __name__ == "__main__":
main()