Skip to content

Unstructured Loader

Unstructured Loader

UnstructuredLoader

Bases: BaseLoader

任意类型的文档读取。子类需要明确定义cls.mime_types类型

Notes

注意使用Unstructured库对文档进行读取与分片后,不可以使用ele.id作为数据库主键使用。因为其存在重复的可能性。

partition_kwargs property

partition_kwargs: dict

用户进行分片调用时的一些特殊配置参数

Returns:

Type Description
dict

dict

load

load(
    path_or_uri: str,
    *,
    file: bytes | IO[bytes] | None = None,
    content_type: Optional[str] = None,
    **kwargs: Any
) -> Document

从给定的 URI 或文件对象加载文档并返回 Document 对象 / Load document from the given URI or file object and return Document.

Parameters:

Name Type Description Default
path_or_uri str

文档的路径或 URI (必填)/ File path or uri (required)

required
file bytes | IO[bytes] | None

文件内容,可以是 bytes 或 IO[bytes] 对象(与 path_or_uri 参数二选一)/ File content as bytes or IO[bytes] (mutually exclusive with path_or_uri).

None
content_type Optional[str]

文档的内容类型(MIME 类型),如果不提供则尝试自动检测 / The content type (MIME type), auto-detected if not provided.

None
**kwargs Any

其他可选参数,传递给底层的分片函数 / Additional keyword arguments passed to the partition function.

{}

Returns:

Name Type Description
Document Document

返回一个包含文档内容的 Document 对象 / The loaded document.

Raises:

Type Description
ValueError

如果 path_or_uri 和 file 都未提供,或都提供了 / If neither or both path_or_uri and file are provided.

Source code in tfrobot/utils/document_loaders/unstructured.py
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
def load(
    self,
    path_or_uri: str,
    *,
    file: bytes | IO[bytes] | None = None,
    content_type: Optional[str] = None,
    **kwargs: Any,
) -> Document:
    """
    从给定的 URI 或文件对象加载文档并返回 Document 对象 / Load document from the given URI or file object and return Document.

    Args:
        path_or_uri: 文档的路径或 URI (必填)/ File path or uri (required)
        file: 文件内容,可以是 bytes 或 IO[bytes] 对象(与 path_or_uri 参数二选一)/ File content as bytes or IO[bytes] (mutually exclusive with path_or_uri).
        content_type: 文档的内容类型(MIME 类型),如果不提供则尝试自动检测 / The content type (MIME type), auto-detected if not provided.
        **kwargs: 其他可选参数,传递给底层的分片函数 / Additional keyword arguments passed to the partition function.

    Returns:
        Document: 返回一个包含文档内容的 Document 对象 / The loaded document.

    Raises:
        ValueError: 如果 path_or_uri 和 file 都未提供,或都提供了 / If neither or both path_or_uri and file are provided.
    """
    from unstructured.file_utils.filetype import detect_filetype
    from unstructured.partition.auto import partition

    uri = convert_to_file_url(path_or_uri)
    filename = kwargs.pop("filename", get_filename_from_uri(uri))
    # 处理文件对象 / Handle file object
    file_obj = BytesIO(file) if isinstance(file, bytes) else file if file is not None else self.get_file_obj(uri)

    # TFROB-400:raw_bytes 按需读——``_capture_raw_bytes`` 自我守卫(按 ``_needs_raw_bytes``
    # 决定是否真读 file_obj),调用方无需 if 包裹。默认 ``DefaultHashStrategy`` 路径返回 None。
    raw_bytes: bytes | None = self._capture_raw_bytes(file_obj)

    unique_element_ids = kwargs.pop("unique_element_ids", True)  # 是否使用唯一元素ID
    kwargs.pop("url", None)  # 因为TFRobot partition封装均是使用IO流进行解析,如果再传入url UnstructuredIO会报错。
    partition_kwargs = self._compute_partition_kwargs(file_obj)
    partition_kwargs.update(kwargs)
    if not unique_element_ids:
        raise ValueError("TFRobot必须启用唯一元素ID以确保正确处理文档。")

    # 检测文件类型 / Detect file type
    if not content_type:
        # 使用文件对象检测 / Detect using file object
        file_obj.seek(0)
        if not (ft := detect_filetype(file=file_obj, metadata_file_path=filename)):
            raise ValueError("无法识别文件类型")
        else:
            f_type = TFFileType.from_mime_type(ft.mime_type)
    else:
        f_type = TFFileType.from_mime_type(content_type)

    if f_type is None:
        raise ValueError("未正确识别文件类型")
    elif hasattr(self, "mime_types") and f_type not in self.mime_types:
        raise ValueError(f"不支持的文件类型:{f_type}")

    # 执行分片 / Perform partitioning
    # 使用文件对象 / Use file object
    file_obj.seek(0)
    els_raw = partition(file=file_obj, unique_element_ids=True, content_type=content_type, **partition_kwargs)

    if self.element_filter:
        els_raw = self.element_filter(els_raw)
    # 以 els.id 去重 + 过滤无效 Element / Dedup by id + drop invalid elements.
    # 注意:`e.id` 在 unstructured 产出中可能重复(见类 docstring),不可作数据库主键。
    #
    # 过滤规则:
    #   1. 必须有 e 且 e.id(显式无效)
    #   2. 文本类 Element:`str(e).strip()` 非空(丢解析失败的空白)
    #   3. **非文本类 Element(Image/Table/Formula/...):豁免空 text 规则**——
    #      这些类型的语义载体在 metadata(image_base64 / text_as_html 等),空 text 是
    #      合法状态。历史上这条规则缺失,导致 OCR provider 输出 `![](img-N.png)` 形态
    #      Image(alt 为空、element.text 为空)被误删。白名单见 `_NON_TEXT_CATEGORIES`。
    els = []
    seen_ids = set()

    for e in els_raw:
        if not (e and e.id):
            continue
        category = getattr(e, "category", None)
        if not str(e).strip() and category not in _NON_TEXT_CATEGORIES:
            continue
        if e.id in seen_ids:
            continue
        els.append(e)
        seen_ids.add(e.id)

    els = self._post_partition_hook(els, file_obj=file_obj, filename=filename, partition_kwargs=partition_kwargs)

    all_els = [create_element_by_unstructured_element(e, filename=filename) for e in els]
    # 判断是否有chunk_size要求,如果有的话,按要求进行chuck调整
    if self.min_chunk_size or self.max_chunk_size:
        all_els = self._adjust_chunk_size(all_els)
    # TFROB-400:loader 出口装配 hash(PDF/Image 通过 `OCRCapableUnstructuredLoader.load → super().load()` 也走此处)。
    # strategy 由 :meth:`_make_hash_strategy` hook 决定——子类按需 override(Image
    # 静态返回 :class:`ImageSourceHashStrategy`;PDF 按 raw_bytes 嗅探动态决策)。
    # ``raw_bytes`` 直接透传:当 ``_needs_raw_bytes`` 为 False 时入参是 None,
    # 默认实现忽略;子类若需要 raw_bytes(如 PDFLoader 嗅探)由其 override 自己 fail-fast。
    return Document.from_elements(
        all_els,
        file_uri=AnyUrl(uri),
        file_type=f_type,
        hash_strategy=self._make_hash_strategy(raw_bytes),
        raw_bytes=raw_bytes,
    )