♻️
Python(openpyxl)でExcelをHTMLに変換する試み
はじめに
今回はExcelで作った文書のレイアウトを、PythonでHTMLに変換するツールを作成してみました。
サンプルで使った文書は、いわゆるExcel方眼紙の稟議書です。
結果
実行結果から書いてしまいますが、Pythonのライブラリ「openpyxl」を使うことで、
Excelのセルの情報を読み取り、それっぽくHTML+CSSに変換できることができました!
ですが、再現性を上げるには装飾の反映や、マージセルの考慮などが大変なことも分かりました。
処理の流れを解説しますが、罫線の種類、背景色、文字色の指定などいろいろ未完成です💦
処理の流れ
【1】メイン処理
エントリーポイントで、以下の処理をおこないます。
- 指定パスのExcelファイルとワークシートを開きます。
wb = openpyxl.load_workbook(excel_path, data_only=True)
ws = wb.active
- Excelファイルのセル構成を読み取ってHTMLに変換し、htmlファイルに出力します。
- Excelファイルのセル装飾を読み取ってCSSを生成し、cssファイルに出力します。
main.py
import openpyxl
import generate_css_styles
import excel_to_html
# ファイルパスを定義
excel_file_path = "excel.xlsx"
html_output_path = "output.html"
css_output_path = "styles.css"
def generate_html_and_css_files(excel_path, css_path, html_path):
wb = openpyxl.load_workbook(excel_path, data_only=True)
ws = wb.active
generate_html_file(ws, html_path)
generate_css_file(ws, css_path)
print("完了しました。")
def generate_html_file(ws, html_path):
html_content = excel_to_html.convert(ws)
with open(html_path, "w", encoding="utf-8") as html_file:
html_file.write(html_content)
print("HTMLファイルを作成しました。")
def generate_css_file(ws, css_path):
css_styles = generate_css_styles.generate(ws)
with open(css_path, "w", encoding="utf-8") as css_file:
css_file.write(css_styles)
print("CSSファイルを作成しました。")
if __name__ == "__main__":
generate_html_and_css_files(excel_file_path, css_output_path, html_output_path)
【2】HTMLへの変換処理
メイン処理から呼ばれて、セル構成から変換したHTML情報を返します。
- Excelシートからセル内容を取得し、HTMLのtable形式に変換します。
ws.iter_rows()
- 各tdにはCSSのclass名を付与します。
- マージセルにはrowspan、colspanを付与します。
ws.merged_cells
excel_to_html.py
def convert(ws):
table_content = convert_to_html_table(ws)
html_template = generate_html_template(table_content)
return html_template
def convert_to_html_table(ws):
table_content = ""
for row_idx, row in enumerate(ws.iter_rows(), start=1):
table_content += "<tr>"
for col_idx, cell in enumerate(row, start=1):
cell_value = cell.value if cell.value is not None else ""
cell_value = newline_to_html_breaks(str(cell_value))
css_class = f"cell-{row_idx}-{col_idx}"
rowspan, colspan = get_col_row_span(ws, cell)
table_content += f'<td class="{css_class}" rowspan="{rowspan}" colspan="{colspan}">{cell_value}</td>'
table_content += "</tr>"
return table_content
def newline_to_html_breaks(text):
return text.replace('\n', '<br>').replace('\r\n', '<br>')
def get_col_row_span(ws, cell):
rowspan = 1
colspan = 1
if cell.coordinate in ws.merged_cells:
for merged_range in ws.merged_cells.ranges:
if cell.coordinate in merged_range:
rowspan = merged_range.max_row - merged_range.min_row + 1
colspan = merged_range.max_col - merged_range.min_col + 1
break
return rowspan, colspan
def generate_html_template(table_content):
html_template = f"""
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<table class="excel-table">
{table_content}
</table>
</body>
</html>
"""
return html_template
【3】CSSの生成処理
メイン処理から呼ばれて、セル装飾から生成したCSS情報を返します。
繰り返しになりますが未完成です💣💥
- ExcelからHTMLに変換したtableのCSSを定義します。
- セルの罫線、文字のサイズ、太字、配置、セルの幅高さなどから、各tdのCSSを生成します。
cell.border
cell.font.size
cell.font.bold
cell.alignment
ws.row_dimensions[cell.row].height
generate_css_styles.py
import openpyxl
from openpyxl.utils import get_column_letter
def generate(ws):
css_styles = generate_table_styles()
for row in ws.iter_rows():
for cell in row:
css_styles += generate_cell_styles(ws, cell)
css_styles += generate_merged_cell_styles(ws, cell)
return css_styles
def generate_table_styles():
return """
.excel-table {
border-collapse: collapse;
}
"""
def generate_cell_styles(ws, cell):
css_properties = []
css_properties.append(generate_bold_styles(cell))
css_properties.append(generate_border_styles(cell))
css_properties.append(generate_width_styles(ws, cell))
css_properties.append(generate_height_styles(ws, cell))
css_properties.append(generate_font_styles(cell))
css_properties.append(generate_alignment_styles(cell))
css_properties = list(filter(lambda x: x != "", css_properties))
css_indent = " "
css_class = f".cell-{cell.row}-{cell.column} {{\n{css_indent}"
css_class += f"\n{css_indent}".join(css_properties)
css_class += "\n}\n"
return css_class
def generate_bold_styles(cell):
if cell.font and cell.font.bold:
return "font-weight: bold;"
return ""
def generate_border_styles(cell):
border_sides = ["top", "left", "right", "bottom"]
border_styles = []
for border_side in border_sides:
border = getattr(cell.border, border_side)
if border.style and border.style != "none":
border_color = border.color.rgb[2:] if border.color else "000000" # Default color is black
border_styles.append(f"border-{border_side}: solid 1px #{border_color};")
if border_styles:
return " ".join(border_styles)
return ""
def generate_width_styles(ws, cell):
col_width = ws.column_dimensions[get_column_letter(cell.column)].width
col_width = points_to_pixels(col_width)
return f"width: {col_width}px;"
def generate_height_styles(ws, cell):
row_height = ws.row_dimensions[cell.row].height
return f"height: {row_height}px;"
def generate_font_styles(cell):
return f"font-size: {cell.font.size}px;"
def generate_alignment_styles(cell):
horizontal_alignment = cell.alignment.horizontal
if horizontal_alignment in ["left", "center", "right"]:
return f"text-align: {horizontal_alignment};"
return ""
def generate_merged_cell_styles(ws, cell):
css_styles = ""
for merged_range in ws.merged_cells.ranges:
if cell.coordinate not in merged_range:
continue
for row in range(merged_range.min_row, merged_range.max_row + 1):
for col in range(merged_range.min_col, merged_range.max_col + 1):
if row != merged_range.min_row or col != merged_range.min_col:
merged_cell_class = f".cell-{row}-{col}"
css_styles += f"{merged_cell_class} {{ display: none; }}\n"
return css_styles
def points_to_pixels(points):
pixels = points * 1.33
return round(pixels)
おわりに
openpyxlはExcelファイルを強力かつ柔軟に操作できるため、データ操作や自動レポート作成などExcel業務の効率化にもいろいろ活用できそうです。
活Excelの際に、ご利用を検討してみてはいかがでしょうか。
HTML変換の際は、当記事が少しでも参考になれば幸いです🫠
Discussion