web-dev-qa-db-ja.com

コマンドラインからPDF CSVとしてテーブルデータを抽出する方法は?

here からすべての行を抽出しながら、列ヘッダーとすべてのページヘッダーを無視したい、つまりSupported Devices

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

結果のファイルはCSVスプレッドシート形式(カンマ区切り値フィールド)である必要があります。

つまり、出力がまったくブレーキをかけないように、上記のコマンドを改善したいと思います。何か案は?

16
user706838

私もあなたに別の解決策を提供します。

この場合、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の最初の10行(合計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スクリーンキャストです(これもダウンロードでき、Linux/MacOSX/Unixターミナルでローカルで再生できますasciinemaコマンドラインツールのヘルプ)、主にtabula-extractor

asciicast

23
Kurt Pfeifle

あなたが望むものはかなり簡単ですが、あなたはまた別の問題を抱えています(あなたがそれを知っているかどうかはわかりません...)。

まず、コマンドに_-nopgbrk_ for( "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_オプションを使用して一連のスペース文字として表示されます。同じ行に2つある場合もあります。
  3. ただし、テキスト列はページごとに同じ間隔で配置されていません。
  4. そのため、「空のCSVフィールド」(余分な_,_セパレータが必要な場合)と見なす必要のあるスペースの数が行ごとにわかりません。
  5. その結果、現在のコードでは、一部の行で1つ、2つ、または3つ(4つではなく)のフィールドのみが表示され、これらのフィールドは誤った列に表示されます。

これには回避策があります:

  1. _-x ... -y ... -W ... -H ..._パラメータをpdftotextに追加して、PDF列単位でトリミングします。
  2. 次に、pastecolumnなどのユーティリティを組み合わせて列を追加します。

次のコマンドは、最初の列を抽出します。

_pdftotext -layout -x  38 -y 77 -W 176 -H 500  \
          DAC06E7D1302B790429AF6E84696FCFAB20B.pdf - > 1st-columns.txt
_

これらは、2番目、3番目、4番目の列用です。

_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_の読み方と利用方法を知っていると、いつでも良いことです。 :-)

とにかく、4つのテキストファイルを適切なCVS区切り文字を挟んで列として並べて追加する方法を確認してください。または、新しい質問をする:-)

5
Kurt Pfeifle

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
4
Nobu

これは、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);
}
1
user3354850

作成時に制御できるPDFから表形式のデータを抽出する場合(タイムシート契約の場合、従業員が署名する必要があります)の場合、次の解決策はよりクリーンになります。

  1. フィールドIDを含むPDFフォームを作成します。

  2. PDFフォームに記入して保存します。

  3. PDFからフォームデータを抽出できるオープンソースツールである Apache PDFBox を使用します。コマンドラインのサンプルツール PrintFields が含まれ、必要なフィールド情報を印刷するために次のように呼び出します。

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

    他のオプションについては、 この質問 を参照してください。

上記のワークフローの代わりに、PDFフォームの入力とデータのテーブルへのエクスポートを許可するデジタル署名Webサービスを使用することもできます。 SignRequest など テンプレートを作成する 以降 署名されたドキュメントのデータをエクスポートする を許可します(アフィリエイトではなく、自分で見つけただけです)。

0
tanius