如何用Python高效识别PDF删除线文本并提取无删除线内容?
高效识别PDF中删除线文本的Python方案
为什么pdf2docx能识别删除线而PyMuPDF官方称无法直接支持?
PyMuPDF官方所说的“无法识别删除线文本”,指的是多数PDF并未将删除线作为文本的原生属性存储——大部分场景下,删除线是通过在文本上方绘制一条水平线条实现的,而非给文本标记删除线样式。pdf2docx之所以能识别,是因为它额外做了逻辑处理:提取PDF中的文本块与页面线条,通过计算两者的位置关系,判断文本是否被线条覆盖,进而标记为删除线格式。
更高效的直接解析方案(无需转docx)
跳过docx转换步骤,直接用PyMuPDF解析文本与线条,能大幅提升处理效率。核心逻辑:
- 提取页面所有文本块及坐标
- 提取页面中近似水平的线条(删除线通常为水平)
- 通过位置匹配判断文本块是否被线条覆盖
- 按需提取带删除线的文本,或反向提取无删除线的目标内容
代码示例
import os import glob import fitz # PyMuPDF def has_strikeout(text_block, lines): """判断文本块是否被删除线覆盖""" text_rect = fitz.Rect(text_block[:4]) # 文本块的矩形区域 text_mid_y = (text_rect.y0 + text_rect.y1) / 2 # 文本块垂直中心位置 # 遍历线条,检查是否有水平线条覆盖文本块中心区域 for line in lines: line_rect = fitz.Rect(line[:4]) # 匹配条件:水平方向覆盖文本块,垂直方向贴近文本中心 if (line_rect.x0 < text_rect.x1 and line_rect.x1 > text_rect.x0 and abs(line_rect.y0 - text_mid_y) < 3 and abs(line_rect.y1 - text_mid_y) < 3): return True return False def extract_target_text(pdf_path, get_strikeout=False): """提取PDF中目标文本:get_strikeout=True时提取带删除线的文本,否则提取无删除线的文本""" target_text = [] doc = fitz.open(pdf_path) for page in doc: # 提取带坐标的文本块 text_blocks = page.get_text("blocks") # 提取页面中近似水平的线条 lines = [] for path in page.get_drawings(): for item in path["items"]: if item[0] == 'l': # 'l'代表直线 x0, y0 = item[1] x1, y1 = item[2] if abs(y0 - y1) < 2: # 筛选水平线条 lines.append((x0, y0, x1, y1)) # 筛选目标文本 for block in text_blocks: text = block[4].strip() if text and has_strikeout(block, lines) == get_strikeout: target_text.append(text) doc.close() return target_text # 批量处理PDF文件 if __name__ == "__main__": pdf_files = glob.glob(os.getcwd() + r'\PDFs\*.pdf') all_target_text = [] for pdf in pdf_files: print(f"处理文件: {pdf}") # 提取无删除线的目标文本,若要提取删除线文本则将get_strikeout设为True text = extract_target_text(pdf, get_strikeout=False) all_target_text.extend(text) print("所有无删除线的目标文本:") print("\n".join(all_target_text))
方案优势
- 效率显著提升:直接解析PDF,避免docx转换的额外开销,处理10页PDF仅需数秒
- 无中间文件:无需生成、删除临时docx文件,流程更简洁
- 可定制性强:可根据实际PDF的删除线样式调整匹配阈值(如线条粗细、与文本的距离)
补充说明
少数规范生成的PDF会将删除线作为文本属性存储,这种情况可尝试用page.get_text("dict")查看文本的flags属性是否包含删除线标记,但此类场景占比极低,多数情况仍需通过线条匹配判断。
内容的提问来源于stack exchange,提问作者wanderingcatto
相关产品推荐
相关产品推荐

