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
相关产品推荐
相关产品推荐

