You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyMuPDF中search_for遇换行拆分文本,如何识别同属原短语的实例?

解决PyMuPDF中文本因换行/排版拆分导致的匹配问题

当你用page.search_for()查找短语时,一旦短语被换行或排版拆分到不同文本块,就会得到多个独立的匹配区域,打乱索引逻辑。可以通过以下几种方法检测并合并这些拆分的实例,还原完整的短语区域:

方法一:基于单词级数据拼接匹配

利用page.get_text("words")获取页面所有单词的位置和内容,先将单词拼接成完整文本,找到目标短语的位置后,再对应回单词的区域并合并成一个整体bbox,这样不管短语是否拆分,都能得到完整的匹配区域。

示例代码:

import fitz

def get_full_phrase_bbox(page, target_phrase):
    # 获取页面所有单词信息:(x0, y0, x1, y1, 单词文本, 块编号, 行编号, 单词编号)
    words = page.get_text("words")
    if not words:
        return None

    # 拼接单词为完整文本,同时记录每个单词在文本中的位置和bbox
    full_text = ""
    word_mapping = []
    for word_info in words:
        x0, y0, x1, y1, word = word_info[:5]
        start = len(full_text)
        # 按文档实际情况调整拼接方式:如果文档单词间无空格,就去掉"+ "
        full_text += word + " "
        end = len(full_text) - 1  # 剔除最后添加的空格
        word_mapping.append((start, end, (x0, y0, x1, y1)))

    # 查找目标短语在拼接文本中的位置
    phrase_start = full_text.find(target_phrase)
    if phrase_start == -1:
        return None
    phrase_end = phrase_start + len(target_phrase)

    # 合并对应单词的bbox
    merged_bbox = None
    for s, e, bbox in word_mapping:
        # 判断单词是否属于目标短语的范围
        if s >= phrase_start and e <= phrase_end:
            if merged_bbox is None:
                merged_bbox = list(bbox)
            else:
                # 取所有单词bbox的最小左上、最大右下坐标,合并成整体区域
                merged_bbox[0] = min(merged_bbox[0], bbox[0])
                merged_bbox[1] = min(merged_bbox[1], bbox[1])
                merged_bbox[2] = max(merged_bbox[2], bbox[2])
                merged_bbox[3] = max(merged_bbox[3], bbox[3])
    return tuple(merged_bbox) if merged_bbox else None

# 调用示例
doc = fitz.open("your_file.pdf")
page = doc[0]
target = "需要脱敏的目标短语"
full_area = get_full_phrase_bbox(page, target)
if full_area:
    # 用白色矩形覆盖实现脱敏
    page.draw_rect(full_area, color=(1,1,1), fill=(1,1,1))
doc.save("desensitized_result.pdf")
doc.close()

方法二:合并search_for结果并验证文本

先通过search_for()获取所有可能的匹配片段,再按位置排序后拼接文本,检查是否等于目标短语,同时验证片段间的位置是否连续(比如行间距小于字体大小的一半)。

示例代码:

import fitz

def merge_split_matches(page, target_phrase, threshold=5):
    # 获取所有匹配片段(先匹配短语的第一个单词缩小范围)
    matches = page.search_for(target_phrase.split()[0])
    if not matches:
        return None

    # 按y坐标(从上到下)、x坐标(从左到右)排序匹配片段
    matches.sort(key=lambda bbox: (bbox[1], bbox[0]))

    # 逐个拼接片段文本,验证是否匹配目标短语
    current_text = ""
    candidate_bboxes = []
    for bbox in matches:
        # 获取当前bbox对应的文本
        text = page.get_text("text", clip=bbox).strip()
        current_text += text + " "
        candidate_bboxes.append(bbox)

        # 检查当前拼接文本是否匹配目标短语
        if current_text.strip() == target_phrase:
            # 合并bbox
            merged_bbox = [bbox[0], bbox[1], bbox[2], bbox[3]]
            for b in candidate_bboxes:
                merged_bbox[0] = min(merged_bbox[0], b[0])
                merged_bbox[1] = min(merged_bbox[1], b[1])
                merged_bbox[2] = max(merged_bbox[2], b[2])
                merged_bbox[3] = max(merged_bbox[3], b[3])
            return tuple(merged_bbox)
        
        # 如果拼接过长,重置匹配状态
        if len(current_text) > len(target_phrase) + 10:
            current_text = text + " "
            candidate_bboxes = [bbox]
    return None

注意事项

  • 拼接文本时,要根据文档实际排版调整空格处理逻辑(比如有些文档单词间无空格,直接拼接即可)。
  • 位置阈值(比如行间距)可以根据文档的字体大小、行间距灵活调整,一般设为字体大小的1/2到1倍。
  • 如果目标短语包含特殊字符,需要确保拼接和匹配时的字符一致性(比如全角/半角空格)。

内容的提问来源于stack exchange,提问作者eliotz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 01:52:04