Files
claudeskill/skills/excel-to-markdown/scripts/excel_to_markdown.py
Misaka Company cb8f57c12b Add worksheet selection to excel-to-markdown skill
Add --sheet parameter to specify worksheets by name or index (1-based). Change default behavior to use first worksheet instead of active sheet, with proper error messages for invalid worksheet references.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-29 10:10:57 +08:00

246 lines
8.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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, sheet_name: Optional[str] = None):
"""
初始化转换器
Args:
excel_file: Excel文件路径
sheet_name: 工作表名称或索引None表示使用第一个工作表
"""
self.workbook = openpyxl.load_workbook(excel_file, data_only=True)
# 获取指定的工作表
if sheet_name is None:
# 使用第一个工作表
self.sheet = self.workbook.worksheets[0]
elif isinstance(sheet_name, int) or sheet_name.isdigit():
# 按索引获取从1开始
sheet_index = int(sheet_name) - 1
if 0 <= sheet_index < len(self.workbook.worksheets):
self.sheet = self.workbook.worksheets[sheet_index]
else:
raise ValueError(f"工作表索引 {sheet_name} 超出范围1-{len(self.workbook.worksheets)}")
else:
# 按名称获取
if sheet_name in self.workbook.sheetnames:
self.sheet = self.workbook[sheet_name]
else:
available_sheets = ", ".join(self.workbook.sheetnames)
raise ValueError(f"找不到工作表 '{sheet_name}',可用的工作表: {available_sheets}")
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
# 指定工作表(按名称)
python excel_to_markdown.py input.xlsx -o output.md --sheet "Sheet2"
# 指定工作表(按索引)
python excel_to_markdown.py input.xlsx -o output.md --sheet 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')
parser.add_argument('--sheet', help='工作表名称或索引(不指定则使用第一个工作表)')
args = parser.parse_args()
try:
# 创建转换器
converter = ExcelToMarkdown(args.input_file, args.sheet)
# 执行转换
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()