From b017e367539ecd535fcb8c7ae70e1e5c0bb281bd Mon Sep 17 00:00:00 2001 From: Misaka Company Date: Tue, 27 Jan 2026 12:54:34 +0800 Subject: [PATCH] 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 --- .gitignore | 43 ++++ scripts/excel_to_markdown.py | 220 ++++++++++++++++++ skills/excel-to-markdown/SKILL.md | 121 ++++++++++ .../scripts/excel_to_markdown.py | 220 ++++++++++++++++++ 4 files changed, 604 insertions(+) create mode 100644 .gitignore create mode 100644 scripts/excel_to_markdown.py create mode 100644 skills/excel-to-markdown/SKILL.md create mode 100644 skills/excel-to-markdown/scripts/excel_to_markdown.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d6bfb28 --- /dev/null +++ b/.gitignore @@ -0,0 +1,43 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# UV +.venv/ +uv.lock + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Project specific +*.xlsx +*.xls + +# .claude +.claude/ \ No newline at end of file diff --git a/scripts/excel_to_markdown.py b/scripts/excel_to_markdown.py new file mode 100644 index 0000000..d7f1149 --- /dev/null +++ b/scripts/excel_to_markdown.py @@ -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", "
") + + 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() \ No newline at end of file diff --git a/skills/excel-to-markdown/SKILL.md b/skills/excel-to-markdown/SKILL.md new file mode 100644 index 0000000..3ec44b9 --- /dev/null +++ b/skills/excel-to-markdown/SKILL.md @@ -0,0 +1,121 @@ +--- +name: excel-to-markdown +description: Convert Excel (.xlsx, .xls) files to Markdown table format. Use for converting Excel documents to Markdown for display or documentation, extracting and analyzing Excel data in a structured format, processing Excel files with automatic data range detection, generating Markdown tables with optional row/column numbering, or handling partial conversions with specific row/column ranges. Triggered by requests like "convert this Excel to Markdown", "export Excel as MD", "analyze this spreadsheet", or when working with .xlsx/.xls files that need to be viewed or processed as text. +--- + +# Excel to Markdown Converter + +## Overview + +Convert Excel spreadsheets to Markdown table format with automatic data range detection and flexible output options. The converter handles complex multi-sheet workbooks, preserves cell formatting, and can display row/column numbers for easy reference. + +## Quick Start + +### Basic Conversion + +Convert an entire Excel file to Markdown: + +```bash +python3 scripts/excel_to_markdown.py input.xlsx -o output.md +``` + +### Convert with Row/Column Numbers + +Display Excel row and column identifiers for reference: + +```bash +python3 scripts/excel_to_markdown.py input.xlsx -o output.md --show-rows --show-cols +``` + +### Specify Data Range + +Convert only a specific portion of the spreadsheet: + +```bash +# Convert first 10 rows and 5 columns +python3 scripts/excel_to_markdown.py input.xlsx -o output.md -r 10 -c 5 + +# Start from row 2, column 3, convert 20 rows +python3 scripts/excel_to_markdown.py input.xlsx -o output.md --start-row 2 --start-col 3 -r 20 +``` + +## Command Line Options + +| Option | Description | +|--------|-------------| +| `input_file` | Path to the Excel file (required) | +| `-o, --output` | Output Markdown file path (optional, prints to console if not specified) | +| `-r, --max-rows` | Maximum number of rows to convert (auto-detect if not specified) | +| `-c, --max-cols` | Maximum number of columns to convert (auto-detect if not specified) | +| `--show-rows` | Display row numbers in the first column | +| `--show-cols` | Display column letters (A, B, C...) in the first row | +| `--start-row` | Starting row number (default: 1) | +| `--start-col` | Starting column number (default: 1) | + +## Features + +### Automatic Data Range Detection + +The converter automatically scans the spreadsheet to identify the actual data range (up to 100 rows and 100 columns by default), ignoring empty trailing rows and columns. + +### Cell Content Handling + +- Empty cells are rendered as blank +- Pipe characters (`|`) in cell content are escaped as `\|` +- Newlines within cells are converted to `
` tags +- All cell values are trimmed of leading/trailing whitespace + +### Multi-Sheet Support + +By default, the converter processes the active (last selected) sheet in the workbook. + +## Common Use Cases + +### 1. Data Analysis + +Extract Excel data for analysis or processing: + +```bash +python3 scripts/excel_to_markdown.py sales_data.xlsx -o sales_analysis.md +``` + +Then analyze the Markdown output or provide insights based on the structured data. + +### 2. Documentation + +Convert spreadsheets to Markdown for inclusion in documentation: + +```bash +python3 scripts/excel_to_markdown.py specifications.xlsx -o specs.md --show-rows --show-cols +``` + +The row/column numbers make it easy to reference specific cells in documentation. + +### 3. Quick Preview + +Quickly view Excel contents without opening a spreadsheet application: + +```bash +python3 scripts/excel_to_markdown.py report.xlsx +``` + +### 4. Partial Data Extraction + +Extract specific sections from large spreadsheets: + +```bash +# Get header row and first 10 data rows +python3 scripts/excel_to_markdown.py large_file.xlsx -o sample.md -r 11 +``` + +## Script Location + +The converter script is located at: `scripts/excel_to_markdown.py` + +## Dependencies + +The script requires `openpyxl` for Excel file processing: + +```bash +pip install openpyxl +``` diff --git a/skills/excel-to-markdown/scripts/excel_to_markdown.py b/skills/excel-to-markdown/scripts/excel_to_markdown.py new file mode 100644 index 0000000..d7f1149 --- /dev/null +++ b/skills/excel-to-markdown/scripts/excel_to_markdown.py @@ -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", "
") + + 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() \ No newline at end of file