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

如何用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的字符级提取接口,基于字符坐标关系还原真实文本结构
  • 核心实现逻辑:
    1. 调用page.chars获取页面上所有独立字符的内容、左上角x坐标、左上角y坐标、字号属性
    2. 按y坐标对所有字符做分行聚类:两个字符的y坐标差值小于当前行平均字号的60%时,判定为属于同一行
    3. 对同一行内的字符按x坐标从小到大排序,计算相邻字符的水平间距:如果间距超过正常字符宽度的1.5倍,就插入空格做内容分隔
    4. 检测垂直堆叠的分数块:当定位到短横线字符,它的正上方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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.27 04:24:03