增加参数控制是否使用ocr模式解析pdf
This commit is contained in:
@@ -46,6 +46,12 @@ python parser.py <file_path> [options]
|
|||||||
|--------|--------|------|
|
|--------|--------|------|
|
||||||
| `-n <num>` | `--context <num>` | 每个匹配结果包含的前后非空行数(默认:2) |
|
| `-n <num>` | `--context <num>` | 每个匹配结果包含的前后非空行数(默认:2) |
|
||||||
|
|
||||||
|
PDF 专用参数:
|
||||||
|
|
||||||
|
| 长选项 | 说明 |
|
||||||
|
|--------|------|
|
||||||
|
| `--high-res` | 启用 OCR 版面分析(需要额外依赖,处理较慢) |
|
||||||
|
|
||||||
### 退出码
|
### 退出码
|
||||||
|
|
||||||
| 退出码 | 含义 |
|
| 退出码 | 含义 |
|
||||||
@@ -178,23 +184,25 @@ uv run --with docling --with "unstructured[xlsx]" --with markdownify --with "mar
|
|||||||
|
|
||||||
### PDF
|
### PDF
|
||||||
|
|
||||||
优先级:Docling → unstructured → MarkItDown → pypdf
|
默认优先级:Docling → unstructured (fast) → MarkItDown → pypdf
|
||||||
|
|
||||||
|
`--high-res` 优先级:Docling OCR → unstructured OCR (hi_res) → Docling → unstructured (fast) → MarkItDown → pypdf
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# pip - 基础文本提取(使用 fast 策略,无需 OCR)
|
# pip - 基础文本提取(fast 策略,无需 OCR)
|
||||||
pip install docling "unstructured[pdf]"" markdownify "markitdown[pdf]" pypdf
|
pip install docling "unstructured[pdf]" markdownify "markitdown[pdf]" pypdf
|
||||||
|
|
||||||
# pip - OCR 版面分析(使用 hi_res 策略 + PaddleOCR)
|
# pip - OCR 版面分析(--high-res 所需依赖)
|
||||||
pip install docling "unstructured[pdf]" unstructured-paddleocr "paddlepaddle==2.6.2" ml-dtypes markdownify "markitdown[pdf]" pypdf
|
pip install docling "unstructured[pdf]" unstructured-paddleocr "paddlepaddle==2.6.2" ml-dtypes markdownify "markitdown[pdf]" pypdf
|
||||||
|
|
||||||
# uv - 基础文本提取
|
# uv - 基础文本提取
|
||||||
uv run --with docling --with "unstructured[pdf]" --with markdownify --with "markitdown[pdf]" --with pypdf parser.py report.pdf
|
uv run --with docling --with "unstructured[pdf]" --with markdownify --with "markitdown[pdf]" --with pypdf parser.py report.pdf
|
||||||
|
|
||||||
# uv - OCR 版面分析
|
# uv - OCR 版面分析
|
||||||
uv run --with docling --with "unstructured[pdf]" --with unstructured-paddleocr --with "paddlepaddle==2.6.2" --with ml-dtypes --with markdownify --with "markitdown[pdf]" --with pypdf parser.py report.pdf
|
uv run --with docling --with "unstructured[pdf]" --with unstructured-paddleocr --with "paddlepaddle==2.6.2" --with ml-dtypes --with markdownify --with "markitdown[pdf]" --with pypdf parser.py report.pdf --high-res
|
||||||
```
|
```
|
||||||
|
|
||||||
> PDF 无内置 XML 原生解析,至少需要安装 pypdf。unstructured 的 `hi_res` 策略需要额外安装 `unstructured-paddleocr`、`paddlepaddle==2.6.2`、`ml-dtypes`,不可用时自动回退 `fast` 策略。PaddlePaddle 必须锁定 2.x 版本,3.x 在 Windows 上有 OneDNN 兼容问题。
|
> PDF 无内置 XML 原生解析,至少需要安装 pypdf。默认模式下 Docling 不启用 OCR,unstructured 使用 fast 策略。指定 `--high-res` 后,Docling 启用 OCR,unstructured 使用 hi_res 策略配合 PaddleOCR 进行版面分析。hi_res 策略需要额外安装 `unstructured-paddleocr`、`paddlepaddle==2.6.2`、`ml-dtypes`。PaddlePaddle 必须锁定 2.x 版本,3.x 在 Windows 上有 OneDNN 兼容问题。
|
||||||
>
|
>
|
||||||
|
|
||||||
### 安装所有依赖
|
### 安装所有依赖
|
||||||
@@ -225,7 +233,7 @@ pip install "markitdown[pdf]" # PDF
|
|||||||
pip install "markitdown[docx,pptx,xlsx,pdf]" # 全部
|
pip install "markitdown[docx,pptx,xlsx,pdf]" # 全部
|
||||||
```
|
```
|
||||||
|
|
||||||
**Docling**:首次运行会自动下载 OCR/视觉模型到缓存目录,需保持网络连通。
|
**Docling**:DOCX/PPTX/XLSX 使用 SimplePipeline 直接解析 XML 结构,不涉及 OCR。PDF 默认不启用 OCR(`do_ocr=False`),指定 `--high-res` 后启用 OCR(`do_ocr=True`)。首次运行 OCR 模式会自动下载模型到缓存目录,需保持网络连通。
|
||||||
|
|
||||||
**unstructured**:需同时安装 `markdownify`。支持按文档类型安装特定 extras 以减少依赖量:
|
**unstructured**:需同时安装 `markdownify`。支持按文档类型安装特定 extras 以减少依赖量:
|
||||||
|
|
||||||
@@ -284,7 +292,7 @@ pip install "markitdown[docx,pptx,xlsx,pdf]" # 全部
|
|||||||
**XLSX** — 以 `## SheetName` 区分工作表,数据以 Markdown 表格呈现:
|
**XLSX** — 以 `## SheetName` 区分工作表,数据以 Markdown 表格呈现:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# Excel数据转换结果
|
# Excel数据转换结果 (原生XML解析)
|
||||||
|
|
||||||
## Sheet1
|
## Sheet1
|
||||||
|
|
||||||
@@ -309,9 +317,9 @@ pip install "markitdown[docx,pptx,xlsx,pdf]" # 全部
|
|||||||
|------|------|
|
|------|------|
|
||||||
| 图片移除 | 删除 `` 语法 |
|
| 图片移除 | 删除 `` 语法 |
|
||||||
| 空行规范化 | 连续多个空行合并为一个 |
|
| 空行规范化 | 连续多个空行合并为一个 |
|
||||||
| RGB 噪声过滤 | 移除 `R:255 G:128 B:0` 格式的颜色值行 |
|
| RGB 噪声过滤 | 移除 `R:255 G:128 B:0` 格式的颜色值行(仅 unstructured 解析器) |
|
||||||
| 页码噪声过滤 | 移除 `— 3 —` 格式的页码行 |
|
| 页码噪声过滤 | 移除 `— 3 —` 格式的页码行(仅 unstructured 解析器) |
|
||||||
| 页眉/页脚过滤 | unstructured 解析器自动跳过 Header/Footer 元素 |
|
| 页眉/页脚过滤 | 自动跳过 Header/Footer 元素(仅 unstructured 解析器) |
|
||||||
|
|
||||||
## 错误处理
|
## 错误处理
|
||||||
|
|
||||||
@@ -384,12 +392,14 @@ $ python parser.py report.docx -s "[invalid"
|
|||||||
|
|
||||||
### PDF
|
### PDF
|
||||||
|
|
||||||
| 解析器 | 优点 | 缺点 | 适用场景 |
|
| 解析器 | 模式 | 优点 | 缺点 | 适用场景 |
|
||||||
|---------|------|--------|---------|
|
|---------|------|------|--------|---------|
|
||||||
| **Docling** | 内置 OCR;结构化 Markdown;表格/图片占位 | 模型体积大;OCR 耗时长 | 扫描版 PDF;多语言 |
|
| **Docling** | 默认 | 结构化 Markdown;表格/图片占位 | 首次需下载模型 | 有文本层的 PDF |
|
||||||
| **unstructured** | hi_res 版面分析 + PaddleOCR;元素感知;自动回退 fast | 需额外 PaddleOCR 依赖 | 版面分析;OCR |
|
| **Docling OCR** | `--high-res` | 内置 OCR;结构化 Markdown | 模型体积大;OCR 耗时长 | 扫描版 PDF;多语言 |
|
||||||
| **MarkItDown** | 微软官方;格式规范 | 输出简洁 | 标准格式 |
|
| **unstructured** | 默认 | fast 策略;速度快 | 不做版面分析;标题不可靠 | 快速文本提取 |
|
||||||
| **pypdf** | 轻量;速度快;安装简单 | 功能简单 | 快速文本提取 |
|
| **unstructured OCR** | `--high-res` | hi_res 版面分析 + PaddleOCR;标题识别 | 需额外 PaddleOCR 依赖 | 版面分析;OCR |
|
||||||
|
| **MarkItDown** | 通用 | 微软官方;格式规范 | 输出简洁 | 标准格式 |
|
||||||
|
| **pypdf** | 通用 | 轻量;速度快;安装简单 | 功能简单 | 快速文本提取 |
|
||||||
|
|
||||||
## 常见问题
|
## 常见问题
|
||||||
|
|
||||||
@@ -399,7 +409,7 @@ $ python parser.py report.docx -s "[invalid"
|
|||||||
|
|
||||||
### PDF 文件没有标题层级?
|
### PDF 文件没有标题层级?
|
||||||
|
|
||||||
PDF 是版面描述格式,不包含语义化标题结构。Docling 或 unstructured hi_res 策略可通过版面分析识别部分标题,准确度取决于排版质量。建议用 `-s` 搜索定位内容,或用 `-c` / `-l` 了解文档规模。
|
PDF 是版面描述格式,不包含语义化标题结构。使用 `--high-res` 参数可启用 Docling OCR 或 unstructured hi_res 策略,通过版面分析识别部分标题,准确度取决于排版质量。默认模式下建议用 `-s` 搜索定位内容,或用 `-c` / `-l` 了解文档规模。
|
||||||
|
|
||||||
### 表格格式不正确?
|
### 表格格式不正确?
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,17 @@
|
|||||||
"""文档解析器命令行交互模块,提供命令行接口。支持 DOCX、PPTX、XLSX 和 PDF 文件。"""
|
"""文档解析器命令行交互模块,提供命令行接口。支持 DOCX、PPTX、XLSX 和 PDF 文件。"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
# 抑制第三方库的进度条和日志,仅保留解析结果输出
|
||||||
|
os.environ["HF_HUB_DISABLE_PROGRESS_BARS"] = "1"
|
||||||
|
os.environ["HF_HUB_DISABLE_TELEMETRY"] = "1"
|
||||||
|
os.environ["TQDM_DISABLE"] = "1"
|
||||||
|
warnings.filterwarnings("ignore")
|
||||||
|
logging.disable(logging.WARNING)
|
||||||
|
|
||||||
import common
|
import common
|
||||||
import docx_parser
|
import docx_parser
|
||||||
@@ -27,6 +36,12 @@ def main() -> None:
|
|||||||
help="与 -s 配合使用,指定每个检索结果包含的前后行数(不包含空行)",
|
help="与 -s 配合使用,指定每个检索结果包含的前后行数(不包含空行)",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--high-res",
|
||||||
|
action="store_true",
|
||||||
|
help="PDF 解析时启用 OCR 版面分析(需要额外依赖,处理较慢)",
|
||||||
|
)
|
||||||
|
|
||||||
group = parser.add_mutually_exclusive_group()
|
group = parser.add_mutually_exclusive_group()
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
"-c", "--count", action="store_true", help="返回解析后的 markdown 文档的总字数"
|
"-c", "--count", action="store_true", help="返回解析后的 markdown 文档的总字数"
|
||||||
@@ -88,12 +103,22 @@ def main() -> None:
|
|||||||
("XML 原生解析", xlsx_parser.parse_xlsx_with_xml),
|
("XML 原生解析", xlsx_parser.parse_xlsx_with_xml),
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
parsers = [
|
if args.high_res:
|
||||||
("docling", pdf_parser.parse_pdf_with_docling),
|
parsers = [
|
||||||
("unstructured", pdf_parser.parse_pdf_with_unstructured),
|
("docling OCR", pdf_parser.parse_pdf_with_docling_ocr),
|
||||||
("MarkItDown", pdf_parser.parse_pdf_with_markitdown),
|
("unstructured OCR", pdf_parser.parse_pdf_with_unstructured_ocr),
|
||||||
("pypdf", pdf_parser.parse_pdf_with_pypdf),
|
("docling", pdf_parser.parse_pdf_with_docling),
|
||||||
]
|
("unstructured", pdf_parser.parse_pdf_with_unstructured),
|
||||||
|
("MarkItDown", pdf_parser.parse_pdf_with_markitdown),
|
||||||
|
("pypdf", pdf_parser.parse_pdf_with_pypdf),
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
parsers = [
|
||||||
|
("docling", pdf_parser.parse_pdf_with_docling),
|
||||||
|
("unstructured", pdf_parser.parse_pdf_with_unstructured),
|
||||||
|
("MarkItDown", pdf_parser.parse_pdf_with_markitdown),
|
||||||
|
("pypdf", pdf_parser.parse_pdf_with_pypdf),
|
||||||
|
]
|
||||||
|
|
||||||
failures = []
|
failures = []
|
||||||
content = None
|
content = None
|
||||||
|
|||||||
@@ -3,42 +3,69 @@
|
|||||||
|
|
||||||
from typing import Optional, Tuple
|
from typing import Optional, Tuple
|
||||||
|
|
||||||
from common import _unstructured_elements_to_markdown, parse_with_docling, parse_with_markitdown
|
from common import _unstructured_elements_to_markdown, parse_with_markitdown
|
||||||
|
|
||||||
|
|
||||||
def parse_pdf_with_docling(file_path: str) -> Tuple[Optional[str], Optional[str]]:
|
def parse_pdf_with_docling(file_path: str) -> Tuple[Optional[str], Optional[str]]:
|
||||||
"""使用 docling 库解析 PDF 文件"""
|
"""使用 docling 库解析 PDF 文件(不启用 OCR)"""
|
||||||
return parse_with_docling(file_path)
|
try:
|
||||||
|
from docling.datamodel.base_models import InputFormat
|
||||||
|
from docling.datamodel.pipeline_options import PdfPipelineOptions
|
||||||
|
from docling.document_converter import DocumentConverter, PdfFormatOption
|
||||||
|
except ImportError:
|
||||||
|
return None, "docling 库未安装"
|
||||||
|
|
||||||
|
try:
|
||||||
|
converter = DocumentConverter(
|
||||||
|
format_options={
|
||||||
|
InputFormat.PDF: PdfFormatOption(
|
||||||
|
pipeline_options=PdfPipelineOptions(do_ocr=False)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
result = converter.convert(file_path)
|
||||||
|
markdown_content = result.document.export_to_markdown()
|
||||||
|
if not markdown_content.strip():
|
||||||
|
return None, "文档为空"
|
||||||
|
return markdown_content, None
|
||||||
|
except Exception as e:
|
||||||
|
return None, f"docling 解析失败: {str(e)}"
|
||||||
|
|
||||||
|
|
||||||
|
def parse_pdf_with_docling_ocr(file_path: str) -> Tuple[Optional[str], Optional[str]]:
|
||||||
|
"""使用 docling 库解析 PDF 文件(启用 OCR)"""
|
||||||
|
try:
|
||||||
|
from docling.document_converter import DocumentConverter
|
||||||
|
except ImportError:
|
||||||
|
return None, "docling 库未安装"
|
||||||
|
|
||||||
|
try:
|
||||||
|
converter = DocumentConverter()
|
||||||
|
result = converter.convert(file_path)
|
||||||
|
markdown_content = result.document.export_to_markdown()
|
||||||
|
if not markdown_content.strip():
|
||||||
|
return None, "文档为空"
|
||||||
|
return markdown_content, None
|
||||||
|
except Exception as e:
|
||||||
|
return None, f"docling OCR 解析失败: {str(e)}"
|
||||||
|
|
||||||
|
|
||||||
def parse_pdf_with_unstructured(file_path: str) -> Tuple[Optional[str], Optional[str]]:
|
def parse_pdf_with_unstructured(file_path: str) -> Tuple[Optional[str], Optional[str]]:
|
||||||
"""使用 unstructured 库解析 PDF 文件,优先 hi_res 策略配合 PaddleOCR"""
|
"""使用 unstructured 库解析 PDF 文件(fast 策略)"""
|
||||||
try:
|
try:
|
||||||
from unstructured.partition.pdf import partition_pdf
|
from unstructured.partition.pdf import partition_pdf
|
||||||
except ImportError:
|
except ImportError:
|
||||||
return None, "unstructured 库未安装"
|
return None, "unstructured 库未安装"
|
||||||
|
|
||||||
base_kwargs = {"filename": file_path, "infer_table_structure": True}
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 优先 hi_res 策略(版面分析 + PaddleOCR),失败则回退 fast
|
elements = partition_pdf(
|
||||||
try:
|
filename=file_path,
|
||||||
from unstructured.partition.utils.constants import OCR_AGENT_PADDLE
|
infer_table_structure=True,
|
||||||
|
strategy="fast",
|
||||||
elements = partition_pdf(
|
languages=["chi_sim"],
|
||||||
**base_kwargs,
|
)
|
||||||
strategy="hi_res",
|
# fast 策略不做版面分析,Title 类型标注不可靠
|
||||||
languages=["chi_sim"],
|
content = _unstructured_elements_to_markdown(elements, trust_titles=False)
|
||||||
ocr_agent=OCR_AGENT_PADDLE,
|
|
||||||
table_ocr_agent=OCR_AGENT_PADDLE,
|
|
||||||
)
|
|
||||||
trust_titles = True
|
|
||||||
except Exception:
|
|
||||||
# fast 策略不做版面分析,Title 类型标注不可靠
|
|
||||||
elements = partition_pdf(**base_kwargs, strategy="fast", languages=["chi_sim"])
|
|
||||||
trust_titles = False
|
|
||||||
|
|
||||||
content = _unstructured_elements_to_markdown(elements, trust_titles)
|
|
||||||
if not content.strip():
|
if not content.strip():
|
||||||
return None, "文档为空"
|
return None, "文档为空"
|
||||||
return content, None
|
return content, None
|
||||||
@@ -46,6 +73,37 @@ def parse_pdf_with_unstructured(file_path: str) -> Tuple[Optional[str], Optional
|
|||||||
return None, f"unstructured 解析失败: {str(e)}"
|
return None, f"unstructured 解析失败: {str(e)}"
|
||||||
|
|
||||||
|
|
||||||
|
def parse_pdf_with_unstructured_ocr(
|
||||||
|
file_path: str,
|
||||||
|
) -> Tuple[Optional[str], Optional[str]]:
|
||||||
|
"""使用 unstructured 库解析 PDF 文件(hi_res 策略 + PaddleOCR)"""
|
||||||
|
try:
|
||||||
|
from unstructured.partition.pdf import partition_pdf
|
||||||
|
except ImportError:
|
||||||
|
return None, "unstructured 库未安装"
|
||||||
|
|
||||||
|
try:
|
||||||
|
from unstructured.partition.utils.constants import OCR_AGENT_PADDLE
|
||||||
|
except ImportError:
|
||||||
|
return None, "unstructured-paddleocr 库未安装"
|
||||||
|
|
||||||
|
try:
|
||||||
|
elements = partition_pdf(
|
||||||
|
filename=file_path,
|
||||||
|
infer_table_structure=True,
|
||||||
|
strategy="hi_res",
|
||||||
|
languages=["chi_sim"],
|
||||||
|
ocr_agent=OCR_AGENT_PADDLE,
|
||||||
|
table_ocr_agent=OCR_AGENT_PADDLE,
|
||||||
|
)
|
||||||
|
content = _unstructured_elements_to_markdown(elements, trust_titles=True)
|
||||||
|
if not content.strip():
|
||||||
|
return None, "文档为空"
|
||||||
|
return content, None
|
||||||
|
except Exception as e:
|
||||||
|
return None, f"unstructured OCR 解析失败: {str(e)}"
|
||||||
|
|
||||||
|
|
||||||
def parse_pdf_with_markitdown(file_path: str) -> Tuple[Optional[str], Optional[str]]:
|
def parse_pdf_with_markitdown(file_path: str) -> Tuple[Optional[str], Optional[str]]:
|
||||||
"""使用 MarkItDown 库解析 PDF 文件"""
|
"""使用 MarkItDown 库解析 PDF 文件"""
|
||||||
return parse_with_markitdown(file_path)
|
return parse_with_markitdown(file_path)
|
||||||
|
|||||||
Reference in New Issue
Block a user