Files
lyxy-document/lyxy_document_reader.py
lanyuanxiaoyao 750ef50a8d refactor: 重构解析器架构并添加编码检测和配置管理
简化 parse_input() 为纯调度器,通过遍历 readers 的 supports() 方法识别输入类型,移除 URL 特殊处理和文件检查逻辑。各 reader 的 parse() 方法负责完整验证(文件存在、格式有效性)。

新增功能:
- 添加 chardet 编码自动检测,支持多种中文编码回退机制
- 创建统一配置类管理编码、下载超时、日志等级等配置项
- HTML reader 支持本地文件编码检测和 URL 统一处理

安全性改进:
- 修复 safe_open_zip() 路径遍历漏洞,使用 pathlib 规范化路径
- 添加边界检查,search_markdown() 检查负数参数

其他改进:
- 修复类型注解(argparse.Namespace)
- 日志系统仅输出 ERROR 级别,避免干扰 Markdown 输出
- 更新 BaseReader 接口文档,明确 supports() 和 parse() 职责划分
- 同步 delta specs 到主 specs(document-reading、html-reader、configuration、encoding-detection)
2026-03-08 16:33:40 +08:00

101 lines
2.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""文档解析器命令行交互模块,提供命令行接口。支持 DOCX、PPTX、XLSX、PDF、HTML 和 URL。"""
import argparse
import logging
import os
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")
# 配置日志系统,只输出 ERROR 级别
logging.basicConfig(level=logging.ERROR, format='%(levelname)s: %(message)s')
# 设置第三方库日志等级
logging.getLogger('docling').setLevel(logging.ERROR)
logging.getLogger('unstructured').setLevel(logging.ERROR)
from core import (
FileDetectionError,
ReaderNotFoundError,
output_result,
parse_input,
process_content,
)
from readers import READERS
def main() -> None:
parser = argparse.ArgumentParser(
description="将 DOCX、PPTX、XLSX、PDF、HTML 文件或 URL 解析为 Markdown"
)
parser.add_argument("input_path", help="DOCX、PPTX、XLSX、PDF、HTML 文件或 URL")
parser.add_argument(
"-n",
"--context",
type=int,
default=2,
help="与 -s 配合使用,指定每个检索结果包含的前后行数(不包含空行)",
)
group = parser.add_mutually_exclusive_group()
group.add_argument(
"-c", "--count", action="store_true", help="返回解析后的 markdown 文档的总字数"
)
group.add_argument(
"-l", "--lines", action="store_true", help="返回解析后的 markdown 文档的总行数"
)
group.add_argument(
"-t",
"--titles",
action="store_true",
help="返回解析后的 markdown 文档的标题行1-6级",
)
group.add_argument(
"-tc",
"--title-content",
help="指定标题名称,输出该标题及其下级内容(不包含#号)",
)
group.add_argument(
"-s",
"--search",
help="使用正则表达式搜索文档,返回所有匹配结果(用---分隔)",
)
args = parser.parse_args()
# 实例化所有 readers
readers = [ReaderCls() for ReaderCls in READERS]
try:
content, failures = parse_input(args.input_path, readers)
except FileDetectionError as e:
print(f"错误: {e}")
sys.exit(1)
except ReaderNotFoundError as e:
print(f"错误: {e}")
sys.exit(1)
if content is None:
print("所有解析方法均失败:")
for failure in failures:
print(failure)
sys.exit(1)
# 处理内容
content = process_content(content)
# 输出结果
output_result(content, args)
if __name__ == "__main__":
main()