如何从命令行中将 PDF 中的 table 数据提取为 CSV?
How to extract table data from PDF as CSV from the command line?
我想从 here 中提取所有行,同时忽略列 headers 以及所有页面 headers,即 Supported Devices
。
pdftotext -layout DAC06E7D1302B790429AF6E84696FCFAB20B.pdf - \
| sed '$d' \
| sed -r 's/ +/,/g; s/ //g' \
> output.csv
生成的文件应为 CSV 电子表格格式(逗号分隔值字段)。
换句话说,我想改进上面的命令,让输出完全不刹车。有什么想法吗?
你想要的很简单,但你也遇到了不同的问题(我不确定你是否知道...)。
首先,您应该将 -nopgbrk
for ("No pagebreaks, please!") 添加到您的命令中。因为这些讨厌的 ^L
字符否则会出现在输出中,所以以后不需要过滤掉。
添加一个 grep -vE '(Supported Devices|^$)'
将过滤掉所有你不想要的行,包括空行,或只有 spaces:
的行
pdftotext -layout -nopgbrk \
DAC06E7D1302B790429AF6E84696FCFAB20B.pdf - \
| grep -vE '(Supported Devices|^$|Marketing Name)' \
| gsed '$d' \
| gsed -r 's# +#,#g' \
| gsed '# ##g' \
> output2.csv
但是,你的另一个问题是:
- 部分 table 字段为空。
- 空字段与
-layout
选项一起显示为一系列 space 个字符,有时甚至在同一行中有两个。
- 但是,文本列在页面之间 spaced 并不完全相同。
- 因此,您将无法逐行知道需要将多少 space 视为一个 "empty CSV field"(您需要额外的
,
分隔符).
- 因此,您当前的代码将只显示一些行的一个、两个或三个(而不是四个)字段,并且这些字段最终出现在错误的列中!
有一个解决方法:
- 将
-x ... -y ... -W ... -H ...
参数添加到 pdftotext
以裁剪 PDF column-wise。
- 然后使用
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
使用什么值的线索,我先做了运行这条命令是为了找到header列的确切坐标字:
pdftotext -f 1 -l 1 -layout -bbox \
DAC06E7D1302B790429AF6E84696FCFAB20B.pdf - | head -n 10
如果您知道如何阅读和使用pdftotext -h
,那总是好的。 :-)
无论如何,如何将四个文本文件作为列并排附加,并在它们之间使用适当的 CVS 分隔符,您应该自己去了解。或者问一个新问题:-)
我也会为您提供另一种解决方案。
虽然在这种情况下,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 中的内容如下所示:
它甚至在最后一页 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 页面上看起来像这样:
TabulaPDF 和 Tabula-Extractor 对于这样的工作来说真的非常酷!
更新
这是一个 ASCiinema 截屏视频(您也可以 download 并借助 asciinema
命令行工具), 主演 tabula-extractor
:
因为 , tabula-java
是 tabula-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
这可以使用如下所示的 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);
}
如果您想从创建时可以控制的 PDF 中提取表格数据(对于您的员工必须签署的时间表合同),以下解决方案会更清晰:
创建带有字段 ID 的 PDF 表单。
让人们填写并保存 PDF 表单。
使用您将按如下方式调用的 Apache PDFBox, an open source tool that allows to extract form data from a PDF. It includes a command-line example tool PrintFields 来打印所需的字段信息:
org.apache.pdfbox.examples.interactive.form.PrintFields file.pdf
有关其他选项,请参阅 this question。
作为上述工作流程的替代方案,也许您还可以使用数字签名网络服务,该服务允许填写 PDF 表单并将数据导出到表格中。比如SignRequest, which allows to create templates and later export the data of signed documents。 (没有隶属关系,只是自己发现的。)
我想从 here 中提取所有行,同时忽略列 headers 以及所有页面 headers,即 Supported Devices
。
pdftotext -layout DAC06E7D1302B790429AF6E84696FCFAB20B.pdf - \
| sed '$d' \
| sed -r 's/ +/,/g; s/ //g' \
> output.csv
生成的文件应为 CSV 电子表格格式(逗号分隔值字段)。
换句话说,我想改进上面的命令,让输出完全不刹车。有什么想法吗?
你想要的很简单,但你也遇到了不同的问题(我不确定你是否知道...)。
首先,您应该将 -nopgbrk
for ("No pagebreaks, please!") 添加到您的命令中。因为这些讨厌的 ^L
字符否则会出现在输出中,所以以后不需要过滤掉。
添加一个 grep -vE '(Supported Devices|^$)'
将过滤掉所有你不想要的行,包括空行,或只有 spaces:
pdftotext -layout -nopgbrk \
DAC06E7D1302B790429AF6E84696FCFAB20B.pdf - \
| grep -vE '(Supported Devices|^$|Marketing Name)' \
| gsed '$d' \
| gsed -r 's# +#,#g' \
| gsed '# ##g' \
> output2.csv
但是,你的另一个问题是:
- 部分 table 字段为空。
- 空字段与
-layout
选项一起显示为一系列 space 个字符,有时甚至在同一行中有两个。 - 但是,文本列在页面之间 spaced 并不完全相同。
- 因此,您将无法逐行知道需要将多少 space 视为一个 "empty CSV field"(您需要额外的
,
分隔符). - 因此,您当前的代码将只显示一些行的一个、两个或三个(而不是四个)字段,并且这些字段最终出现在错误的列中!
有一个解决方法:
- 将
-x ... -y ... -W ... -H ...
参数添加到pdftotext
以裁剪 PDF column-wise。 - 然后使用
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
使用什么值的线索,我先做了运行这条命令是为了找到header列的确切坐标字:
pdftotext -f 1 -l 1 -layout -bbox \
DAC06E7D1302B790429AF6E84696FCFAB20B.pdf - | head -n 10
如果您知道如何阅读和使用pdftotext -h
,那总是好的。 :-)
无论如何,如何将四个文本文件作为列并排附加,并在它们之间使用适当的 CVS 分隔符,您应该自己去了解。或者问一个新问题:-)
我也会为您提供另一种解决方案。
虽然在这种情况下,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 中的内容如下所示:
它甚至在最后一页 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 页面上看起来像这样:
TabulaPDF 和 Tabula-Extractor 对于这样的工作来说真的非常酷!
更新
这是一个 ASCiinema 截屏视频(您也可以 download 并借助 asciinema
命令行工具), 主演 tabula-extractor
:
因为 tabula-java
是 tabula-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
这可以使用如下所示的 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);
}
如果您想从创建时可以控制的 PDF 中提取表格数据(对于您的员工必须签署的时间表合同),以下解决方案会更清晰:
创建带有字段 ID 的 PDF 表单。
让人们填写并保存 PDF 表单。
使用您将按如下方式调用的 Apache PDFBox, an open source tool that allows to extract form data from a PDF. It includes a command-line example tool PrintFields 来打印所需的字段信息:
org.apache.pdfbox.examples.interactive.form.PrintFields file.pdf
有关其他选项,请参阅 this question。
作为上述工作流程的替代方案,也许您还可以使用数字签名网络服务,该服务允许填写 PDF 表单并将数据导出到表格中。比如SignRequest, which allows to create templates and later export the data of signed documents。 (没有隶属关系,只是自己发现的。)