23

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

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

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

换句话说,我想改进上面的命令,使输出根本不会刹车。有任何想法吗?

4

5 回答 5

31

我也会为您提供另一种解决方案。

虽然在这种情况下,该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 文件。

CVS 的前十行(总共 8727 行)如下所示:

$ 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 中看起来像这样:

示例 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 页面上看起来像这样:

样本 PDF 的最后一页

TabulaPDF 和 Tabula-Extractor 非常非常适合这样的工作!


更新

这是一个 ASCIinema 截屏视频(您也可以借助命令行工具在 Linux/MacOSX/Unix 终端本地下载asciinema和重播),主演tabula-extractor

asciiccast

于 2015-05-18T21:17:36.510 回答
11

正如Martin R 评论的那样,tabula-java是新版本tabula-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
于 2017-07-30T23:53:59.190 回答
6

你想要的很简单,但你也遇到了不同的问题(我不确定你是否意识到......)。

首先,您应该在命令中添加-nopgbrkfor ( "No pagebreaks, please!" )。因为这些讨厌^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. 添加-x ... -y ... -W ... -H ...参数以pdftotext按列裁剪 PDF。
  2. paste然后使用和等实用程序的组合附加列column

以下命令提取第一列:

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. :-)

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

于 2015-05-18T20:27:48.503 回答
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);
}
于 2018-04-15T06:44:45.967 回答
0

如果您想从 PDF 中提取您在创建时可以控制的表格数据(对于您的员工必须签署的时间表合同),以下解决方案将更简洁:

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

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

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

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

    有关其他选项,请参阅此问题

作为上述工作流程的替代方案,也许您还可以使用数字签名 Web 服务,该服务允许填写 PDF 表单并将数据导出到表格。例如SignRequest,它允许创建模板并在以后导出已签名文档的数据。(不附属,只是自己发现的。)

于 2019-02-17T22:41:38.687 回答