如何检测PDF提取文本的段落?能否通过位置区分文本块?
利用文本块位置区分段落的实现方法
核心思路
PyMuPDF提取的每个文本块(TextBlock)都自带位置信息(bbox边界框),可以通过对比相邻文本块的垂直间距、水平对齐、行高这些维度,精准区分同段落换行和独立文本块,进而划分段落。
具体实现步骤
- 提取文本块及位置数据:用PyMuPDF遍历PDF页面,获取每个文本块的
bbox(含左上角x0,y0、右下角x1,y1坐标)和文本内容。注意PyMuPDF的坐标原点在页面左下角,y值越大位置越靠上。 - 计算垂直间距判定段落:计算当前块底部
y0与下一块顶部y1的差值(因y轴向上,差值为上块y0 - 当前块y1)。若这个间距大于行高的1.5-2倍,则判定为新段落;若间距接近行高,则属于同段落的换行。 - 结合水平对齐辅助判断:如果相邻文本块的左对齐位置(
x0)差异极小(比如小于5个单位),且垂直间距符合换行阈值,基本可判定为同段落换行;若左对齐偏移明显,即使间距小,也可能是新段落或列表项。 - 处理特殊排版:
- 列表项:通常有固定缩进或开头符号(如•、数字),可结合文本开头特征和位置偏移判断。
- 标题:字号、行高通常更大,垂直间距会远大于普通段落,可单独识别。
示例代码
import fitz def split_into_paragraphs(page): # 获取所有文本块,格式为(x0, y0, x1, y1, text, block_no, type) blocks = page.get_text("blocks") # 按y坐标从大到小排序(页面顶部内容y值更大) blocks.sort(key=lambda b: -b[1]) paragraphs = [] current_para = [] base_line_height = None for idx, block in enumerate(blocks): x0, y0, x1, y1, text, _, _ = block current_line_h = y1 - y0 # 初始化基准行高 if base_line_height is None: base_line_height = current_line_h if idx == 0: current_para.append(text.strip()) continue # 获取上一个文本块的位置 prev_block = blocks[idx-1] prev_y0 = prev_block[1] # 计算两个文本块的垂直间距 vertical_gap = prev_y0 - y1 # 判定新段落的条件:间距超过行高1.8倍,或左对齐偏移过大 if vertical_gap > base_line_height * 1.8 or abs(x0 - prev_block[0]) > 10: paragraphs.append("\n".join(current_para)) current_para = [text.strip()] base_line_height = current_line_h else: current_para.append(text.strip()) # 添加最后一个段落 if current_para: paragraphs.append("\n".join(current_para)) return paragraphs # 调用示例 doc = fitz.open("target.pdf") target_page = doc[0] result_paragraphs = split_into_paragraphs(target_page) for para in result_paragraphs: print(para) print("---段落分割线---")
注意事项
- 不同PDF排版差异大,阈值(如1.8倍行高、10单位偏移)需根据实际文档调整。
- 多列排版的PDF,需先判断文本块所属列,再按列分别处理段落。
- 若文本块内部包含多行,可先拆分单行后再进行位置对比。
内容的提问来源于stack exchange,提问作者Anm
相关产品推荐
相关产品推荐

