1. 集成概述
Dify 集成多种文档处理和网页爬取服务,用于从各类文档和网页中提取文本内容,构建知识库。
核心价值
- 多格式支持: PDF、Word、Excel、HTML、Markdown 等
- 网页爬取: 支持 Firecrawl、Jina Reader、WaterCrawl 等服务
- 文档解析: 本地解析和第三方服务解析
- 内容清洗: 提取主要内容,去除噪音
2. 支持的服务/产品
2.1 文档格式支持
| 格式 | 状态 | 解析方式 | 说明 |
|---|---|---|---|
| ✅ 支持 | pypdfium2 | 本地 PDF 解析 | |
| Word (docx) | ✅ 支持 | python-docx | Word 文档解析 |
| Excel (xlsx) | ✅ 支持 | openpyxl | Excel 表格解析 |
| CSV | ✅ 支持 | csv module | CSV 文件解析 |
| Markdown | ✅ 支持 | markdown-it-py | Markdown 解析 |
| HTML | ✅ 支持 | BeautifulSoup | HTML 解析 |
| Text | ✅ 支持 | Native | 纯文本文件 |
| Notion | ✅ 支持 | Notion API | Notion 页面导入 |
2.2 网页爬取服务
| 服务名称 | 状态 | 说明 |
|---|---|---|
| Firecrawl | ✅ 支持 | 高质量网页爬取和转换为 Markdown |
| Jina Reader | ✅ 支持 | LLM 友好的网页内容提取 |
| WaterCrawl | ✅ 支持 | 网页爬取服务 |
| 内置爬虫 | ✅ 支持 | 基于 BeautifulSoup 的简单爬虫 |
2.3 文档处理服务
| 服务名称 | 状态 | 说明 |
|---|---|---|
| Unstructured.io | ✅ 支持 | 企业级文档解析服务 |
| Azure Blob Extractor | ✅ 支持 | Azure 文档智能服务 |
| 本地处理 | ✅ 支持 | 基于 Python 库的本地处理 |
3. 集成方式
3.1 架构设计
┌─────────────────────────────────────────────────────┐
│ Document Processing │
├─────────────────────────────────────────────────────┤
│ Extract Processor │
│ ┌───────────────────────────────────────────────┐ │
│ │ - 选择合适的提取器 │ │
│ │ - 协调提取流程 │ │
│ │ - 内容后处理 │ │
│ └───────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────┤
│ Extractor Layer │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ PDF │ │ Word │ │ Excel │ │
│ │Extractor │ │Extractor │ │Extractor │ ... │
│ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │Firecrawl │ │ Jina │ │ HTML │ │
│ │Extractor │ │Extractor │ │Extractor │ ... │
│ └──────────┘ └──────────┘ └──────────┘ │
├─────────────────────────────────────────────────────┤
│ External Services (Optional) │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Unstructured │ │ Firecrawl │ │
│ │ Service │ │ Service │ ... │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────┘
3.2 配置方式
bash
# Unstructured.io 配置
RAG_ETL_TYPE=Unstructured
UNSTRUCTURED_API_URL=https://api.unstructured.io/general/v0/general
UNSTRUCTURED_API_KEY=your_api_key
# Firecrawl 配置
FIRECRAWL_API_KEY=your_api_key
FIRECRAWL_API_URL=https://api.firecrawl.dev
# Jina Reader 配置
JINA_READER_API_KEY=your_api_key
4. 代码实现
4.1 核心代码路径
📌 以下路径均为项目实际文件结构
api/core/rag/extractor/
├── extractor_base.py # 提取器基类
├── extract_processor.py # 提取处理器
├── pdf_extractor.py # PDF 提取器
├── word_extractor.py # Word 提取器
├── excel_extractor.py # Excel 提取器
├── csv_extractor.py # CSV 提取器
├── markdown_extractor.py # Markdown 提取器
├── html_extractor.py # HTML 提取器
├── text_extractor.py # 文本提取器
├── notion_extractor.py # Notion 提取器
├── jina_reader_extractor.py # Jina Reader 提取器
├── firecrawl/ # Firecrawl 提取器
│ ├── firecrawl_app.py # Firecrawl API 客户端
│ └── firecrawl_web_extractor.py # Firecrawl 网页提取器
├── watercrawl/ # WaterCrawl 提取器
│ └── extractor.py # WaterCrawl 网页提取器
├── unstructured/ # Unstructured 提取器
│ ├── unstructured_doc_extractor.py # Word 文档提取
│ ├── unstructured_eml_extractor.py # EML 邮件提取
│ ├── unstructured_epub_extractor.py # EPUB 提取
│ ├── unstructured_markdown_extractor.py # Markdown 提取
│ ├── unstructured_msg_extractor.py # MSG 邮件提取
│ ├── unstructured_pptx_extractor.py # PPTX 提取
│ ├── unstructured_ppt_extractor.py # PPT 提取
│ └── unstructured_xml_extractor.py # XML 提取
├── blob/ # Blob 处理
│ └── blob.py
└── entity/ # 实体定义
└── extract_setting.py
4.2 提取器基类
📌 源代码引用 : api/core/rag/extractor/extractor_base.py
python
# api/core/rag/extractor/extractor_base.py - 源代码引用
class BaseExtractor(ABC):
"""Interface for extract files."""
@abstractmethod
def extract(self):
raise NotImplementedError
5. 使用示例
5.1 PDF 提取
⚠️ 概念性示例 : 以下代码为演示 PDF 提取的概念性示例,可参考源代码 api/core/rag/extractor/pdf_extractor.py
python
from core.rag.extractor.pdf_extractor import PdfExtractor
# 提取 PDF 内容
extractor = PdfExtractor(
file_path="/path/to/document.pdf",
file_cache_key="cache_key" # 可选,用于缓存
)
documents = extractor.extract()
for doc in documents:
print(f"Page {doc.metadata['page']}: {doc.page_content}")
5.2 Word 提取
⚠️ 概念性示例 : 以下代码为演示 Word 提取的概念性示例,可参考源代码 api/core/rag/extractor/word_extractor.py
python
from core.rag.extractor.word_extractor import WordExtractor
# 注意:WordExtractor 需要 tenant_id 和 user_id 参数
extractor = WordExtractor(
file_path="/path/to/document.docx",
tenant_id="your_tenant_id",
user_id="your_user_id"
)
documents = extractor.extract()
5.3 Excel 提取
⚠️ 概念性示例 : 以下代码为演示 Excel 提取的概念性示例,可参考源代码 api/core/rag/extractor/excel_extractor.py
python
from core.rag.extractor.excel_extractor import ExcelExtractor
extractor = ExcelExtractor(file_path="/path/to/spreadsheet.xlsx")
documents = extractor.extract()
# Excel 每行会成为一个 document
for doc in documents:
print(f"Content: {doc.page_content}")
print(f"Source: {doc.metadata['source']}")
5.4 Firecrawl 网页爬取
⚠️ 概念性示例 : 以下代码为演示 Firecrawl 使用的概念性示例,可参考源代码 api/core/rag/extractor/firecrawl/firecrawl_web_extractor.py
python
from core.rag.extractor.firecrawl.firecrawl_web_extractor import FirecrawlWebExtractor
# 爬取单个页面
extractor = FirecrawlWebExtractor(
url="https://example.com",
job_id="job_123",
tenant_id="tenant_id",
mode="scrape", # 或 "crawl"
only_main_content=True
)
documents = extractor.extract()
5.5 Jina Reader 网页提取
⚠️ 概念性示例 : 以下代码为演示 Jina Reader 使用的概念性示例,可参考源代码 api/core/rag/extractor/jina_reader_extractor.py
python
from core.rag.extractor.jina_reader_extractor import JinaReaderWebExtractor
extractor = JinaReaderWebExtractor(
url="https://example.com",
job_id="job_123",
tenant_id="tenant_id",
mode="crawl"
)
documents = extractor.extract()
5.6 HTML 提取
⚠️ 概念性示例 : 以下代码为演示 HTML 提取的概念性示例,可参考源代码 api/core/rag/extractor/html_extractor.py
python
from core.rag.extractor.html_extractor import HtmlExtractor
extractor = HtmlExtractor(file_path="/path/to/page.html")
documents = extractor.extract()
5.7 Notion 导入
⚠️ 概念性示例 : 以下代码为演示 Notion 导入的概念性示例,可参考源代码 api/core/rag/extractor/notion_extractor.py
python
from core.rag.extractor.notion_extractor import NotionExtractor
extractor = NotionExtractor(
notion_workspace_id="workspace_id",
notion_obj_id="page_id",
notion_page_type="page", # 或 "database"
tenant_id="tenant_id",
# 可选参数:
# document_model=document_model,
# notion_access_token="token",
# credential_id="credential_id"
)
documents = extractor.extract()
5.8 使用 Unstructured 服务
⚠️ 概念性示例 : 以下代码为演示 Unstructured 服务使用的概念性示例,可参考源代码 api/core/rag/extractor/unstructured/
python
# Unstructured 提取器示例(使用 Word 文档)
from core.rag.extractor.unstructured.unstructured_doc_extractor import UnstructuredWordExtractor
extractor = UnstructuredWordExtractor(
file_path="/path/to/document.docx",
api_url="https://api.unstructured.io/general/v0/general",
api_key="your_api_key"
)
documents = extractor.extract()
6. 错误处理
⚠️ 概念性示例: 本节所有代码为演示错误处理模式的概念性示例,非项目源代码
6.1 文件格式错误
python
try:
extractor = PdfExtractor(file_path=file_path)
documents = extractor.extract()
except UnsupportedFileTypeError as e:
logger.error(f"Unsupported file type: {e}")
except FileNotFoundError as e:
logger.error(f"File not found: {e}")
6.2 网页爬取错误
python
try:
extractor = FirecrawlWebExtractor(url=url, ...)
documents = extractor.extract()
except requests.exceptions.Timeout:
logger.error("Firecrawl request timeout")
except requests.exceptions.RequestException as e:
logger.error(f"Firecrawl error: {e}")
7. 性能优化
⚠️ 概念性示例: 本节所有代码为演示性能优化模式的概念性示例,非项目源代码
7.1 文件缓存
python
from extensions.ext_storage import storage
# 缓存提取的文本
def extract_with_cache(file_path: str, cache_key: str):
# 检查缓存
try:
cached_text = storage.load(cache_key).decode('utf-8')
return [Document(page_content=cached_text)]
except FileNotFoundError:
pass
# 提取
extractor = PdfExtractor(file_path=file_path)
documents = extractor.extract()
# 保存缓存
text = "\n\n".join([doc.page_content for doc in documents])
storage.save(cache_key, text.encode('utf-8'))
return documents
7.2 批量处理
python
from concurrent.futures import ThreadPoolExecutor
def batch_extract(file_paths: list[str], max_workers: int = 5):
"""批量提取文档"""
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = []
for file_path in file_paths:
extractor = get_extractor_for_file(file_path)
future = executor.submit(extractor.extract)
futures.append(future)
results = [future.result() for future in futures]
return results
7.3 流式处理大文件
python
def extract_large_pdf_stream(file_path: str):
"""流式处理大型 PDF"""
extractor = PdfExtractor(file_path=file_path)
# 逐页处理
for document in extractor.load():
yield document
# 可以在这里进行后处理或存储
8. 监控与日志
⚠️ 概念性示例: 本节所有代码为演示监控与日志模式的概念性示例,非项目源代码
python
import time
import logging
logger = logging.getLogger(__name__)
def extract_with_logging(extractor, file_path: str):
"""带日志的提取"""
start_time = time.time()
try:
logger.info(f"Starting extraction: {file_path}")
documents = extractor.extract()
end_time = time.time()
logger.info(
f"Extraction completed: {file_path}, "
f"documents: {len(documents)}, "
f"time: {end_time - start_time:.2f}s"
)
return documents
except Exception as e:
logger.error(f"Extraction failed: {file_path}, error: {e}")
raise
9. 测试
⚠️ 概念性示例: 本节所有代码为演示测试模式的概念性示例,非项目源代码
python
import pytest
from core.rag.extractor.pdf_extractor import PdfExtractor
def test_pdf_extraction():
"""测试 PDF 提取"""
extractor = PdfExtractor(file_path="test.pdf")
documents = extractor.extract()
assert len(documents) > 0
assert documents[0].page_content
assert 'page' in documents[0].metadata
10. 扩展新提取器
⚠️ 概念性示例: 本节所有代码为演示如何扩展新提取器的概念性示例,非项目源代码
10.1 实现提取器类
python
from core.rag.extractor.extractor_base import BaseExtractor
from core.rag.models.document import Document
class MyExtractor(BaseExtractor):
"""自定义提取器"""
def __init__(self, file_path: str, **kwargs):
self.file_path = file_path
def extract(self) -> list[Document]:
"""实现提取逻辑"""
# 读取文件
with open(self.file_path, 'r') as f:
content = f.read()
# 处理内容
processed_content = self.process(content)
# 返回文档
return [Document(
page_content=processed_content,
metadata={'source': self.file_path}
)]
def process(self, content: str) -> str:
"""内容处理逻辑"""
# 实现特定的处理逻辑
return content
10.2 注册提取器
python
# 在 extract_processor.py 中注册
EXTRACTOR_MAP = {
'.pdf': PdfExtractor,
'.docx': WordExtractor,
'.xlsx': ExcelExtractor,
'.my': MyExtractor, # 添加新提取器
}
11. 最佳实践
11.1 文件类型检测
- 使用 MIME 类型检测而非文件扩展名
- 验证文件大小限制
- 检查文件内容完整性
11.2 内容清洗
- 去除多余空白字符
- 统一换行符
- 移除不可见字符
- 提取主要内容区域(网页)
11.3 错误处理
- 捕获并记录所有异常
- 提供降级方案
- 返回有意义的错误信息
11.4 性能考虑
- 大文件分块处理
- 使用缓存减少重复解析
- 异步处理多个文件
- 限制并发数量