Add excel-to-markdown skill and converter script
- Add excel_to_markdown.py script for converting Excel files to Markdown tables - Create excel-to-markdown skill with SKILL.md documentation - Support automatic data range detection and flexible output options - Include features for row/column numbering and partial conversions - Add skill-creator reference documentation Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
220
skills/excel-to-markdown/scripts/excel_to_markdown.py
Normal file
220
skills/excel-to-markdown/scripts/excel_to_markdown.py
Normal file
@@ -0,0 +1,220 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Excel to Markdown Converter
|
||||
将Excel文档转换为Markdown表格格式
|
||||
"""
|
||||
|
||||
import openpyxl
|
||||
from openpyxl.utils import get_column_letter
|
||||
from typing import Optional, Tuple
|
||||
import argparse
|
||||
|
||||
|
||||
class ExcelToMarkdown:
|
||||
"""Excel转Markdown转换器"""
|
||||
|
||||
def __init__(self, excel_file: str):
|
||||
"""
|
||||
初始化转换器
|
||||
|
||||
Args:
|
||||
excel_file: Excel文件路径
|
||||
"""
|
||||
self.workbook = openpyxl.load_workbook(excel_file, data_only=True)
|
||||
self.sheet = self.workbook.active
|
||||
|
||||
def detect_data_range(self, max_scan_rows: int = 100, max_scan_cols: int = 100) -> Tuple[int, int]:
|
||||
"""
|
||||
自动检测数据范围
|
||||
|
||||
Args:
|
||||
max_scan_rows: 最大扫描行数
|
||||
max_scan_cols: 最大扫描列数
|
||||
|
||||
Returns:
|
||||
(最大行号, 最大列号)
|
||||
"""
|
||||
max_row = 0
|
||||
max_col = 0
|
||||
|
||||
# 扫描前100行和前100列
|
||||
scan_rows = min(max_scan_rows, self.sheet.max_row)
|
||||
scan_cols = min(max_scan_cols, self.sheet.max_column)
|
||||
|
||||
for row in range(1, scan_rows + 1):
|
||||
for col in range(1, scan_cols + 1):
|
||||
cell_value = self.sheet.cell(row=row, column=col).value
|
||||
if cell_value is not None and str(cell_value).strip() != "":
|
||||
max_row = max(max_row, row)
|
||||
max_col = max(max_col, col)
|
||||
|
||||
return max_row, max_col
|
||||
|
||||
def convert(self,
|
||||
max_rows: Optional[int] = None,
|
||||
max_cols: Optional[int] = None,
|
||||
show_row_numbers: bool = False,
|
||||
show_col_numbers: bool = False,
|
||||
start_row: int = 1,
|
||||
start_col: int = 1) -> str:
|
||||
"""
|
||||
转换Excel为Markdown格式
|
||||
|
||||
Args:
|
||||
max_rows: 最大行数(None表示自动检测)
|
||||
max_cols: 最大列数(None表示自动检测)
|
||||
show_row_numbers: 是否显示行号
|
||||
show_col_numbers: 是否显示列号(字母形式)
|
||||
start_row: 起始行号(默认1)
|
||||
start_col: 起始列号(默认1)
|
||||
|
||||
Returns:
|
||||
Markdown格式的表格字符串
|
||||
"""
|
||||
# 自动检测数据范围
|
||||
if max_rows is None or max_cols is None:
|
||||
detected_rows, detected_cols = self.detect_data_range()
|
||||
if max_rows is None:
|
||||
max_rows = detected_rows
|
||||
if max_cols is None:
|
||||
max_cols = detected_cols
|
||||
|
||||
# 确保至少有1行1列
|
||||
if max_rows < 1 or max_cols < 1:
|
||||
return "| 无数据 |\n|--------|\n"
|
||||
|
||||
# 计算实际的结束位置
|
||||
end_row = min(start_row + max_rows - 1, self.sheet.max_row)
|
||||
end_col = min(start_col + max_cols - 1, self.sheet.max_column)
|
||||
|
||||
markdown_lines = []
|
||||
|
||||
# 构建列号行(如果需要)
|
||||
if show_col_numbers:
|
||||
header_parts = []
|
||||
if show_row_numbers:
|
||||
header_parts.append("") # 行号列的占位
|
||||
|
||||
for col in range(start_col, end_col + 1):
|
||||
col_letter = get_column_letter(col)
|
||||
header_parts.append(col_letter)
|
||||
|
||||
markdown_lines.append("| " + " | ".join(header_parts) + " |")
|
||||
|
||||
# 分隔线
|
||||
separator_parts = ["---"] * len(header_parts)
|
||||
markdown_lines.append("| " + " | ".join(separator_parts) + " |")
|
||||
|
||||
# 构建数据行
|
||||
for row in range(start_row, end_row + 1):
|
||||
row_parts = []
|
||||
|
||||
# 添加行号(如果需要)
|
||||
if show_row_numbers:
|
||||
row_parts.append(str(row))
|
||||
|
||||
# 添加单元格数据
|
||||
for col in range(start_col, end_col + 1):
|
||||
cell_value = self.sheet.cell(row=row, column=col).value
|
||||
|
||||
# 处理空值
|
||||
if cell_value is None:
|
||||
cell_str = ""
|
||||
else:
|
||||
cell_str = str(cell_value).strip()
|
||||
# 转义Markdown特殊字符
|
||||
cell_str = cell_str.replace("|", "\\|")
|
||||
cell_str = cell_str.replace("\n", "<br>")
|
||||
|
||||
row_parts.append(cell_str)
|
||||
|
||||
markdown_lines.append("| " + " | ".join(row_parts) + " |")
|
||||
|
||||
# 如果是第一行且没有显示列号,添加分隔线
|
||||
if row == start_row and not show_col_numbers:
|
||||
separator_parts = ["---"] * len(row_parts)
|
||||
markdown_lines.append("| " + " | ".join(separator_parts) + " |")
|
||||
|
||||
return "\n".join(markdown_lines)
|
||||
|
||||
def save_to_file(self, markdown_text: str, output_file: str):
|
||||
"""
|
||||
保存Markdown到文件
|
||||
|
||||
Args:
|
||||
markdown_text: Markdown文本
|
||||
output_file: 输出文件路径
|
||||
"""
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
f.write(markdown_text)
|
||||
|
||||
def close(self):
|
||||
"""关闭工作簿"""
|
||||
self.workbook.close()
|
||||
|
||||
|
||||
def main():
|
||||
"""命令行入口"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description='将Excel文档转换为Markdown表格格式',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
示例:
|
||||
# 自动检测范围并转换
|
||||
python excel_to_markdown.py input.xlsx -o output.md
|
||||
|
||||
# 指定转换前10行、前5列
|
||||
python excel_to_markdown.py input.xlsx -o output.md -r 10 -c 5
|
||||
|
||||
# 显示行号和列号
|
||||
python excel_to_markdown.py input.xlsx -o output.md --show-rows --show-cols
|
||||
|
||||
# 从第2行第2列开始,转换5行3列
|
||||
python excel_to_markdown.py input.xlsx -o output.md -r 5 -c 3 --start-row 2 --start-col 2
|
||||
"""
|
||||
)
|
||||
|
||||
parser.add_argument('input_file', help='输入的Excel文件路径')
|
||||
parser.add_argument('-o', '--output', help='输出的Markdown文件路径(不指定则输出到控制台)')
|
||||
parser.add_argument('-r', '--max-rows', type=int, help='最大转换行数(不指定则自动检测)')
|
||||
parser.add_argument('-c', '--max-cols', type=int, help='最大转换列数(不指定则自动检测)')
|
||||
parser.add_argument('--show-rows', action='store_true', help='显示行号')
|
||||
parser.add_argument('--show-cols', action='store_true', help='显示列号(字母形式)')
|
||||
parser.add_argument('--start-row', type=int, default=1, help='起始行号(默认1)')
|
||||
parser.add_argument('--start-col', type=int, default=1, help='起始列号(默认1)')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
# 创建转换器
|
||||
converter = ExcelToMarkdown(args.input_file)
|
||||
|
||||
# 执行转换
|
||||
markdown_text = converter.convert(
|
||||
max_rows=args.max_rows,
|
||||
max_cols=args.max_cols,
|
||||
show_row_numbers=args.show_rows,
|
||||
show_col_numbers=args.show_cols,
|
||||
start_row=args.start_row,
|
||||
start_col=args.start_col
|
||||
)
|
||||
|
||||
# 输出结果
|
||||
if args.output:
|
||||
converter.save_to_file(markdown_text, args.output)
|
||||
print(f"✓ 转换完成!已保存到: {args.output}")
|
||||
else:
|
||||
print(markdown_text)
|
||||
|
||||
# 关闭工作簿
|
||||
converter.close()
|
||||
|
||||
except FileNotFoundError:
|
||||
print(f"✗ 错误: 找不到文件 '{args.input_file}'")
|
||||
except Exception as e:
|
||||
print(f"✗ 错误: {str(e)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user