Skip to content

SolMing/extract-to-markdown

Repository files navigation

ExtractToMarkdown - 文档转换引擎

License: MIT Python 3.8+

一个功能强大且易于使用的文档转换工具,支持将多种格式的文档转换为高质量的Markdown格式。

👨‍💻 作者

Solming - 项目创建者

⚠️ 免责声明: 这是作者业余时间开发的个人项目,主要用于自用。维护时间有限,不保证及时响应Issues和PR。欢迎fork和自行修改。

🌟 项目特色

  • 🚀 多引擎支持: 集成多种转换引擎,自动选择最佳方案
  • 📄 格式丰富: 支持PDF、Word、PPT、图片等多种文档格式
  • 异步处理: 高性能异步架构,支持批量处理
  • 🔧 配置灵活: 丰富的配置选项,满足不同需求
  • ☁️ 云存储: 内置OBS云存储支持,自动上传和链接替换
  • 🛠️ 开发友好: 完善的错误处理和调试模式

📋 支持格式

文档类型 支持格式 推荐工具
PDF .pdf marker, docling
Word .docx, .doc docling
PPT .pptx, .ppt pptx2md
图片 .png, .jpg, .jpeg paddleocr

🚀 快速开始

1. 安装依赖

# 激活环境(如果使用conda)
conda activate file-extract

# 基础转换工具(选装)
pip install marker-pdf    # PDF转换(推荐)
pip install docling       # 通用转换
pip install pptx2md       # PPT转换
pip install paddleocr     # 图片OCR

2. 基础使用

import asyncio
from converter_engine import CleanConverter

async def main():
    # 创建转换器
    converter = CleanConverter()
    
    # 转换文件
    result = await converter.convert_file("document.pdf")
    
    if result.success:
        print(f"✅ 转换成功: {result.file_id}")
        print(f"📄 内容长度: {len(result.markdown_content)} 字符")
        
        # 保存结果
        with open(f"{result.file_id}.md", "w", encoding="utf-8") as f:
            f.write(result.markdown_content)
    else:
        print(f"❌ 转换失败: {result.error_message}")

# 运行
asyncio.run(main())

3. 批量转换

async def batch_convert():
    converter = CleanConverter()
    
    files = ["doc1.pdf", "doc2.docx", "slide.pptx"]
    results = await converter.convert_files(files)
    
    for result in results:
        if result.success:
            print(f"✅ {result.file_id}")
        else:
            print(f"❌ {result.file_id}: {result.error_message}")

asyncio.run(batch_convert())

⚙️ 配置选项

基本配置

from converter_engine import CleanConverter, ConversionConfig, FileManagementConfig

config = ConversionConfig(
    work_temp_dir="/tmp/converter",     # 工作目录
    log_level="INFO",                   # 日志级别
    file_management=FileManagementConfig(
        keep_work_files=False,          # 是否保留临时文件
        debug_mode=False               # 调试模式
    )
)

converter = CleanConverter(config)

OBS云存储上传

from converter_engine import ObsUploadConfig

obs_config = ObsUploadConfig(
    enabled=True,
    obs_config={
        "access_key": "your_access_key",
        "secret_key": "your_secret_key", 
        "endpoint": "obs.cn-north-4.myhuaweicloud.com",
        "bucket": "your_bucket",
        "region": "cn-north-4"
    },
    upload_types=["image"],             # 只上传图片
    replace_links=True                  # 替换Markdown中的链接
)

config = ConversionConfig(obs_upload=obs_config)
converter = CleanConverter(config)

环境变量配置

创建 .env 文件:

# 基础配置
WORK_TEMP_DIR=/tmp/converter
LOG_LEVEL=INFO
DEBUG_MODE=false

# OBS配置
OBS_UPLOAD_ENABLED=true
OBS_ACCESS_KEY=your_access_key
OBS_SECRET_KEY=your_secret_key
OBS_ENDPOINT=obs.cn-north-4.myhuaweicloud.com
OBS_BUCKET=your_bucket
OBS_REGION=cn-north-4

然后加载配置:

from converter_engine import load_config_from_env

config = load_config_from_env()
converter = CleanConverter(config)

🔧 指定转换方法

# 指定PDF转换方法
result = await converter.convert_file("document.pdf", "marker")

# 指定图片OCR方法  
result = await converter.convert_file("image.png", "paddleocr")

# 批量转换时指定不同方法
methods = {
    "doc1.pdf": "marker",
    "doc2.pdf": "docling", 
    "image.png": "paddleocr"
}
results = await converter.convert_files(files, methods)

🛠️ 调试模式

config = ConversionConfig(
    file_management=FileManagementConfig(
        debug_mode=True    # 启用调试模式,保留所有临时文件
    ),
    log_level="DEBUG"      # 详细日志
)

converter = CleanConverter(config)
result = await converter.convert_file("document.pdf")

# 调试模式下可以查看工作目录
print(f"工作目录: {result.work_dir}")

📚 详细文档

🛠️ 核心工具详解

CleanConverter 主要参数

from converter_engine import CleanConverter, ConversionConfig, FileManagementConfig, ObsUploadConfig

# 所有参数都是可选的,可以完全使用默认配置
converter = CleanConverter()  # 使用默认配置

# 或者自定义配置
config = ConversionConfig(
    work_temp_dir=None,                 # [可选] 工作目录路径,默认: 系统临时目录
    log_level="INFO",                   # [可选] 日志级别,默认: "INFO"
    file_management=FileManagementConfig(
        keep_work_files=False,          # [可选] 是否保留工作文件,默认: False
        debug_mode=False,               # [可选] 调试模式,默认: False
        keep_only_types=None,           # [可选] 只保留特定类型文件,默认: None
        custom_cleanup_func=None        # [可选] 自定义清理函数,默认: None
    ),
    obs_upload=ObsUploadConfig(
        enabled=False,                  # [可选] 是否启用OBS上传,默认: False
        obs_config=None,                # [可选] OBS配置字典,默认: None
        upload_types=["image"],         # [可选] 上传的文件类型,默认: ["image"]
        storage_prefix="md",            # [可选] 存储路径前缀,默认: "md"
        replace_links=True,             # [可选] 是否替换Markdown链接,默认: True
        remove_local_image_links=True   # [可选] 不上传时是否移除图片链接,默认: True
    )
)

converter = CleanConverter(config)

参数说明:

  • 🟢 全部可选:所有参数都有默认值,可以零配置启动
  • 🔧 智能默认:默认配置适合大多数使用场景
  • ⚙️ 渐进配置:可以只配置需要修改的部分

convert_file 方法详解

result = await converter.convert_file(
    file_path="document.pdf",           # [必需] 文件路径
    conversion_method=None,             # [可选] 指定转换方法,默认: 自动选择最佳方法
    
    # PDF专用参数(可选)
    max_pages=None,                     # [可选] 最大页数限制,默认: 无限制
    langs="Chinese,English",            # [可选] 语言设置,默认: "Chinese,English"
    
    # OCR专用参数(可选) 
    y_tolerance=10,                     # [可选] Y坐标容差,默认: 10
    paragraph_gap=30,                   # [可选] 段落间距阈值,默认: 30
    confidence_threshold=0.5,           # [可选] 置信度阈值,默认: 0.5
    
    # PPT专用参数(可选)
    disable_notes=True,                 # [可选] 是否禁用备注,默认: True
)

参数说明:

  • 🔴 file_path: 唯一必需参数,其他参数都有合理的默认值
  • 🟡 conversion_method: 不指定时自动选择可用的最佳转换器
  • 🟢 格式专用参数: 根据文件类型自动使用对应参数

转换结果字段说明

@dataclass
class ConversionResult:
    file_id: str                        # 唯一文件ID(时间戳+UUID)
    markdown_content: str               # 转换后的Markdown内容
    local_files: List[FileInfo]         # 本地生成的文件列表
    uploaded_files: List[UploadedFile]  # 上传到云端的文件列表
    success: bool                       # 转换是否成功
    error_message: Optional[str]        # 错误信息(失败时)
    work_dir: Optional[Path]            # 工作目录路径(调试时有用)

FileInfo 文件信息

@dataclass  
class FileInfo:
    file_path: Path                     # 文件完整路径
    file_id: str                        # 文件ID
    file_name: str                      # 文件名
    file_size: int                      # 文件大小(字节)
    file_type: str                      # 文件类型:"image", "document", "file"
    mime_type: str                      # MIME类型

UploadedFile 上传文件信息

@dataclass
class UploadedFile:
    file_name: str                      # 原始文件名
    file_size: int                      # 文件大小(字节)  
    upload_url: str                     # 上传后的访问URL
    upload_type: str                    # 上传类型:"obs", "local"
    object_key: Optional[str]           # 对象存储的键名

🔧 高级功能

1. 调试模式详解

# 启用调试模式
config = ConversionConfig(
    file_management=FileManagementConfig(
        debug_mode=True,                # 强制保留所有中间文件
        keep_work_files=True           # 保留工作文件
    ),
    log_level="DEBUG"                   # 详细日志输出
)

converter = CleanConverter(config)
result = await converter.convert_file("document.pdf")

if result.success:
    print(f"工作目录: {result.work_dir}")
    print(f"生成文件数: {len(result.local_files)}")
    
    # 查看所有生成的文件
    for file_info in result.local_files:
        print(f"- {file_info.file_name} ({file_info.file_type})")

调试模式特性:

  • ✅ 保留所有中间文件和临时目录
  • ✅ 输出详细的转换过程日志
  • ✅ 提供工作目录路径便于检查
  • ✅ 显示每个转换步骤的文件生成情况

2. 工作目录管理

# 方式1:配置中指定
config = ConversionConfig(work_temp_dir="/custom/work/dir")

# 方式2:环境变量
import os
os.environ['WORK_TEMP_DIR'] = '/custom/work/dir'

# 方式3:检查当前工作目录状态
from converter_engine import work_dir_config

status = work_dir_config.get_status()
print(f"工作目录: {status['base_dir']}")
print(f"可写: {status['writable']}")
print(f"子目录数: {status['subdirs_count']}")
print(f"总大小: {status['total_size_mb']} MB")

3. 图片链接处理策略

# 策略1: 上传到OBS并替换链接
config = ConversionConfig(
    obs_upload=ObsUploadConfig(
        enabled=True,
        replace_links=True,             # 替换为OBS链接
        remove_local_image_links=False
    )
)

# 策略2: 保留本地图片链接
config = ConversionConfig(
    obs_upload=ObsUploadConfig(
        enabled=False,
        remove_local_image_links=False  # 保留本地链接
    )
)

# 策略3: 移除所有图片链接
config = ConversionConfig(
    obs_upload=ObsUploadConfig(
        enabled=False,
        remove_local_image_links=True   # 移除图片链接,保留文本标记
    )
)

链接处理效果对比:

# 原始: ![图片](./images/pic1.png)

# OBS替换: ![图片](https://bucket.obs.com/md/file123/pic1.png)
# 保留本地: ![图片](./images/pic1.png)  
# 移除链接: *[图片]*

4. OBS云存储配置

obs_config = {
    "access_key": "your_access_key",        # 访问密钥
    "secret_key": "your_secret_key",        # 秘密密钥
    "endpoint": "obs.cn-north-4.myhuaweicloud.com",  # 终端点
    "bucket": "your_bucket",                # 存储桶名称
    "region": "cn-north-4"                  # 区域
}

config = ConversionConfig(
    obs_upload=ObsUploadConfig(
        enabled=True,
        obs_config=obs_config,
        upload_types=["image", "document"],  # 上传文件类型
        storage_prefix="projects/docs",      # 存储路径前缀
        replace_links=True                   # 替换Markdown链接
    )
)

converter = CleanConverter(config)
result = await converter.convert_file("document.pdf")

# 查看上传结果
for uploaded_file in result.uploaded_files:
    print(f"文件: {uploaded_file.file_name}")
    print(f"URL: {uploaded_file.upload_url}")
    print(f"大小: {uploaded_file.file_size} bytes")

OBS存储路径结构:

bucket/
├── projects/docs/20241201_123456_abc12345/  # storage_prefix/file_id/
│   ├── image1.png
│   ├── image2.jpg
│   └── document.md

🔍 检查转换器状态

converter = CleanConverter()

# 查看引擎状态
converter.print_status()

# 获取可用转换器信息
status = converter.get_converter_status()
print(status)

❓ 常见问题

转换失败怎么办?

  1. 检查依赖: 确保安装了对应格式的转换工具
  2. 启用调试: 设置 debug_mode=True 查看详细错误
  3. 尝试其他方法: 如marker失败可尝试docling

支持哪些转换方法?

  • PDF: marker(推荐), docling, mineru
  • DOCX: docling, python-docx
  • PPT: pptx2md(推荐), docling
  • 图片: paddleocr(推荐), easyocr, tesseract

如何安装转换工具?

# PDF转换(选择一个)
pip install marker-pdf     # 推荐,质量最高
pip install docling        # 通用,速度快

# PPT转换
pip install pptx2md        # 专业PPT转换

# 图片OCR(选择一个)
pip install paddleocr      # 推荐,中文支持好
pip install easyocr        # 多语言支持

🚀 项目集成迁移指南

集成到现有项目

1. 复制转换引擎

# 方式1: 直接复制整个 converter_engine 目录到你的项目
cp -r /path/to/extractToMarkdown/converter_engine /your/project/

# 方式2: 作为 Git 子模块引入
cd /your/project
git submodule add <repository_url> converter_engine

# 方式3: 安装为 Python 包(如果已打包)
pip install converter-engine

2. 安装依赖

# 激活你的项目虚拟环境
conda activate your-env  # 或 source venv/bin/activate

# 安装基础依赖
pip install asyncio pathlib dataclasses typing

# 安装转换工具依赖(按需选择)
pip install marker-pdf     # PDF转换
pip install docling        # 通用转换  
pip install pptx2md        # PPT转换
pip install paddleocr      # 图片OCR

3. 基础集成代码

# your_project/document_service.py
import asyncio
import sys
from pathlib import Path

# 添加 converter_engine 到 Python 路径
sys.path.append(str(Path(__file__).parent / "converter_engine"))

from converter_engine import CleanConverter, ConversionConfig

class DocumentService:
    def __init__(self):
        # 使用项目专用配置
        config = ConversionConfig(
            work_temp_dir=str(Path(__file__).parent / "temp"),
            log_level="INFO"
        )
        self.converter = CleanConverter(config)
    
    async def convert_document(self, file_path: str) -> dict:
        """转换文档为Markdown"""
        try:
            result = await self.converter.convert_file(file_path)
            
            return {
                "success": result.success,
                "content": result.markdown_content,
                "file_id": result.file_id,
                "error": result.error_message
            }
        except Exception as e:
            return {"success": False, "error": str(e)}

# 使用示例
async def main():
    service = DocumentService()
    result = await service.convert_document("document.pdf")
    print(result)

if __name__ == "__main__":
    asyncio.run(main())

OBS云存储集成

1. OBS依赖安装

# 安装华为云OBS SDK
pip install obs-sdk-python

# 如果你的项目已有其他云存储,可以扩展上传器

2. OBS配置集成

# config/obs_config.py
import os
from converter_engine import ConversionConfig, ObsUploadConfig

def get_obs_config():
    """从环境变量或配置文件加载OBS配置"""
    obs_config = {
        "access_key": os.getenv("HUAWEI_OBS_ACCESS_KEY"),
        "secret_key": os.getenv("HUAWEI_OBS_SECRET_KEY"), 
        "endpoint": os.getenv("HUAWEI_OBS_ENDPOINT", "obs.cn-north-4.myhuaweicloud.com"),
        "bucket": os.getenv("HUAWEI_OBS_BUCKET"),
        "region": os.getenv("HUAWEI_OBS_REGION", "cn-north-4")
    }
    
    # 检查必需配置
    if not all([obs_config["access_key"], obs_config["secret_key"], obs_config["bucket"]]):
        return None
    
    return ConversionConfig(
        obs_upload=ObsUploadConfig(
            enabled=True,
            obs_config=obs_config,
            upload_types=["image"],
            storage_prefix=f"documents/{os.getenv('PROJECT_NAME', 'default')}",
            replace_links=True
        )
    )

# 在你的服务中使用
from config.obs_config import get_obs_config

config = get_obs_config()
if config:
    converter = CleanConverter(config)
    print("✅ OBS上传已启用")
else:
    converter = CleanConverter()  # 使用默认配置
    print("⚠️ OBS未配置,使用本地模式")

3. 环境变量配置

# .env 或环境变量
# OBS配置
HUAWEI_OBS_ACCESS_KEY=your_access_key
HUAWEI_OBS_SECRET_KEY=your_secret_key  
HUAWEI_OBS_ENDPOINT=obs.cn-north-4.myhuaweicloud.com
HUAWEI_OBS_BUCKET=your_project_bucket
HUAWEI_OBS_REGION=cn-north-4
PROJECT_NAME=my_project

# 转换器配置
WORK_TEMP_DIR=/tmp/my_project_converter
LOG_LEVEL=INFO

重要注意事项

🔒 安全考虑

  1. 敏感信息管理

    # ❌ 不要硬编码敏感信息
    obs_config = {"access_key": "hardcoded_key"}
    
    # ✅ 使用环境变量或密钥管理服务
    obs_config = {"access_key": os.getenv("OBS_ACCESS_KEY")}
  2. 权限控制

    # 确保工作目录权限正确
    chmod 755 /tmp/converter
    chown app_user:app_group /tmp/converter

📁 目录结构建议

your_project/
├── converter_engine/          # 转换引擎(复制或子模块)
├── config/
│   ├── __init__.py
│   └── converter_config.py    # 转换器配置
├── services/
│   └── document_service.py    # 文档服务封装
├── temp/                      # 临时工作目录
├── requirements.txt           # 包含转换器依赖
└── .env                       # 环境变量配置

⚠️ 部署注意事项

  1. 文件权限

    # 确保应用有临时目录写权限
    mkdir -p /app/temp
    chown -R app_user:app_group /app/temp
  2. 依赖管理

    # Dockerfile 示例
    FROM python:3.9
    
    # 安装系统依赖(如果需要)
    RUN apt-get update && apt-get install -y \
        tesseract-ocr \
        libreoffice \
        && rm -rf /var/lib/apt/lists/*
    
    # 复制转换引擎
    COPY converter_engine/ /app/converter_engine/
    
    # 安装Python依赖
    COPY requirements.txt .
    RUN pip install -r requirements.txt
  3. 资源限制

    # 控制转换器并发数
    import asyncio
    
    semaphore = asyncio.Semaphore(3)  # 最多3个并发转换
    
    async def limited_convert(file_path):
        async with semaphore:
            return await converter.convert_file(file_path)
  4. 健康检查

    def health_check():
        """检查转换器健康状态"""
        try:
            converter = CleanConverter()
            status = converter.get_converter_status()
            return {"status": "healthy", "converters": status}
        except Exception as e:
            return {"status": "unhealthy", "error": str(e)}

与其他云存储集成

如果你的项目使用AWS S3、阿里云OSS等其他云存储:

# 扩展上传器(可选)
from converter_engine.uploader import BaseUploader, UploadResult

class S3Uploader(BaseUploader):
    def __init__(self, s3_config):
        self.s3_client = boto3.client('s3', **s3_config)
    
    async def upload_file(self, file_path: str, file_type: str) -> UploadResult:
        # 实现S3上传逻辑
        pass

🤝 贡献

欢迎提交Issue和Pull Request!在贡献之前,请确保:

  1. 遵循现有的代码风格
  2. 添加适当的测试
  3. 更新相关文档

📄 许可证

本项目基于 MIT License 开源。

⚠️ 重要说明:

  • 本项目的MIT许可证仅适用于项目本身的代码
  • 项目集成的第三方工具(如marker、mineru、docling等)具有各自的开源协议
  • 使用者需要遵守所有相关第三方工具的许可证要求
  • 本项目仅提供对这些工具的封装和集成,不改变其原有许可证条款

⭐ 致谢

感谢以下开源项目:

📞 联系方式

如有问题或建议,请通过GitHub联系:

⚠️ 注意: 由于这是业余项目,回复可能不及时,请谅解。建议fork项目自行修改以满足特定需求。


Made with ❤️ by Solming
async def upload_file(self, file_path, object_key=None, **kwargs): # 实现S3上传逻辑 pass
def is_available(self):
    # 检查S3是否可用
    pass

注册自定义上传器

from converter_engine.uploader import uploader_manager uploader_manager.register_uploader("s3", S3Uploader(s3_config))


## 📄 许可证

本项目采用 MIT 许可证。 

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages