如何在命令行中将PDF表格数据提取为CSV格式?

25

我希望从这里提取所有行,同时忽略列标题以及所有页面标题,即Supported Devices

pdftotext -layout DAC06E7D1302B790429AF6E84696FCFAB20B.pdf - \
 | sed '$d'                                                  \
 | sed -r 's/ +/,/g; s/ //g'                                 \
 > output.csv

最终生成的文件应该是CSV电子表格格式(逗号分隔值字段)。

换句话说,我想改进上述命令,使输出完全不中断。有什么想法吗?

7个回答

34
我还可以提供另一种解决方案。
虽然在这种情况下,使用“pdftotext”方法的工作量合理,但有些情况下每一页的列宽可能不同(正如您所展示的PDF文件)。在这种情况下,不太为人知但相当酷的免费开源软件Tabula-Extractor是最佳选择。
我自己正在使用直接从GitHub检出的版本:
$ cd $HOME ; mkdir svn-stuff ; cd svn-stuff
$ git clone https://github.com/tabulapdf/tabula-extractor.git git.tabula-extractor

我写了一个非常简单的封装脚本,就像这样:

$ cat ~/bin/tabulaextr

 #!/bin/bash
 cd ${HOME}/svn-stuff/git.tabula-extractor/bin
 ./tabula $@

既然~/bin/在我的$PATH中,我只需要运行

$ tabulaextr --pages all                                 \
         $(pwd)/DAC06E7D1302B790429AF6E84696FCFAB20B.pdf \
        | tee my.csv

从所有页面提取所有表格并将它们转换为单个CSV文件。
前10行(共8727行)的CSV如下所示:
$ head DAC06E7D1302B790429AF6E84696FCFAB20B.csv 

 Retail Branding,Marketing Name,Device,Model
 "","",AD681H,Smartfren Andromax AD681H
 "","",FJL21,FJL21
 "","",Luno,Luno
 "","",T31,Panasonic T31
 "","",hws7721g,MediaPad 7 Youth 2
 3Q,OC1020A,OC1020A,OC1020A
 7Eleven,IN265,IN265,IN265
 A.O.I. ELECTRONICS FACTORY,A.O.I.,TR10CS1_11,TR10CS1
 AG Mobile,Status,Status,Status

在原始的PDF中看起来是这样的:

Screenshot from top of first page of sample PDF

甚至在最后一页,第293页,也有这些行:

 nabi,"nabi Big Tab HD\xe2\x84\xa2 20""",DMTAB-NV20A,DMTAB-NV20A
 nabi,"nabi Big Tab HD\xe2\x84\xa2 24""",DMTAB-NV24A,DMTAB-NV24A

在PDF页面上看起来像这样:

last page of sample PDF

TabulaPDF和Tabula-Extractor在这类工作中非常非常棒!

更新

这里有一个 ASCiinema 屏幕录像(你也可以通过 下载 并使用 asciinema 命令行工具在 Linux/MacOSX/Unix 终端本地重新播放),主角是 tabula-extractor

asciicast


4
新版基于Java的tabula可以在此处找到: https://github.com/tabulapdf/tabula-java - Martin R.

16

正如Martin R所评论的tabula-javatabula-extractor的新版本并且正在活跃更新。1.0.0版于2017年7月21日发布。

下载jar文件并使用最新版的Java即可:

java -jar ./tabula-1.0.0-jar-with-dependencies.jar \
    --pages=all \
    ./DAC06E7D1302B790429AF6E84696FCFAB20B.pdf
    > support_devices.csv

8
您想要的内容相对容易,但您也遇到了不同的问题(我不确定您是否意识到了这一点...)。
首先,您应该在命令中添加“-nopgbrk”(即“请勿分页!”)。因为这些讨厌的“^L”字符会在输出中出现,然后需要进行过滤。
然后添加“grep -vE '(Supported Devices|^$)'”,将过滤掉所有不需要的行,包括空行或只有空格的行:
pdftotext -layout -nopgbrk                           \
   DAC06E7D1302B790429AF6E84696FCFAB20B.pdf -        \
 | grep -vE '(Supported Devices|^$|Marketing Name)'  \
 | gsed '$d'                                         \
 | gsed -r 's# +#,#g'                                \
 | gsed '# ##g'                                      \
 > output2.csv

然而,你还面临着如下问题:
  1. 某些表格字段为空。
  2. 空字段在使用 -layout 选项时以一系列空格字符出现,甚至有时在同一行中出现两次。
  3. 然而,文本列在每个页面上的间距不相同。
  4. 因此,你无法知道从一行到另一行需要将多少个空格视为“空的 CSV 字段”(在这种情况下需要额外的, 分隔符)。
  5. 结果是,你当前的代码会为某些行仅显示一个、两个或三个(而不是四个)字段,并且这些字段出现在错误的列中。
这个问题可以通过以下方法解决:
  1. pdftotext 添加 -x ... -y ... -W ... -H ... 参数以按列裁剪 PDF。
  2. 然后使用像 pastecolumn 这样的工具组合来附加列。
下面的命令提取第一列:
pdftotext -layout -x  38 -y 77 -W 176 -H 500  \
          DAC06E7D1302B790429AF6E84696FCFAB20B.pdf - > 1st-columns.txt

这些是针对第二、第三和第四列的内容:
pdftotext -layout -x 214 -y 77 -W 176 -H 500  \
          DAC06E7D1302B790429AF6E84696FCFAB20B.pdf - > 2nd-columns.txt

pdftotext -layout -x 390 -y 77 -W 176 -H 500  \
          DAC06E7D1302B790429AF6E84696FCFAB20B.pdf - > 3rd-columns.txt

pdftotext -layout -x 567 -y 77 -W 176 -H 500  \
          DAC06E7D1302B790429AF6E84696FCFAB20B.pdf - > 4th-columns.txt

顺便说一下,我有点作弊了:为了得到关于 -x-y-W-H 使用哪些值的线索,我首先运行了这个命令,以找到列标题单词的确切坐标:

pdftotext -f 1 -l 1 -layout -bbox \
          DAC06E7D1302B790429AF6E84696FCFAB20B.pdf - | head -n 10

如果您知道如何阅读和使用pdftotext -h,那就太好了。 :-)

无论如何,如何将四个文本文件作为列并排附加在一起,使用适当的CSV分隔符,您应该自己找出答案。或者提一个新问题 :-)


Kurt Pfeifle:你是如何基于-bbox命令测量x和y坐标的?我在PDF查看器中进行测量,得到的X和Y分别为50和100。 - riccs_0x
@riccs_0x:pdftotext命令需要以PostScript点作为距离单位。你的PDF阅读器(哪一个?!)是否显示PostScript点?我记不清当初是如何确定上述命令的参数的了,那时候已经超过3年了。如果今天我必须再做一次,我会使用Ghostscript和'gs -sDEVICE=bbox'来确定整个页面的边界框,然后估算每个列的相应参数,最后根据第一轮结果进行微调/修改... - Kurt Pfeifle
我正在使用 Evince 和 Atril。 - riccs_0x
抱歉打扰了,我知道这个问题是很久以前的了。我只是遇到了几次这个问题,已经设法解决了,但我正在寻找一种更稳定的方法。感谢你在这里提出的好主意。 - riccs_0x

1

问题涉及命令行,但我看到这里还有另一个答案提到了使用Excel,值得一提的是,现在你可以直接从Excel中导入PDF文件。这节省了我很多时间。

根据这里的指示进行修改: https://www.makeuseof.com/easily-extract-table-from-pdf/

  1. 打开一个新的Excel电子表格。

  2. 转到“数据”选项卡。

  3. 在“获取和转换”部分,单击“获取数据”。

  4. 从列表中选择“从文件”,然后选择“从PDF”。

点击“打开”后,Excel将打开一个导航窗口。在此窗口中,您将看到PDF文件包含的不同表格。

  1. 选择要导入的表格。

  2. 点击“加载”。

在Ubuntu中搜索更专业的解决方案后,我对Windows中这种方法的效果感到惊讶。


在找到Excel解决方案之前,我在Ubuntu上检查了LibreOffice,但似乎没有这个功能。 - DAB
与Office产品的其他几个功能一样,PDF数据源在Mac上不可用。我正在使用Excel for Mac版本16.78(23100802)。 - undefined

1
这可以通过使用 IntelliGet(http://akribiatech.com/intelliget)脚本轻松完成,如下所示。
userVariables = brand, name, device, model;
{ start = Not(Or(Or(IsSubstring("Supported Devices",Line(0)),
                  IsSubstring("Retail Branding",Line(0))),
                IsEqual(Length(Trim(Line(0))),0))); 
  brand = Trim(Substring(Line(0),10,44));
  name = Trim(Substring(Line(0),45,79));
  device = Trim(Substring(Line(0),80,114));
  model = Trim(Substring(Line(0),115,200));
  output = Concat(brand, ",", name, ",", device, ",", model);
}

0

如果您想从PDF中提取表格数据,并且在创建时有控制权(例如,员工签署的时间表合同),则以下解决方案更为简洁:

  1. 创建一个带有字段ID的PDF表单。

  2. 让人们填写并保存PDF表单。

  3. 使用Apache PDFBox,这是一个开源工具,允许从PDF中提取表单数据。它包括一个命令行示例工具PrintFields,您可以按以下方式调用以打印所需的字段信息:

    org.apache.pdfbox.examples.interactive.form.PrintFields file.pdf
    

    有关其他选项,请参见this question

作为上述工作流程的替代方案,您也可以使用数字签名Web服务来允许PDF表单填充并将数据导出到表格中。例如像 SignRequest这样的服务,它允许 创建模板 并随后 导出已签署文档的数据。(本人与该服务无任何关联,只是自己找到的。)

0

Kurt的第一个答案是一个很好的方法,可以提取文本列,但在想要将其作为电子表格导入到Excel的情况下,我们可以只需将文本作为纯文本导入。

这是Windows文本输出。

pdftotext -nopgbrk -layout -fixed 4 -y 40 -H 600 -W 1000 "C:\data\DAC06E7D1302B790429AF6E84696FCFAB20B.pdf" -|find " ">>out.txt

前几行

                                                                                                  AD681H                                      Smartfren Andromax AD681H
                                                                                                  FJL21                                       FJL21
                                                                                                  Luno                                        Luno
                                                                                                  T31                                         Panasonic T31
                                                                                                  hws7721g                                    MediaPad 7 Youth 2
          3Q                                          OC1020A                                     OC1020A                                     OC1020A
          7Eleven                                     IN265                                       IN265                                       IN265
          A.O.I. ELECTRONICS FACTORY                  A.O.I.                                      TR10CS1_11                                  TR10CS1

导入将是分列的,包括奇怪的引号字符,并且如果仍然需要,可以保存为CSV格式。

enter image description here enter image description here enter image description here

有许多优秀的“免费”文本转CSV导入器,可以将文本分析和变形为其他格式,例如图表或列。其中一些可以通过命令行驱动。这可能是免费[mium] Windows 用户中最功能丰富的一个!使用上面的示例文件。

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接