batch-convert

batch-convert

熱門

使用統一的管道批次轉換多種格式的文件

316星標
70分支
更新於 2026/1/31
SKILL.md
唯讀
名稱
batch-convert
描述

使用統一的管道批次轉換多種格式的文件

版本
1.0

批次轉換技能

概述

此技能可透過統一的管道批次轉換多種格式的文件。一次轉換數百個檔案,使用一致的設定、自動格式偵測和並行處理,以達到最高效率。

使用方式

  1. 指定來源資料夾或檔案
  2. 選擇目標格式
  3. 可選:設定轉換選項
  4. 我會處理所有檔案並顯示進度

範例提示:

  • "將此資料夾中的所有 PDF 轉換為 Word 文件"
  • "批次將這些 Markdown 檔案轉換為 PDF 和 HTML"
  • "處理所有 Office 檔案並轉換為 Markdown"
  • "將此資料夾中的圖片轉換為單一 PDF"

領域知識

支援的格式矩陣

從 \ 到 DOCX PDF MD HTML PPTX
DOCX - -
PDF - -
MD -
HTML - -
XLSX - -
PPTX - -

核心管道

from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, as_completed
import subprocess
import os

class DocumentConverter:
    """統一的文件轉換管道。"""
    
    def __init__(self, max_workers=4):
        self.max_workers = max_workers
        self.converters = {
            ('md', 'docx'): self._md_to_docx,
            ('md', 'pdf'): self._md_to_pdf,
            ('md', 'html'): self._md_to_html,
            ('md', 'pptx'): self._md_to_pptx,
            ('docx', 'pdf'): self._docx_to_pdf,
            ('docx', 'md'): self._docx_to_md,
            ('pdf', 'docx'): self._pdf_to_docx,
            ('pdf', 'md'): self._pdf_to_md,
            ('xlsx', 'pdf'): self._xlsx_to_pdf,
            ('xlsx', 'md'): self._xlsx_to_md,
            ('pptx', 'pdf'): self._pptx_to_pdf,
            ('pptx', 'md'): self._pptx_to_md,
            ('html', 'md'): self._html_to_md,
            ('html', 'pdf'): self._html_to_pdf,
        }
    
    def convert(self, input_path, output_format, output_dir=None):
        """將單一檔案轉換為目標格式。"""
        input_path = Path(input_path)
        input_format = input_path.suffix[1:].lower()
        
        if output_dir:
            output_path = Path(output_dir) / f"{input_path.stem}.{output_format}"
        else:
            output_path = input_path.with_suffix(f".{output_format}")
        
        converter_key = (input_format, output_format)
        if converter_key not in self.converters:
            raise ValueError(f"不支援的轉換:{input_format} -> {output_format}")
        
        converter = self.converters[converter_key]
        return converter(input_path, output_path)
    
    def batch_convert(self, input_dir, output_format, output_dir=None, 
                      file_pattern="*", recursive=False):
        """批次轉換所有符合條件的檔案。"""
        input_path = Path(input_dir)
        output_path = Path(output_dir) if output_dir else input_path / "converted"
        output_path.mkdir(exist_ok=True)
        
        # 尋找檔案
        if recursive:
            files = list(input_path.rglob(file_pattern))
        else:
            files = list(input_path.glob(file_pattern))
        
        # 過濾出支援的格式
        supported_ext = ['.md', '.docx', '.pdf', '.xlsx', '.pptx', '.html']
        files = [f for f in files if f.suffix.lower() in supported_ext]
        
        results = []
        
        with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
            future_to_file = {
                executor.submit(self.convert, f, output_format, output_path): f
                for f in files
            }
            
            for future in as_completed(future_to_file):
                file = future_to_file[future]
                try:
                    result = future.result()
                    results.append({'file': str(file), 'status': 'success', 'output': str(result)})
                except Exception as e:
                    results.append({'file': str(file), 'status': 'error', 'error': str(e)})
        
        return results

轉換器實作

# Markdown 轉換(使用 Pandoc)
def _md_to_docx(self, input_path, output_path):
    subprocess.run(['pandoc', str(input_path), '-o', str(output_path)], check=True)
    return output_path

def _md_to_pdf(self, input_path, output_path):
    subprocess.run(['pandoc', str(input_path), '-o', str(output_path)], check=True)
    return output_path

def _md_to_html(self, input_path, output_path):
    subprocess.run(['pandoc', str(input_path), '-s', '-o', str(output_path)], check=True)
    return output_path

def _md_to_pptx(self, input_path, output_path):
    subprocess.run(['marp', str(input_path), '-o', str(output_path)], check=True)
    return output_path

# Office 轉 Markdown(使用 markitdown)
def _docx_to_md(self, input_path, output_path):
    from markitdown import MarkItDown
    md = MarkItDown()
    result = md.convert(str(input_path))
    with open(output_path, 'w') as f:
        f.write(result.text_content)
    return output_path

def _xlsx_to_md(self, input_path, output_path):
    from markitdown import MarkItDown
    md = MarkItDown()
    result = md.convert(str(input_path))
    with open(output_path, 'w') as f:
        f.write(result.text_content)
    return output_path

def _pptx_to_md(self, input_path, output_path):
    from markitdown import MarkItDown
    md = MarkItDown()
    result = md.convert(str(input_path))
    with open(output_path, 'w') as f:
        f.write(result.text_content)
    return output_path

# PDF 轉換
def _pdf_to_docx(self, input_path, output_path):
    from pdf2docx import Converter
    cv = Converter(str(input_path))
    cv.convert(str(output_path))
    cv.close()
    return output_path

def _pdf_to_md(self, input_path, output_path):
    from markitdown import MarkItDown
    md = MarkItDown()
    result = md.convert(str(input_path))
    with open(output_path, 'w') as f:
        f.write(result.text_content)
    return output_path

# Office 轉 PDF(使用 LibreOffice)
def _docx_to_pdf(self, input_path, output_path):
    subprocess.run([
        'soffice', '--headless', '--convert-to', 'pdf',
        '--outdir', str(output_path.parent), str(input_path)
    ], check=True)
    return output_path

def _xlsx_to_pdf(self, input_path, output_path):
    subprocess.run([
        'soffice', '--headless', '--convert-to', 'pdf',
        '--outdir', str(output_path.parent), str(input_path)
    ], check=True)
    return output_path

def _pptx_to_pdf(self, input_path, output_path):
    subprocess.run([
        'soffice', '--headless', '--convert-to', 'pdf',
        '--outdir', str(output_path.parent), str(input_path)
    ], check=True)
    return output_path

進度追蹤

from tqdm import tqdm

def batch_convert_with_progress(converter, input_dir, output_format, output_dir=None):
    """批次轉換並顯示進度條。"""
    input_path = Path(input_dir)
    files = list(input_path.glob('*'))
    
    results = []
    for file in tqdm(files, desc=f"正在轉換為 {output_format}"):
        try:
            result = converter.convert(file, output_format, output_dir)
            results.append({'file': str(file), 'status': 'success'})
        except Exception as e:
            results.append({'file': str(file), 'status': 'error', 'error': str(e)})
    
    return results

最佳實務

  1. 先測試樣本:在批次處理前先轉換幾個檔案
  2. 檢查磁碟空間:確保有足夠空間存放輸出
  3. 使用並行處理:透過多個工作執行緒加速
  4. 優雅處理錯誤:記錄失敗,繼續處理
  5. 驗證輸出:抽查轉換後的檔案

常見模式

格式偵測管道

def detect_and_convert(file_path, target_format):
    """自動偵測格式並轉換。"""
    import mimetypes
    
    mime_type, _ = mimetypes.guess_type(str(file_path))
    
    format_map = {
        'application/pdf': 'pdf',
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',
        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',
        'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'pptx',
        'text/markdown': 'md',
        'text/html': 'html',
    }
    
    source_format = format_map.get(mime_type, Path(file_path).suffix[1:])
    
    converter = DocumentConverter()
    return converter.convert(file_path, target_format)

多格式輸出

def convert_to_multiple_formats(input_file, output_formats, output_dir):
    """將一個檔案轉換為多種格式。"""
    converter = DocumentConverter()
    results = {}
    
    for fmt in output_formats:
        try:
            output = converter.convert(input_file, fmt, output_dir)
            results[fmt] = {'status': 'success', 'path': str(output)}
        except Exception as e:
            results[fmt] = {'status': 'error', 'error': str(e)}
    
    return results

# 將 README 轉換為多種格式
results = convert_to_multiple_formats(
    'README.md',
    ['docx', 'pdf', 'html'],
    './exports'
)

範例

範例 1:文件匯出

from pathlib import Path
import json

def export_documentation(docs_dir, export_dir):
    """將所有文件匯出為多種格式。"""
    
    converter = DocumentConverter(max_workers=8)
    docs_path = Path(docs_dir)
    export_path = Path(export_dir)
    
    # 建立格式資料夾
    for fmt in ['pdf', 'docx', 'html']:
        (export_path / fmt).mkdir(parents=True, exist_ok=True)
    
    all_results = {}
    
    # 尋找所有 Markdown 檔案
    md_files = list(docs_path.rglob('*.md'))
    
    for md_file in md_files:
        file_results = {}
        
        for fmt in ['pdf', 'docx', 'html']:
            output_dir = export_path / fmt
            try:
                output = converter.convert(md_file, fmt, output_dir)
                file_results[fmt] = 'success'
            except Exception as e:
                file_results[fmt] = f'error: {e}'
        
        all_results[str(md_file)] = file_results
        print(f"已處理:{md_file.name}")
    
    # 儲存報告
    with open(export_path / 'export_report.json', 'w') as f:
        json.dump(all_results, f, indent=2)
    
    return all_results

results = export_documentation('./docs', './exports')

範例 2:舊版文件遷移

def migrate_legacy_docs(source_dir, target_dir):
    """將舊版文件遷移至現代格式。"""
    
    converter = DocumentConverter(max_workers=4)
    
    # 遷移規則
    migrations = [
        ('*.doc', 'docx'),   # 舊版 Word 轉新版
        ('*.xls', 'xlsx'),   # 舊版 Excel 轉新版
        ('*.ppt', 'pptx'),   # 舊版 PowerPoint 轉新版
        ('*.rtf', 'docx'),   # RTF 轉 Word
    ]
    
    source_path = Path(source_dir)
    target_path = Path(target_dir)
    target_path.mkdir(exist_ok=True)
    
    total_migrated = 0
    errors = []
    
    for pattern, target_format in migrations:
        files = list(source_path.glob(pattern))
        
        for file in files:
            try:
                # 使用 LibreOffice 處理舊版格式
                subprocess.run([
                    'soffice', '--headless',
                    '--convert-to', target_format,
                    '--outdir', str(target_path),
                    str(file)
                ], check=True)
                
                total_migrated += 1
                print(f"已遷移:{file.name}")
                
            except Exception as e:
                errors.append({'file': str(file), 'error': str(e)})
    
    print(f"\n遷移完成:{total_migrated} 個檔案")
    print(f"錯誤:{len(errors)} 個")
    
    return {'migrated': total_migrated, 'errors': errors}

範例 3:報告產生管道

def generate_reports_pipeline(data_files, template_dir, output_dir):
    """從資料檔案使用範本產生報告。"""
    
    from datetime import datetime
    
    converter = DocumentConverter()
    output_path = Path(output_dir)
    output_path.mkdir(exist_ok=True)
    
    timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
    
    reports = []
    
    for data_file in data_files:
        # 載入資料
        data_path = Path(data_file)
        
        # 產生 Markdown 報告
        md_content = f"""---
title: 報告 - {data_path.stem}
date: {datetime.now().strftime('%Y-%m-%d')}
---

# {data_path.stem} 報告

產生時間:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}

## 資料摘要

"""
        
        # 加入資料內容(簡化)
        if data_path.suffix == '.xlsx':
            from markitdown import MarkItDown
            md = MarkItDown()
            result = md.convert(str(data_path))
            md_content += result.text_content
        
        # 儲存 Markdown
        md_file = output_path / f"{data_path.stem}_{timestamp}.md"
        with open(md_file, 'w') as f:
            f.write(md_content)
        
        # 轉換為 PDF 和 DOCX
        for fmt in ['pdf', 'docx']:
            try:
                output = converter.convert(md_file, fmt, output_path)
                reports.append({'source': str(data_file), 'output': str(output), 'format': fmt})
            except Exception as e:
                print(f"轉換 {data_file} 為 {fmt} 時發生錯誤:{e}")
    
    return reports

限制

  • 部分格式組合不支援
  • 複雜格式可能在轉換中遺失
  • 大型檔案可能需要更多時間
  • 部分轉換需要外部工具(LibreOffice、Pandoc)
  • 品質因來源文件複雜度而異

安裝

# 核心相依套件
pip install pdf2docx markitdown python-docx openpyxl

# Pandoc(用於 MD 轉換)
brew install pandoc  # macOS
apt install pandoc   # Ubuntu

# Marp(用於 PPTX)
npm install -g @marp-team/marp-cli

# LibreOffice(用於 Office 格式)
brew install libreoffice  # macOS
apt install libreoffice   # Ubuntu

資源