如何从命令行中将PDF数据从PDF中提取出来?

时间:2015-05-18 18:28:31

标签: pdf grep pdftotext

我想从here中提取所有行,同时忽略列标题以及所有页眉,即Supported Devices

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

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

换句话说,我想改进上面的命令,以便输出根本不会制动。有什么想法吗?

5 个答案:

答案 0 :(得分:21)

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

虽然在这种情况下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中看起来像这样:

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截屏视频(您也可以 download ,并在asciinema命令行的帮助下在Linux / MacOSX / Unix终端中重新播放工具),主演tabula-extractor

asciicast

答案 1 :(得分:5)

你想要的是相当容易的,但你也有不同的问题(我不确定你是否意识到这一点......)。

首先,您应该为您的命令添加-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. 因此,您当前的代码只显示某些行的一个,两个或三个(而不是四个)字段,这些字段最终会出现在错误的列中!
  6. 有一种解决方法:

    1. -x ... -y ... -W ... -H ...参数添加到pdftotext以逐列裁剪PDF。
    2. 然后使用pastecolumn等实用程序的组合附加列。
    3. 以下命令提取第一列:

      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分隔符,你应该自己找出来。或者问一个新问题: - )

答案 2 :(得分:3)

作为Martin R commentedtabula-javatabula-extractor的新版本并且有效。 1.0.0于2017年7月21日发布。

Download the jar file以及最新的java:

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

答案 3 :(得分: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);
}

答案 4 :(得分: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,它允许create templates和更高版本的export the data of signed documents。 (不隶属于,我自己发现了这个。)