Как извлечь данные таблицы из PDF как CSV из командной строки?

Я хочу извлечь все строки из здесь, игнорируя заголовки столбцов, а также все заголовки страниц, то есть Supported Devices.

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

Полученный файл должен быть в формате электронной таблицы CSV (поля значений, разделенных запятыми).

Другими словами, я хочу улучшить приведенную выше команду, чтобы вывод вообще не тормозился. Любые идеи?


person user706838    schedule 18.05.2015    source источник
comment
Если бы это было так просто, @dwarring! - :-)   -  person Kurt Pfeifle    schedule 18.05.2015
comment
этот инструмент у меня сработал, первые 20 страниц бесплатны: pdftables.com   -  person I Like    schedule 12.01.2017


Ответы (5)


Предлагаю и другое решение.

Хотя в этом случае метод 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.

Первые десять (из 8727) строк CVS выглядят так:

$ 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:

asciicast

person Kurt Pfeifle    schedule 18.05.2015

Как прокомментировал Мартин Р., tabula-java - это новая версия tabula-extractor и активная. 1.0.0 была выпущена 21 июля 2017 года.

Загрузите файл jar с последней версией java:

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

То, что вы хотите, довольно просто, но у вас также другая проблема (я не уверен, что вы об этом знаете ...).

Во-первых, вы должны добавить -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. Добавьте параметры -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 между ними, вы должны выяснить сами. Или задайте новый вопрос :-)

person Kurt Pfeifle    schedule 18.05.2015
comment
Курт Пфайфл: Как вы измерили координаты x и y с помощью команды -bbox? Я измеряю внутри PDF-файла и получаю для X и Y, 50 и 100 соответственно - person riccs_0x; 29.07.2018
comment
@ riccs_0x: команде pdftotext требуются точки PostScript в качестве единиц расстояния. Отображает ли ваша программа просмотра PDF (какая ?!) точки PostScript? Я не могу вспомнить, как именно я определил параметры для вышеуказанных команд. Это было более 3-х лет назад. Если бы мне пришлось сделать это сегодня снова, я бы использовал Ghostscript с 'gs -sDEVICE=bbox', чтобы определить ограничивающую рамку всей страницы, затем угадать соответствующие параметры для каждого столбца, а затем настроить / изменить их в зависимости от первых результатов .. .. - person Kurt Pfeifle; 30.07.2018
comment
Я использую Эвинс и Атрил - person riccs_0x; 30.07.2018
comment
Извините за беспокойство, я знаю, что этот вопрос был задан давно. Я несколько раз сталкивался с этой проблемой, и мне удалось как-то ее разобрать, но я ищу более стабильный подход. Спасибо за отличные идеи, которые вы здесь сделали. - person riccs_0x; 30.07.2018

Это легко сделать с помощью скрипта 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);
}
person user3354850    schedule 15.04.2018

Для случая, когда вы хотите извлечь из PDF-файла те табличные данные, которыми вы можете управлять во время создания (для контрактов с расписанием, которые должны подписывать ваши сотрудники), следующее решение будет более чистым:

  1. Создайте форму PDF с идентификаторами полей.

  2. Позвольте людям заполнять и сохранять формы PDF.

  3. Используйте Apache PDFBox, инструмент с открытым исходным кодом, который позволяет извлекать данные формы из PDF. Он включает пример инструмента командной строки PrintFields, который вы должны вызвать следующим образом, чтобы распечатать информацию о желаемом поле:

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

    Чтобы узнать о других вариантах, см. этот вопрос.

В качестве альтернативы описанному выше рабочему процессу, возможно, вы могли бы также использовать веб-службу цифровой подписи, которая позволяет заполнять формы PDF и экспортировать данные в таблицы. Например, SignRequest, который позволяет создавать шаблоны и более поздних версий экспортировать данные подписанных документов. (Не аффилирован, нашел это сам.)

person tanius    schedule 17.02.2019