从 pdf 文件中复制表格,顺序是错乱的, pdf 表格中的文本选择顺序是怎么定义的呢? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
sillydaddy
0.65D
0.15D
V2EX    问与答

从 pdf 文件中复制表格,顺序是错乱的, pdf 表格中的文本选择顺序是怎么定义的呢?

  •  
  •   sillydaddy 2023-11-29 09:51:22 +08:00 2107 次点击
    这是一个创建于 751 天前的主题,其中的信息可能已经有所发展或是发生改变。
    这个 pdf 文件,是建设银行储蓄卡的消费明细,表格形式的。里面的文本都可以选择,但鼠标框选文本时,整个的顺序几乎完全错乱:有时选不中某一个单元,有时选中了鼠标框之外的几行。即使我按住`option`键,选中特定的一列,拷贝出的文本的顺序也不是原始文档里的顺序。

    开始我以为这是 pdf 的固有问题,可是我自己拿 macOS 上的 Number 做了一个同样的表格,然后导出为 pdf ,发现是可以有序选中文本的。不知道建行的 pdf 文件是用的什么工具生成的。

    现在有点疑惑,pdf 中的表格,在选择它里面文本时,选中的顺序是依据什么而定的呢?我模糊记得网页里的表格,有时也有这个选中顺序的问题。
    22 条回复    2023-11-30 09:14:25 +08:00
    Huahuo
        1
    Huahuo  
       2023-11-29 10:04:31 +08:00
    我也有相似的需求,求教 python 有什么库可以顺序读 pdf 的表格吗?
    zhangshine
        2
    zhangshine  
       2023-11-29 10:17:02 +08:00
    我对 pdf 有一点研究。PDF 其实是把字画上去,并不存在 word 里面的行或者段落的格式。所以你复制的时候的感觉和复制图片识别的文字的感觉差不多。
    xomix
        3
    xomix  
       2023-11-29 10:21:16 +08:00
    pdf 实际上是一种虚拟打印指令把文字打印到虚拟纸张的指定位置。
    所以当然可以:
    a.虚拟打印一组表格文字到指定位置
    b.虚拟打印将表格绘制为图片到指定位置
    c.虚拟随机字符顺序将表格打印到指定位置

    既然你无法通过 pdf 复制,那就考虑去水印和 ocr 。
    sillydaddy
        4
    sillydaddy  
    OP
       2023-11-29 10:38:16 +08:00
    @Huahuo 我现在还没到要用 python 读取那一步 。网上好像不少这种提取 pdf 表格的工具。
    @zhangshine pdf 文档是不是分 2 种,其中 1 种是可以直接选文字的,一般纯文本转 pdf 就是这种。所以感觉跟图片 ocr 还是有点区别。
    @xomix 文字是可以直接复制的,只不过顺序是乱的。ocr 也是会考虑的。不知道有 1#楼说的 python 工具可以读取 pdf 表格吗?
    xomix
        5
    xomix  
       2023-11-29 10:41:50 +08:00
    @sillydaddy 你再认真看看我的回复,我想你的那个表格的输出方式是我例子里面的 c 例,也就是说你复制出来的结果就是正确结果,它就是随机乱序将文本输出至坐标位置实现的表格。
    这种时候只能 ocr
    yiyiwa
        6
    yiyiwa  
       2023-11-29 10:45:59 +08:00   1
    python pdfplumber 这个库可以
    sillydaddy
        7
    sillydaddy  
    OP
       2023-11-29 10:48:28 +08:00
    @xomix 我明白你的意思了,就是说生成 pdf 的原始文本顺序就是乱的,但每个字符映射到 pdf 的位置是正确的,所以复制会有问题?按照这个说法,框选文字时,其实复制的是原始(顺序错乱)文本的一部分,比如计算出鼠标框左上角位置对应的文本,然后复制到鼠标框右下角位置对应的文本结束?这块框选复制的逻辑没有太明白。
    zhangshine
        8
    zhangshine  
       2023-11-29 10:50:59 +08:00
    @sillydaddy 算是个 plus 版本,就是再画的基础上会添加一些结构信息,这样就和 word 差不多了。
    FlashEcho
        9
    FlashEcho  
       2023-11-29 10:53:12 +08:00
    我建议你直接截图给 chatgpt ,然后让他输出给你想要的格式,我打 latex 里的数学公式就是这么做的,而且准确率很高
    xomix
        10
    xomix  
       2023-11-29 10:58:30 +08:00
    @sillydaddy pdf 你没做过深入理解吧。
    我简单说一下,方便让你明白,很多东西是大致这样不是严格的原理。

    pdf 是把 图像 或 文字 按照固定格式渲染的技术,有点儿像 htrml 。

    当他输出一段文字时候,可以对每一个文字设置其定位属性,但是这段文字本身是连续的文本。因此你可以选择这段文字,但当你选择了这段文字时,能够复制的是文本段的实际顺序而不是展示顺序,所以看起来是正常顺序,但是复制后是乱序。
    sillydaddy
        11
    sillydaddy  
    OP
       2023-11-29 11:49:39 +08:00
    @zhangshine 明白了
    @chesha1 gpt4 吧,我还有呢

    @xomix 举个具体的例子,如果 pdf 打印输出的结果是"0123456789",而生成这个打印结果的原始文本是"5678901234",也就是说,「文本索引」->「打印位置」的映射关系是 0->5, 1->6, 2->7, 3->8, 4->9, 5->0, 6->1, 7->2, 8->3, 9->4 。那么当我在 pdf 中,用鼠标从"0123456789"当中的"0"字符,也就是打印位置是 0 的地方,开始往后框选三个字符,框选的位置分别是 0,1,2 ,那么从根据「文本索引」->「打印位置」的关系,反映射回去,得出实际被选中的字符是"567"对吗?

    如果是(像你说的)直接建立的是「文本字符」->「打印位置」的映射,也就是说"0"->0, "1"->1, ... "9"->9 ,那么鼠标框选,根据位置反映射到字符时,就不会出现问题了。

    你能根据这个例子解释一下吗?因为#10 楼的逻辑我没太理解。
    xomix
        12
    xomix  
       2023-11-29 12:24:19 +08:00
    @sillydaddy
    以下是一段伪代码,肯定不是 pdf ,只是方便你理解。

    打印代码段如下

    文本段开始
    [绝对位置 0 ,3]1
    [绝对位置 0 ,0]2
    [绝对位置 0 ,9]3
    [绝对位置 0 ,5]4
    [绝对位置 0 ,7]5
    [绝对位置 0 ,6]6
    [绝对位置 0 ,1]7
    [绝对位置 0 ,2]8
    [绝对位置 0 ,4]9
    [绝对位置 0 ,8]0
    文本段结束


    你看到的展示将会是
    2781946503

    因为是组文本,当你选择时候,这些文字会都被你选中,但是你复制时会复制无格式信息的文本段内容
    1234567890
    sillydaddy
        13
    sillydaddy  
    OP
       2023-11-29 13:20:31 +08:00
    @xomix 你#12 楼说到的这一层面我是理解的。我不理解的是,如果#12 楼的例子中,只选中 10 个字符中的 3 个字符,会发生什么?怎么根据鼠标框选的位置,反推得到框选选中的字符。想了解这个,是因为根据它可以推理出在 pdf 中选择表格里面的一部分内容时,会发生什么。
    不过大概的意思我知道了,谢谢你耐心的解释。
    Huahuo
        14
    Huahuo  
       2023-11-29 13:32:44 +08:00
    @yiyiwa 多谢
    @sillydaddy python pdfplumber 这个好用
    kumakichi
        15
    kumakichi  
       2023-11-29 13:36:27 +08:00
    一个猜想,
    截图 丢给 gpt 生成 csv 格式的?
    然后复制回来?
    013231
        16
    013231  
       2023-11-29 13:37:46 +08:00   1
    findsomeone
        17
    findsomeone  
       2023-11-29 13:42:05 +08:00
    除了 py 那个库,如果你用 iphone 的话,截图放相册再识别复制也可以~~
    xomix
        18
    xomix  
       2023-11-29 15:10:07 +08:00
    @sillydaddy 不同的 api 处理方式不一样,这个没有一概而论的。不过大致都是选择三个文本,至于是选择原始数据三个文本还是选择实际现实数据三个文本,最后按照原始文本顺序排列。
    xomix
        19
    xomix  
       2023-11-29 15:11:02 +08:00
    @sillydaddy 这还是理想情况,如果说一开始 pdf 就不想让你复制,生成的时候可能用多组随机顺序文本按照指定位置输出,你复制的文本就更乱了。
    sillydaddy
        20
    sillydaddy  
    OP
       2023-11-29 16:01:20 +08:00
    @yiyiwa
    @Huahuo
    @013231
    感谢。看了下 pdfplumber 这个库不错,还可以可视化 debug 。camelot 也不错,也可以可视化 debug ,文档也挺详细。
    HiyaKuso
        21
    HiyaKuso  
       2023-11-29 17:16:30 +08:00
    我之前用的 pdfplumber 和 PyMuPDF ,最终用了 pdfplumber ,不过后来新项目用了 PyMuPDF ,因为更新 1.23.0 版本后代码写起来比较简单,准确度还行,又可以转为 DataFrame 使用。
    代码大概是这样:
    tables = page.find_tables()
    df = tables[0].to_pandas()
    df.to_excel('example.xlsx', index=False)
    sillydaddy
        22
    sillydaddy  
    OP
       2023-11-30 09:14:25 +08:00
    @HiyaKuso 是啊,用起来真简单
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2574 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 30ms UTC 14:39 PVG 22:39 LAX 06:39 JFK 09:39
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86