如何用Python从PDF提取带分数?解决pdfplumber数值提取错误
PDF带分数文本提取异常修复方案
- 典型异常场景:PDF中实际标注的带分数长度为
36 1/2英寸,通过现有pdfplumber代码提取得到的数值却是362,整数部分与分数分母直接拼接,丢失了分数的结构信息 - 现有代码逻辑:通过
extract_text()拿到整页线性文本后,按换行拆分行,匹配包含inches的行后取第一个元素作为长度值,正常排版的内容可正常提取,仅对上下堆叠格式的带分数出现识别错误
现有参考代码:
import pdfplumber with pdfplumber.open("Sample.pdf") as pdf: first_page = pdf.pages[0] text = first_page.extract_text() for row in text.split('\n'): if 'inches' in row: num = row.split()[0] print(num)
异常根源:
extract_text()接口默认按线性顺序拼接字符时,会忽略字符的垂直位置差异。带分数中的分数部分是排版软件常用的上下堆叠格式:分子位于文字基线以上、分数线与文字基线齐平、分母位于文字基线以下,和前面的整数部分不在同一水平高度。线性提取流程不会保留垂直方向的偏移关系,直接把整数、分子、分母按出现顺序拼接成连续字符串,最终出现36拼接1再拼接2得到362的错误。
具体修复步骤
- 不再依赖整页线性提取的纯文本做拆分,转而调用pdfplumber的字符级提取接口,基于字符坐标关系还原真实文本结构
- 核心实现逻辑:
- 调用
page.chars获取页面上所有独立字符的内容、左上角x坐标、左上角y坐标、字号属性 - 按y坐标对所有字符做分行聚类:两个字符的y坐标差值小于当前行平均字号的60%时,判定为属于同一行
- 对同一行内的字符按x坐标从小到大排序,计算相邻字符的水平间距:如果间距超过正常字符宽度的1.5倍,就插入空格做内容分隔
- 检测垂直堆叠的分数块:当定位到短横线字符,它的正上方1倍字号距离内存在一个单字符、正下方1倍字号距离内存在一个单字符时,将这部分拼接为
分子/分母的标准格式,避免分数结构丢失
- 调用
- 可直接复用的提取代码示例:
import pdfplumber def extract_text_with_position(page): chars = page.chars if not chars: return "" # 基于y坐标聚类分行 lines = [] current_line = [chars[0]] for char in chars[1:]: if abs(char["top"] - current_line[0]["top"]) < current_line[0]["size"] * 0.6: current_line.append(char) else: current_line.sort(key=lambda c: c["x0"]) lines.append(current_line) current_line = [char] # 处理最后一行内容 current_line.sort(key=lambda c: c["x0"]) lines.append(current_line) # 按行拼接文本,自动识别空格位置 result = [] for line in lines: line_str = line[0]["text"] for idx in range(1, len(line)): gap = line[idx]["x0"] - line[idx-1]["x1"] if gap > line[idx-1]["size"] * 1.5: line_str += " " line_str += line[idx]["text"] result.append(line_str) return "\n".join(result) with pdfplumber.open("Sample.pdf") as pdf: first_page = pdf.pages[0] # 替换原有的extract_text调用 processed_text = extract_text_with_position(first_page) for row in processed_text.split('\n'): if 'inches' in row: num = row.split()[0] print(num)
内容的提问来源于stack exchange,提问作者Arun
相关产品推荐
相关产品推荐

