From 643daf994e8ffc16e9f14ab15c7655ae4046e69b Mon Sep 17 00:00:00 2001
From: lizhihua <501926355@qq.com>
Date: Tue, 28 Apr 2026 11:19:48 +0800
Subject: [PATCH] m
---
README_zh.md | 465 +++++++++
...06\346\236\220\346\212\245\345\221\212.md" | 909 ++++++++++++++++++
2 files changed, 1374 insertions(+)
create mode 100644 README_zh.md
create mode 100644 "\345\210\206\346\236\220\346\212\245\345\221\212.md"
diff --git a/README_zh.md b/README_zh.md
new file mode 100644
index 000000000..12ec14e76
--- /dev/null
+++ b/README_zh.md
@@ -0,0 +1,465 @@
+# MarkItDown - 中文README
+
+
+
+[](https://pypi.org/project/markitdown/)
+
+[](https://github.com/microsoft/autogen)
+
+**将各种文档格式转换为 Markdown 的轻量级 Python 工具**
+
+
+
+---
+
+## ⚠️ 重要安全提示
+
+> MarkItDown 执行 I/O 操作时具有当前进程的权限。类似于 `open()` 或 `requests.get()`,它将访问进程本身可以访问的资源。
+>
+> **请在不受信任的环境中对输入进行消毒处理,并调用最窄的 `convert_*` 函数以满足您的使用场景(例如 `convert_stream()` 或 `convert_local()`)。**
+
+详见 [安全考虑](#安全考虑) 部分。
+
+---
+
+## 简介
+
+MarkItDown 是微软开源的轻量级 Python 工具,用于将各种文件格式转换为 Markdown,专为与大语言模型(LLM)和相关文本分析管道配合使用而设计。
+
+### 为什么选择 Markdown?
+
+Markdown 极其接近纯文本,标记最少,但仍能表示重要的文档结构。主流 LLM(如 OpenAI 的 GPT-4o)原生"理解"Markdown,并且经常在回复中不加提示地使用 Markdown。这表明它们在大量 Markdown 格式的文本上进行了训练,并且理解得很好。此外,Markdown 约定也是高度 token 高效的。
+
+### 支持的格式
+
+MarkItDown 目前支持从以下格式转换:
+
+| 类别 | 支持的格式 |
+|------|-----------|
+| **办公文档** | PDF, Word (DOCX), PowerPoint (PPTX), Excel (XLSX/XLS) |
+| **网页** | HTML, Wikipedia, YouTube (字幕), Bing 搜索结果 |
+| **媒体** | 图片 (EXIF 元数据 + OCR), 音频 (EXIF 元数据 + 语音转录) |
+| **文本格式** | CSV, JSON, XML, Jupyter Notebook (IPYNB) |
+| **其他** | ZIP 文件 (遍历内容), EPUB 电子书, Outlook 消息 (MSG), RSS 订阅 |
+
+---
+
+## 前置要求
+
+MarkItDown 需要 Python 3.10 或更高版本。建议使用虚拟环境以避免依赖冲突。
+
+### 创建虚拟环境
+
+**标准 Python 安装:**
+```bash
+python -m venv .venv
+source .venv/bin/activate
+```
+
+**使用 uv:**
+```bash
+uv venv --python=3.12 .venv
+source .venv/bin/activate
+# 注意:在此虚拟环境中安装包时请使用 'uv pip install' 而非 'pip install'
+```
+
+**使用 Anaconda:**
+```bash
+conda create -n markitdown python=3.12
+conda activate markitdown
+```
+
+---
+
+## 安装
+
+### 从 PyPI 安装
+
+安装所有可选依赖(推荐):
+```bash
+pip install 'markitdown[all]'
+```
+
+或仅安装特定格式的依赖:
+```bash
+pip install 'markitdown[pdf, docx, pptx]'
+```
+
+### 从源码安装
+
+```bash
+git clone git@github.com:microsoft/markitdown.git
+cd markitdown
+pip install -e 'packages/markitdown[all]'
+```
+
+---
+
+## 使用方法
+
+### 命令行使用
+
+#### 基本转换
+
+```bash
+# 转换文件并输出到 stdout
+markitdown path-to-file.pdf
+
+# 转换文件并保存到指定文件
+markitdown path-to-file.pdf -o document.md
+
+# 使用重定向
+markitdown path-to-file.pdf > document.md
+
+# 从 stdin 读取
+cat path-to-file.pdf | markitdown
+```
+
+#### 命令行选项
+
+```bash
+# 查看版本
+markitdown --version
+
+# 查看帮助
+markitdown --help
+
+# 提供文件扩展名提示(当从 stdin 读取时)
+markitdown --extension .pdf
+
+# 提供 MIME 类型提示
+markitdown --mime-type "application/pdf"
+
+# 提供字符编码提示
+markitdown --charset UTF-8
+
+# 保留 data URI(如 base64 编码的图片)
+markitdown --keep-data-uris path-to-file.docx
+```
+
+#### 使用 Azure 文档智能
+
+```bash
+# 使用 Azure Document Intelligence 进行更精确的转换
+markitdown path-to-file.pdf -o document.md -d -e ""
+```
+
+#### 插件管理
+
+```bash
+# 列出已安装的插件
+markitdown --list-plugins
+
+# 使用插件进行转换
+markitdown --use-plugins path-to-file.pdf
+```
+
+### Python API 使用
+
+#### 基本用法
+
+```python
+from markitdown import MarkItDown
+
+md = MarkItDown(enable_plugins=False) # 设置为 True 以启用插件
+result = md.convert("test.xlsx")
+print(result.text_content)
+```
+
+#### 转换结果对象
+
+```python
+result = md.convert("document.pdf")
+
+# 获取转换后的 Markdown 内容
+print(result.markdown) # 推荐使用
+print(result.text_content) # 已弃用的别名
+
+# 获取文档标题(如果有)
+print(result.title)
+```
+
+#### 多种输入源
+
+```python
+from markitdown import MarkItDown, StreamInfo
+import io
+import requests
+
+md = MarkItDown()
+
+# 1. 本地文件路径
+result = md.convert("/path/to/document.pdf")
+result = md.convert_local("/path/to/document.pdf")
+
+# 2. URL
+result = md.convert("https://example.com/document.pdf")
+result = md.convert_uri("https://example.com/document.pdf")
+
+# 3. 文件 URI
+result = md.convert("file:///path/to/document.pdf")
+
+# 4. Data URI
+data_uri = "data:text/plain;base64,SGVsbG8sIFdvcmxkIQ=="
+result = md.convert(data_uri)
+
+# 5. requests.Response 对象
+response = requests.get("https://example.com/document.pdf")
+result = md.convert(response)
+result = md.convert_response(response)
+
+# 6. 二进制流
+with open("document.pdf", "rb") as f:
+ result = md.convert_stream(f)
+
+# 或使用 BytesIO
+pdf_data = io.BytesIO(...)
+result = md.convert_stream(
+ pdf_data,
+ stream_info=StreamInfo(extension=".pdf")
+)
+```
+
+#### 使用 LLM 进行图像描述
+
+```python
+from markitdown import MarkItDown
+from openai import OpenAI
+
+client = OpenAI()
+md = MarkItDown(
+ llm_client=client,
+ llm_model="gpt-4o",
+ llm_prompt="自定义提示词(可选)"
+)
+
+# 转换包含图片的文件,LLM 将生成图片描述
+result = md.convert("example.pptx")
+print(result.text_content)
+
+# 转换图片文件
+result = md.convert("example.jpg")
+print(result.text_content)
+```
+
+#### 使用 Azure 文档智能
+
+```python
+from markitdown import MarkItDown
+
+md = MarkItDown(docintel_endpoint="")
+result = md.convert("test.pdf")
+print(result.text_content)
+```
+
+---
+
+## 可选依赖详解
+
+MarkItDown 使用可选依赖来激活各种文件格式支持。
+
+### 可用的依赖组
+
+| 依赖组 | 包含的功能 |
+|--------|-----------|
+| `[all]` | 所有可选依赖(推荐) |
+| `[pptx]` | PowerPoint 文件支持 |
+| `[docx]` | Word 文件支持 |
+| `[xlsx]` | Excel 2007+ 文件支持 |
+| `[xls]` | 旧版 Excel 文件支持 |
+| `[pdf]` | PDF 文件支持 |
+| `[outlook]` | Outlook 消息 (.msg) 支持 |
+| `[audio-transcription]` | WAV/MP3 音频转录 |
+| `[youtube-transcription]` | YouTube 字幕获取 |
+| `[az-doc-intel]` | Azure 文档智能支持 |
+
+### 安装示例
+
+```bash
+# 仅安装 PDF 和 Word 支持
+pip install 'markitdown[pdf, docx]'
+
+# 安装所有依赖
+pip install 'markitdown[all]'
+```
+
+---
+
+## 插件系统
+
+### 什么是插件
+
+MarkItDown 支持第三方插件扩展其功能。插件通过 Python 的 `entry_points` 机制自动发现。
+
+### markitdown-ocr 插件
+
+`markitdown-ocr` 插件为 PDF、DOCX、PPTX 和 XLSX 转换器添加 OCR 支持,使用 LLM Vision 从嵌入图片中提取文本。
+
+#### 安装
+
+```bash
+pip install markitdown-ocr
+pip install openai # 或任何 OpenAI 兼容的客户端
+```
+
+#### 使用
+
+```python
+from markitdown import MarkItDown
+from openai import OpenAI
+
+md = MarkItDown(
+ enable_plugins=True,
+ llm_client=OpenAI(),
+ llm_model="gpt-4o",
+)
+result = md.convert("document_with_images.pdf")
+print(result.text_content)
+```
+
+> 如果未提供 `llm_client`,插件仍会加载,但 OCR 会被静默跳过,转而使用标准的内置转换器。
+
+### 查找和开发插件
+
+- 在 GitHub 上搜索标签 `#markitdown-plugin` 查找可用插件
+- 参考 `packages/markitdown-sample-plugin` 开发自己的插件
+
+---
+
+## Docker 使用
+
+### 构建镜像
+
+```bash
+docker build -t markitdown:latest .
+```
+
+### 运行容器
+
+```bash
+# 转换本地文件
+docker run --rm -i markitdown:latest < ~/your-file.pdf > output.md
+```
+
+---
+
+## 安全考虑
+
+### 输入消毒
+
+**不要直接将不受信任的输入传递给 MarkItDown。** 如果输入的任何部分可能由不受信任的用户或系统控制(例如在托管或服务器端应用程序中),则必须在调用 MarkItDown 之前对其进行验证和限制。
+
+根据您的环境,这可能包括:
+- 限制文件路径范围
+- 限制 URI 方案和网络目标
+- 阻止访问私有、回环、链路本地或元数据服务地址
+
+### 使用合适的 API
+
+优先选择最符合您使用场景的狭窄转换 API:
+
+| API | 用途 | 安全性 |
+|-----|------|--------|
+| `convert()` | 最宽泛,接受本地文件、远程 URL、流 | ⚠️ 最宽泛 |
+| `convert_local()` | 仅本地文件 | ⚠️ 仍需路径验证 |
+| `convert_stream()` | 仅二进制流 | ✅ 最可控 |
+| `convert_response()` | 仅 requests.Response | ✅ 您控制请求 |
+
+**示例:**
+```python
+# ❌ 危险:用户输入可能是恶意路径或 URL
+md = MarkItDown()
+result = md.convert(user_input)
+
+# ✅ 安全:使用狭窄的 API 并自己控制获取
+import requests
+
+# 对于 HTTP 资源
+response = requests.get(
+ url,
+ timeout=10,
+ allow_redirects=False # 控制重定向
+)
+result = md.convert_response(response)
+
+# 对于本地文件(先验证路径)
+import os
+
+# 确保路径在允许的目录内
+allowed_dir = "/allowed/directory"
+full_path = os.path.abspath(os.path.join(allowed_dir, user_input))
+
+if not full_path.startswith(allowed_dir):
+ raise ValueError("Invalid path")
+
+result = md.convert_local(full_path)
+```
+
+---
+
+## 故障排除
+
+### 常见问题
+
+**Q: 转换 PDF 时出现 `MissingDependencyException`?**
+
+A: 确保安装了 PDF 依赖:
+```bash
+pip install 'markitdown[pdf]'
+# 或
+pip install 'markitdown[all]'
+```
+
+**Q: 转换 DOCX 时数学公式不显示?**
+
+A: MarkItDown 支持将 OMML(Office Math Markup Language)转换为 LaTeX 格式。确保安装了完整依赖:
+```bash
+pip install 'markitdown[docx]'
+```
+
+**Q: 如何处理深度嵌套的 HTML?**
+
+A: MarkItDown 会自动处理。当 HTML 嵌套过深导致 `RecursionError` 时,它会回退到纯文本提取模式,并发出警告。
+
+**Q: 如何保留图片的 base64 编码?**
+
+A: 使用 `keep_data_uris=True` 参数:
+```python
+# Python API
+result = md.convert("document.docx", keep_data_uris=True)
+
+# 命令行
+markitdown --keep-data-uris document.docx
+```
+
+---
+
+## 项目结构
+
+```
+markitdown/
+├── packages/
+│ ├── markitdown/ # 核心包
+│ │ ├── src/markitdown/
+│ │ │ ├── _markitdown.py # 核心 MarkItDown 类
+│ │ │ ├── _base_converter.py # 转换器基类
+│ │ │ ├── converters/ # 各种格式转换器
+│ │ │ └── ...
+│ │ └── tests/ # 测试文件
+│ ├── markitdown-ocr/ # OCR 插件
+│ ├── markitdown-sample-plugin/ # 示例插件
+│ └── markitdown-mcp/ # MCP 支持
+└── README.md
+```
+
+---
+
+## 许可证
+
+本项目采用 MIT 许可证。详见 [LICENSE](LICENSE) 文件。
+
+---
+
+## 商标
+
+本项目可能包含项目、产品或服务的商标或徽标。Microsoft 商标或徽标的授权使用必须遵守 [Microsoft 商标和品牌指南](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general)。使用第三方商标或徽标需遵守这些第三方的政策。
diff --git "a/\345\210\206\346\236\220\346\212\245\345\221\212.md" "b/\345\210\206\346\236\220\346\212\245\345\221\212.md"
new file mode 100644
index 000000000..a1113d862
--- /dev/null
+++ "b/\345\210\206\346\236\220\346\212\245\345\221\212.md"
@@ -0,0 +1,909 @@
+# MarkItDown 项目分析报告
+
+## 1. 项目概述
+
+### 1.1 项目简介
+**MarkItDown** 是微软开源的一款轻量级 Python 工具,专门用于将各种文件格式转换为 Markdown 格式。该项目由 AutoGen 团队开发,主要设计用于与大语言模型(LLM)和相关文本分析管道配合使用。
+
+### 1.2 项目特点
+- **Markdown 优先**:专注于保留文档结构(标题、列表、表格、链接等)转换为 Markdown
+- **LLM 友好**:Markdown 格式非常适合 LLM 处理,因为:
+ - 接近纯文本,标记最少
+ - 主流 LLM(如 GPT-4o)原生理解 Markdown
+ - 高度 token 高效
+- **插件化架构**:支持第三方插件扩展功能
+
+### 1.3 项目许可证
+- **许可证类型**:MIT License
+- **版权方**:Microsoft Corporation
+
+---
+
+## 2. 项目架构分析
+
+### 2.1 目录结构
+
+```
+markitdown/
+├── packages/
+│ ├── markitdown/ # 核心包
+│ │ ├── src/markitdown/
+│ │ │ ├── __init__.py # 导出核心类和函数
+│ │ │ ├── __main__.py # CLI 入口
+│ │ │ ├── __about__.py # 版本信息
+│ │ │ ├── _markitdown.py # 核心 MarkItDown 类
+│ │ │ ├── _base_converter.py # 转换器基类
+│ │ │ ├── _stream_info.py # 流信息数据类
+│ │ │ ├── _exceptions.py # 异常定义
+│ │ │ ├── _uri_utils.py # URI 处理工具
+│ │ │ ├── converters/ # 各种格式转换器
+│ │ │ │ ├── __init__.py
+│ │ │ │ ├── _pdf_converter.py
+│ │ │ │ ├── _docx_converter.py
+│ │ │ │ ├── _pptx_converter.py
+│ │ │ │ ├── _xlsx_converter.py
+│ │ │ │ ├── _html_converter.py
+│ │ │ │ ├── _image_converter.py
+│ │ │ │ ├── _audio_converter.py
+│ │ │ │ ├── _zip_converter.py
+│ │ │ │ ├── _epub_converter.py
+│ │ │ │ ├── _csv_converter.py
+│ │ │ │ ├── _wikipedia_converter.py
+│ │ │ │ ├── _youtube_converter.py
+│ │ │ │ ├── _rss_converter.py
+│ │ │ │ ├── _ipynb_converter.py
+│ │ │ │ ├── _bing_serp_converter.py
+│ │ │ │ ├── _outlook_msg_converter.py
+│ │ │ │ ├── _doc_intel_converter.py
+│ │ │ │ └── _markdownify.py # 自定义 Markdown 转换
+│ │ │ └── converter_utils/ # 转换器工具
+│ │ │ └── docx/ # DOCX 特定工具
+│ │ ├── tests/ # 测试文件
+│ │ │ ├── test_files/ # 测试用文件
+│ │ │ ├── _test_vectors.py # 测试向量定义
+│ │ │ ├── test_module_vectors.py # 模块级测试
+│ │ │ ├── test_module_misc.py # 杂项模块测试
+│ │ │ ├── test_cli_vectors.py # CLI 测试
+│ │ │ └── test_cli_misc.py # 杂项 CLI 测试
+│ │ └── pyproject.toml # 包配置
+│ │
+│ ├── markitdown-ocr/ # OCR 插件包
+│ │ ├── src/markitdown_ocr/
+│ │ │ ├── __init__.py
+│ │ │ ├── __about__.py
+│ │ │ ├── _plugin.py # 插件注册
+│ │ │ ├── _ocr_service.py # OCR 服务
+│ │ │ ├── _pdf_converter_with_ocr.py
+│ │ │ ├── _docx_converter_with_ocr.py
+│ │ │ ├── _pptx_converter_with_ocr.py
+│ │ │ └── _xlsx_converter_with_ocr.py
+│ │ └── pyproject.toml
+│ │
+│ ├── markitdown-sample-plugin/ # 示例插件包
+│ │ ├── src/markitdown_sample_plugin/
+│ │ │ ├── __init__.py
+│ │ │ ├── __about__.py
+│ │ │ └── _plugin.py # RTF 转换器示例
+│ │ └── pyproject.toml
+│ │
+│ └── markitdown-mcp/ # MCP 相关包
+│ ├── src/markitdown_mcp/
+│ └── pyproject.toml
+│
+├── README.md # 主 README
+└── Dockerfile # Docker 配置
+```
+
+### 2.2 核心类关系图
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ MarkItDown (主类) │
+├─────────────────────────────────────────────────────────────────┤
+│ 属性: │
+│ - _converters: List[ConverterRegistration] # 已注册的转换器列表 │
+│ - _requests_session: requests.Session # HTTP 会话 │
+│ - _magika: magika.Magika # 文件类型检测 │
+│ - _llm_client, _llm_model, _llm_prompt # LLM 相关配置 │
+├─────────────────────────────────────────────────────────────────┤
+│ 主要方法: │
+│ - convert() # 统一转换入口 │
+│ - convert_local() # 转换本地文件 │
+│ - convert_stream() # 转换流数据 │
+│ - convert_uri() # 转换 URI (http/https/file/data) │
+│ - convert_response() # 转换 requests.Response │
+│ - register_converter() # 注册新的转换器 │
+│ - enable_builtins() # 启用内置转换器 │
+│ - enable_plugins() # 启用插件 │
+└─────────────────────────────────────────────────────────────────┘
+ │
+ │ 包含
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ ConverterRegistration (注册信息) │
+├─────────────────────────────────────────────────────────────────┤
+│ - converter: DocumentConverter # 转换器实例 │
+│ - priority: float # 优先级 (越小越高) │
+└─────────────────────────────────────────────────────────────────┘
+ │
+ │ 继承
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ DocumentConverter (转换器基类) │
+├─────────────────────────────────────────────────────────────────┤
+│ 抽象方法: │
+│ - accepts(file_stream, stream_info, **kwargs) -> bool │
+│ # 判断是否接受该文件类型 │
+│ │
+│ - convert(file_stream, stream_info, **kwargs) │
+│ -> DocumentConverterResult │
+│ # 执行转换,返回转换结果 │
+└─────────────────────────────────────────────────────────────────┘
+ │
+ ┌─────────────────────┼─────────────────────┐
+ │ │ │
+ ▼ ▼ ▼
+┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
+│ PdfConverter │ │ DocxConverter │ │ HtmlConverter │
+│ (PDF转换) │ │ (Word文档转换) │ │ (HTML转换) │
+├─────────────────┤ ├─────────────────┤ ├─────────────────┤
+│ 使用 pdfminer │ │ 使用 mammoth │ │ 使用 BeautifulSoup│
+│ 和 pdfplumber │ │ 转换为HTML │ │ 和 markdownify │
+│ 提取文本和表格 │ │ 再转Markdown │ │ 转换为Markdown │
+└─────────────────┘ └─────────────────┘ └─────────────────┘
+ │ │ │
+ └─────────────────────┼─────────────────────┘
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ DocumentConverterResult (转换结果) │
+├─────────────────────────────────────────────────────────────────┤
+│ 属性: │
+│ - markdown: str # 转换后的 Markdown 内容 │
+│ - title: Optional[str] # 文档标题 (可选) │
+│ - text_content: str # 已弃用,markdown 的别名 │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+---
+
+## 3. 数据处理流程
+
+### 3.1 整体转换流程
+
+```
+┌──────────────┐ ┌──────────────┐ ┌──────────────────┐
+│ 输入源 │───▶│ 文件类型识别 │───▶│ 选择合适转换器 │
+└──────────────┘ └──────────────┘ └──────────────────┘
+ │ │ │
+ │ │ ▼
+ │ │ ┌──────────────────┐
+ │ │ │ 执行转换 │
+ │ │ │ (Converter) │
+ │ │ └──────────────────┘
+ │ │ │
+ │ │ ▼
+ │ │ ┌──────────────────┐
+ │ │ │ 输出 Markdown │
+ │ │ └──────────────────┘
+ │ │
+ ▼ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ 输入源类型 │
+├─────────────────────────────────────────────────────────────┤
+│ 1. 本地文件路径 (str/Path) │
+│ └──▶ convert_local() │
+│ │
+│ 2. URI (http://, https://, file://, data:...) │
+│ └──▶ convert_uri() │
+│ │
+│ 3. requests.Response 对象 │
+│ └──▶ convert_response() │
+│ │
+│ 4. 二进制流 (BinaryIO) │
+│ └──▶ convert_stream() │
+└─────────────────────────────────────────────────────────────┘
+```
+
+### 3.2 文件类型识别机制
+
+MarkItDown 使用多重机制识别文件类型:
+
+#### 3.2.1 识别策略
+
+```python
+# 优先级从高到低:
+# 1. 显式提供的扩展名 (file_extension 参数)
+# 2. URL/文件名中的扩展名
+# 3. Content-Type 头部的 MIME 类型
+# 4. magika 库的内容检测 (基于文件内容)
+# 5. mimetypes 库的扩展名映射
+```
+
+#### 3.2.2 Magika 集成
+
+项目使用 Google 的 `magika` 库进行文件类型检测:
+
+```python
+# 位于 _markitdown.py:120
+self._magika = magika.Magika()
+
+# 检测逻辑 (位于 _get_stream_info_guesses 方法)
+result = self._magika.identify_stream(file_stream)
+if result.status == "ok" and result.prediction.output.label != "unknown":
+ # 获取检测到的:
+ # - mime_type: MIME 类型
+ # - extensions: 可能的扩展名列表
+ # - is_text: 是否为文本文件
+```
+
+#### 3.2.3 StreamInfo 数据结构
+
+```python
+@dataclass(kw_only=True, frozen=True)
+class StreamInfo:
+ """存储文件流的元数据信息"""
+ mimetype: Optional[str] = None # MIME 类型
+ extension: Optional[str] = None # 文件扩展名
+ charset: Optional[str] = None # 字符编码
+ filename: Optional[str] = None # 文件名
+ local_path: Optional[str] = None # 本地路径
+ url: Optional[str] = None # 来源URL
+```
+
+### 3.3 转换器选择与执行流程
+
+#### 3.3.1 转换器优先级系统
+
+```python
+# 优先级定义 (_markitdown.py:53-59)
+PRIORITY_SPECIFIC_FILE_FORMAT = 0.0 # 特定格式 (最高优先级)
+PRIORITY_GENERIC_FILE_FORMAT = 10.0 # 通用格式 (较低优先级)
+
+# 注册顺序 (后注册的同优先级转换器优先尝试)
+# 位于 enable_builtins() 方法
+self.register_converter(PlainTextConverter(), priority=PRIORITY_GENERIC_FILE_FORMAT)
+self.register_converter(ZipConverter(...), priority=PRIORITY_GENERIC_FILE_FORMAT)
+self.register_converter(HtmlConverter(), priority=PRIORITY_GENERIC_FILE_FORMAT)
+self.register_converter(RssConverter()) # priority=0.0
+self.register_converter(WikipediaConverter()) # priority=0.0
+self.register_converter(YouTubeConverter()) # priority=0.0
+self.register_converter(BingSerpConverter()) # priority=0.0
+self.register_converter(DocxConverter()) # priority=0.0
+self.register_converter(XlsxConverter()) # priority=0.0
+self.register_converter(XlsConverter()) # priority=0.0
+self.register_converter(PptxConverter()) # priority=0.0
+self.register_converter(AudioConverter()) # priority=0.0
+self.register_converter(ImageConverter()) # priority=0.0
+self.register_converter(IpynbConverter()) # priority=0.0
+self.register_converter(PdfConverter()) # priority=0.0
+self.register_converter(OutlookMsgConverter())# priority=0.0
+self.register_converter(EpubConverter()) # priority=0.0
+self.register_converter(CsvConverter()) # priority=0.0
+```
+
+#### 3.3.2 转换执行流程
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ _convert() 核心方法 │
+└─────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ 1. 按优先级排序转换器 │
+│ sorted_registrations = sorted(self._converters, │
+│ key=lambda x: x.priority) │
+└─────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ 2. 遍历所有 stream_info 猜测 + [空 StreamInfo] │
+│ for stream_info in stream_info_guesses + [StreamInfo()]: │
+└─────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ 3. 遍历每个转换器: │
+│ for converter_registration in sorted_registrations: │
+│ converter = converter_registration.converter │
+└─────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+ ┌─────────────────────────────────┐
+ │ 4. 调用 converter.accepts() │
+ │ 判断是否接受该文件类型 │
+ └─────────────────────────────────┘
+ │
+ ┌─────────────────┴─────────────────┐
+ │ │
+ ▼ ▼
+ ┌──────────────┐ ┌──────────────┐
+ │ 返回 True │ │ 返回 False │
+ │ (接受) │ │ (不接受) │
+ └──────────────┘ └──────────────┘
+ │ │
+ ▼ ▼
+┌─────────────────────────┐ 跳过此转换器
+│ 5. 调用 converter. │
+│ convert() 进行转换 │
+└─────────────────────────┘
+ │
+ ┌───────┴───────┐
+ │ │
+ ▼ ▼
+┌──────────┐ ┌──────────────┐
+│ 成功 │ │ 抛出异常 │
+│ 返回结果 │ │ 记录失败尝试 │
+└──────────┘ └──────────────┘
+ │ │
+ ▼ ▼
+┌──────────┐ 继续尝试下一个转换器
+│ 返回结果 │
+└──────────┘
+```
+
+### 3.4 异常处理机制
+
+#### 3.4.1 异常类层次结构
+
+```
+MarkItDownException (基类)
+│
+├── MissingDependencyException
+│ # 当转换器缺少可选依赖时抛出
+│ # 例如:尝试转换 PDF 但未安装 pdfminer
+│
+├── UnsupportedFormatException
+│ # 当没有找到合适的转换器时抛出
+│ # 例如:尝试转换 .xyz 未知格式文件
+│
+└── FileConversionException
+ # 当找到转换器但转换失败时抛出
+ # 包含所有失败尝试的详细信息
+ # 属性: attempts (List[FailedConversionAttempt])
+```
+
+#### 3.4.2 FailedConversionAttempt 类
+
+```python
+class FailedConversionAttempt(object):
+ """记录单次失败的转换尝试"""
+ def __init__(self, converter: Any, exc_info: Optional[tuple] = None):
+ self.converter = converter # 失败的转换器实例
+ self.exc_info = exc_info # 异常信息 (type, value, traceback)
+```
+
+---
+
+## 4. 模块协作机制
+
+### 4.1 核心模块职责
+
+| 模块 | 主要职责 | 关键文件 |
+|------|---------|---------|
+| **MarkItDown 主类** | 管理转换器注册、协调转换流程、处理输入源 | `_markitdown.py` |
+| **转换器基类** | 定义转换器接口规范 | `_base_converter.py` |
+| **流信息** | 存储文件元数据,支持类型识别 | `_stream_info.py` |
+| **异常定义** | 统一异常类型,便于错误处理 | `_exceptions.py` |
+| **URI 工具** | 解析各种 URI 格式 | `_uri_utils.py` |
+| **PDF 转换器** | 提取 PDF 文本和表格 | `_pdf_converter.py` |
+| **DOCX 转换器** | 转换 Word 文档 | `_docx_converter.py` |
+| **HTML 转换器** | 转换 HTML 为 Markdown | `_html_converter.py` |
+| **ZIP 转换器** | 递归处理压缩包内容 | `_zip_converter.py` |
+
+### 4.2 插件系统架构
+
+#### 4.2.1 插件发现机制
+
+MarkItDown 使用 Python 的 `entry_points` 机制发现插件:
+
+```python
+# 位于 pyproject.toml (插件包)
+[project.entry-points."markitdown.plugin"]
+sample_plugin = "markitdown_sample_plugin" # 示例插件
+ocr = "markitdown_ocr" # OCR 插件
+
+# 插件加载逻辑 (_markitdown.py:65-82)
+def _load_plugins() -> Union[None, List[Any]]:
+ """懒加载插件"""
+ global _plugins
+
+ if _plugins is not None:
+ return _plugins
+
+ _plugins = []
+ for entry_point in entry_points(group="markitdown.plugin"):
+ try:
+ _plugins.append(entry_point.load())
+ except Exception:
+ warn(f"Plugin '{entry_point.name}' failed to load...")
+
+ return _plugins
+```
+
+#### 4.2.2 插件接口规范
+
+插件必须实现 `register_converters` 函数:
+
+```python
+# 示例插件 (markitdown-sample-plugin/_plugin.py)
+__plugin_interface_version__ = 1 # 插件接口版本
+
+def register_converters(markitdown: MarkItDown, **kwargs):
+ """
+ 在 MarkItDown 实例构造时调用,用于注册插件提供的转换器。
+ """
+ markitdown.register_converter(RtfConverter())
+```
+
+#### 4.2.3 插件启用流程
+
+```python
+# 位于 _markitdown.py:232-250
+def enable_plugins(self, **kwargs) -> None:
+ """启用并注册插件提供的转换器"""
+ if not self._plugins_enabled:
+ # 加载插件
+ plugins = _load_plugins()
+
+ for plugin in plugins:
+ try:
+ # 调用插件的 register_converters 方法
+ plugin.register_converters(self, **kwargs)
+ except Exception:
+ warn(f"Plugin '{plugin}' failed to register converters")
+
+ self._plugins_enabled = True
+```
+
+### 4.3 转换器间协作
+
+#### 4.3.1 HTML 转换器作为中间层
+
+某些转换器将 HTML 作为中间格式:
+
+```python
+# DocxConverter (_docx_converter.py:58-83)
+def convert(self, file_stream, stream_info, **kwargs):
+ # 1. 使用 mammoth 将 DOCX 转换为 HTML
+ pre_process_stream = pre_process_docx(file_stream)
+ html_content = mammoth.convert_to_html(
+ pre_process_stream,
+ style_map=style_map
+ ).value
+
+ # 2. 复用 HtmlConverter 将 HTML 转换为 Markdown
+ return self._html_converter.convert_string(html_content, **kwargs)
+```
+
+#### 4.3.2 ZIP 转换器递归处理
+
+ZIP 转换器可以递归处理压缩包内的文件:
+
+```python
+# ZipConverter (_zip_converter.py:87-116)
+def convert(self, file_stream, stream_info, **kwargs):
+ with zipfile.ZipFile(file_stream, "r") as zipObj:
+ for name in zipObj.namelist():
+ # 读取压缩包内的文件
+ z_file_stream = io.BytesIO(zipObj.read(name))
+
+ # 构建 StreamInfo
+ z_file_stream_info = StreamInfo(
+ extension=os.path.splitext(name)[1],
+ filename=os.path.basename(name),
+ )
+
+ # 递归调用 MarkItDown 进行转换
+ # 使用 _parent_converters 中的转换器
+ result = self._markitdown.convert_stream(
+ stream=z_file_stream,
+ stream_info=z_file_stream_info,
+ )
+```
+
+---
+
+## 5. 技术依赖分析
+
+### 5.1 核心依赖 (必需)
+
+| 依赖包 | 用途 | 版本要求 |
+|--------|------|---------|
+| `beautifulsoup4` | HTML 解析和操作 | 无 |
+| `requests` | HTTP 请求 (获取远程资源) | 无 |
+| `markdownify` | HTML 转 Markdown | 无 |
+| `magika` | 文件类型检测 (Google 开源) | ~=0.6.1 |
+| `charset-normalizer` | 字符编码检测 | 无 |
+| `defusedxml` | 安全 XML 解析 | 无 |
+
+### 5.2 可选依赖 (按功能分组)
+
+| 功能组 | 依赖包 | 用途 |
+|--------|--------|------|
+| **PDF 处理** | `pdfminer.six`, `pdfplumber` | PDF 文本和表格提取 |
+| **DOCX 处理** | `mammoth`, `lxml` | Word 文档转换 |
+| **PPTX 处理** | `python-pptx` | PowerPoint 处理 |
+| **XLSX 处理** | `pandas`, `openpyxl` | Excel 2007+ 处理 |
+| **XLS 处理** | `pandas`, `xlrd` | 旧版 Excel 处理 |
+| **Outlook** | `olefile` | Outlook .msg 文件处理 |
+| **音频转录** | `pydub`, `SpeechRecognition` | WAV/MP3 语音转文本 |
+| **YouTube** | `youtube-transcript-api` | YouTube 字幕获取 |
+| **Azure 文档智能** | `azure-ai-documentintelligence`, `azure-identity` | 云端文档分析 |
+
+### 5.3 安装选项
+
+```bash
+# 安装所有可选依赖 (推荐)
+pip install 'markitdown[all]'
+
+# 仅安装特定格式的依赖
+pip install 'markitdown[pdf,docx,pptx]'
+
+# 可用的可选依赖组:
+# - [all] - 所有可选依赖
+# - [pptx] - PowerPoint
+# - [docx] - Word
+# - [xlsx] - Excel 2007+
+# - [xls] - 旧版 Excel
+# - [pdf] - PDF
+# - [outlook] - Outlook 消息
+# - [audio-transcription] - 音频转录
+# - [youtube-transcription] - YouTube 字幕
+# - [az-doc-intel] - Azure 文档智能
+```
+
+---
+
+## 6. 关键算法与实现细节
+
+### 6.1 PDF 表格提取算法
+
+#### 6.1.1 双引擎策略
+
+PDF 转换器使用两种提取策略:
+
+```python
+# 策略 1: pdfplumber (优先,更好的表格支持)
+with pdfplumber.open(pdf_bytes) as pdf:
+ for page_idx, page in enumerate(pdf.pages):
+ # 检查是否为表单/表格样式内容
+ page_content = _extract_form_content_from_words(page)
+
+ if page_content is not None:
+ # 使用位置分析提取表格
+ markdown_chunks.append(page_content)
+ else:
+ # 普通文本提取
+ text = page.extract_text()
+
+# 策略 2: pdfminer (回退,更好的文本间距)
+markdown = pdfminer.high_level.extract_text(pdf_bytes)
+```
+
+#### 6.1.2 无边界表格检测算法
+
+```python
+def _extract_form_content_from_words(page: Any) -> str | None:
+ """
+ 通过分析单词位置提取无边界表格
+ """
+ # 1. 提取所有单词及其位置
+ words = page.extract_words(keep_blank_chars=True, x_tolerance=3, y_tolerance=3)
+
+ # 2. 按 Y 坐标分组 (行)
+ y_tolerance = 5
+ rows_by_y: dict[float, list[dict]] = {}
+ for word in words:
+ y_key = round(word["top"] / y_tolerance) * y_tolerance
+ # ... 分组逻辑
+
+ # 3. 分析 X 坐标分布,识别列边界
+ all_table_x_positions = []
+ for info in row_info:
+ if info["num_columns"] >= 3 and not info["is_paragraph"]:
+ all_table_x_positions.extend(info["x_groups"])
+
+ # 4. 聚类分析确定全局列结构
+ global_columns: list[float] = []
+ for x in all_table_x_positions:
+ if not global_columns or x - global_columns[-1] > adaptive_tolerance:
+ global_columns.append(x)
+
+ # 5. 将单词分配到对应列,生成 Markdown 表格
+```
+
+### 6.2 HTML 转 Markdown 策略
+
+#### 6.2.1 自定义 Markdownify 类
+
+```python
+# 基于 markdownify 库,自定义处理逻辑
+class _CustomMarkdownify(MarkdownConverter):
+ """
+ 自定义的 HTML 转 Markdown 转换器
+ - 处理深度嵌套 HTML 的递归限制
+ - 特殊标签处理 (图片、链接、表格等)
+ """
+```
+
+#### 6.2.2 深度嵌套 HTML 的回退机制
+
+```python
+# 位于 HtmlConverter.convert() 方法
+try:
+ # 尝试使用 markdownify 进行递归转换
+ webpage_text = _CustomMarkdownify(**kwargs).convert_soup(body_elm)
+except RecursionError:
+ # 当 HTML 嵌套过深时,回退到纯文本提取
+ warnings.warn(
+ "HTML document is too deeply nested for markdown conversion "
+ "(RecursionError). Falling back to plain-text extraction."
+ )
+ # 使用 BeautifulSoup 的 get_text() 迭代提取
+ webpage_text = target.get_text("\n", strip=True)
+```
+
+### 6.3 LLM 集成机制
+
+#### 6.3.1 图像描述生成
+
+```python
+# 位于 _llm_caption.py
+def llm_caption(
+ image_stream: BinaryIO,
+ stream_info: StreamInfo,
+ client: Any, # OpenAI 兼容客户端
+ model: str, # 模型名称 (如 "gpt-4o")
+ prompt: Optional[str] = None,
+) -> str:
+ """
+ 使用 LLM Vision 模型生成图像描述
+ """
+ # 默认提示词
+ default_prompt = (
+ "Provide a concise description of the image. "
+ "Focus on details that are not obvious from the surrounding context."
+ )
+
+ # 调用 LLM API
+ response = client.chat.completions.create(
+ model=model,
+ messages=[
+ {
+ "role": "user",
+ "content": [
+ {"type": "text", "text": prompt or default_prompt},
+ {"type": "image_url", "image_url": {"url": data_url}}
+ ]
+ }
+ ],
+ max_tokens=300,
+ )
+
+ return response.choices[0].message.content
+```
+
+#### 6.3.2 PPTX 中的图像处理
+
+```python
+# 位于 PptxConverter.get_shape_content()
+if self._is_picture(shape):
+ # 1. 尝试使用 LLM 生成描述
+ llm_client = kwargs.get("llm_client")
+ llm_model = kwargs.get("llm_model")
+
+ if llm_client is not None and llm_model is not None:
+ llm_description = llm_caption(
+ image_stream,
+ image_stream_info,
+ client=llm_client,
+ model=llm_model,
+ prompt=kwargs.get("llm_prompt"),
+ )
+
+ # 2. 同时获取嵌入的 alt 文本
+ try:
+ alt_text = shape._element._nvXxPr.cNvPr.attrib.get("descr", "")
+ except Exception:
+ pass
+
+ # 3. 组合生成 Markdown 图片语法
+ alt_text = "\n".join([llm_description, alt_text]) or shape.name
+```
+
+---
+
+## 7. 安全考虑
+
+### 7.1 安全警告
+
+项目 README 明确指出:
+
+> MarkItDown 执行 I/O 操作时具有当前进程的权限。类似于 `open()` 或 `requests.get()`,它将访问进程本身可以访问的资源。
+
+### 7.2 安全建议
+
+#### 7.2.1 输入验证
+
+```python
+# ❌ 危险做法:直接传递用户输入
+md = MarkItDown()
+result = md.convert(user_input) # user_input 可能包含恶意路径
+
+# ✅ 安全做法:验证和限制输入
+# 1. 限制文件路径范围
+# 2. 限制 URI 方案
+# 3. 阻止访问私有/回环/链路本地地址
+```
+
+#### 7.2.2 使用狭窄的 API
+
+```python
+# 选择合适的转换方法
+
+# 最宽泛 (接受本地文件、远程 URL、流)
+md.convert(source)
+
+# 仅本地文件
+md.convert_local(path)
+
+# 仅流 (最可控)
+md.convert_stream(stream, stream_info=...)
+
+# 处理 HTTP 响应 (自己控制请求)
+response = requests.get(url, timeout=10)
+result = md.convert_response(response)
+```
+
+#### 7.2.3 CVE 修复示例
+
+```python
+# 测试用例 test_doc_rlink() 检测 CVE-2025-11849
+# 防止 DOCX 文件中的 rlink (关系链接) 被滥用读取任意文件
+
+# 修复策略:
+# 1. 阻止读取外部关系文件
+# 2. 不将目标文件内容嵌入输出
+```
+
+---
+
+## 8. 二次开发要点
+
+### 8.1 创建自定义转换器
+
+```python
+from markitdown import DocumentConverter, DocumentConverterResult, StreamInfo
+from typing import BinaryIO, Any
+
+class MyCustomConverter(DocumentConverter):
+ """
+ 自定义转换器示例
+ """
+
+ ACCEPTED_EXTENSIONS = [".myext"]
+ ACCEPTED_MIME_PREFIXES = ["application/my-format"]
+
+ def accepts(
+ self,
+ file_stream: BinaryIO,
+ stream_info: StreamInfo,
+ **kwargs: Any,
+ ) -> bool:
+ """判断是否接受该文件类型"""
+ mimetype = (stream_info.mimetype or "").lower()
+ extension = (stream_info.extension or "").lower()
+
+ # 检查扩展名
+ if extension in self.ACCEPTED_EXTENSIONS:
+ return True
+
+ # 检查 MIME 类型
+ for prefix in self.ACCEPTED_MIME_PREFIXES:
+ if mimetype.startswith(prefix):
+ return True
+
+ return False
+
+ def convert(
+ self,
+ file_stream: BinaryIO,
+ stream_info: StreamInfo,
+ **kwargs: Any,
+ ) -> DocumentConverterResult:
+ """执行转换"""
+ # 读取文件内容
+ content = file_stream.read()
+
+ # 执行转换逻辑...
+ markdown = self._do_convert(content)
+
+ # 返回结果
+ return DocumentConverterResult(
+ markdown=markdown,
+ title="可选标题"
+ )
+
+ def _do_convert(self, content: bytes) -> str:
+ """实际的转换逻辑"""
+ # 实现你的转换逻辑
+ return "# 转换结果\n\n..."
+```
+
+### 8.2 创建插件包
+
+```python
+# 1. 实现插件代码 (my_plugin/_plugin.py)
+from markitdown import MarkItDown
+
+__plugin_interface_version__ = 1
+
+def register_converters(markitdown: MarkItDown, **kwargs):
+ """注册插件提供的转换器"""
+ from ._my_converter import MyCustomConverter
+
+ # 注册转换器 (priority=0.0 与内置转换器同优先级)
+ markitdown.register_converter(MyCustomConverter())
+
+ # 或者使用自定义优先级
+ # markitdown.register_converter(MyCustomConverter(), priority=5.0)
+
+# 2. 配置 pyproject.toml
+[project.entry-points."markitdown.plugin"]
+my_plugin = "my_plugin"
+```
+
+### 8.3 扩展现有转换器
+
+```python
+# 例如:增强 PDF 转换器
+from markitdown.converters import PdfConverter
+from markitdown import DocumentConverterResult
+
+class EnhancedPdfConverter(PdfConverter):
+ """增强版 PDF 转换器"""
+
+ def convert(self, file_stream, stream_info, **kwargs):
+ # 调用基类转换
+ result = super().convert(file_stream, stream_info, **kwargs)
+
+ # 添加自定义后处理
+ enhanced_markdown = self._enhance_markdown(result.markdown)
+
+ return DocumentConverterResult(
+ markdown=enhanced_markdown,
+ title=result.title
+ )
+
+ def _enhance_markdown(self, markdown: str) -> str:
+ """自定义后处理逻辑"""
+ # 例如:添加额外的格式处理
+ return markdown
+```
+
+---
+
+## 9. 总结
+
+### 9.1 项目优势
+
+1. **模块化设计**:清晰的转换器接口,易于扩展
+2. **插件架构**:通过 entry_points 实现动态扩展
+3. **类型检测智能**:结合 magika、mimetypes、扩展名多重检测
+4. **LLM 集成**:支持 Vision 模型进行图像描述和 OCR
+5. **安全意识**:明确的安全警告和最佳实践建议
+
+### 9.2 技术栈特点
+
+- **纯 Python**:无需编译,易于部署
+- **可选依赖**:按需安装,减少依赖体积
+- **现代工具**:使用 hatch 构建,pyproject.toml 配置
+- **测试覆盖**:完善的测试向量和单元测试
+
+### 9.3 适用场景
+
+1. **LLM 应用**:将文档转换为 LLM 友好的 Markdown 格式
+2. **文档处理管道**:作为文档预处理步骤
+3. **RAG 系统**:构建检索增强生成系统的数据源
+4. **批量转换**:批量处理多种格式的文档