Как извлечь данные из HTML-таблицы в сценарии оболочки?

Я пытаюсь создать скрипт BASH, который извлек бы данные из таблицы HTML. Ниже приведен пример таблицы, из которой мне нужно извлечь данные:

<table border=1>
<tr>
<td><b>Component</b></td>
<td><b>Status</b></td>
<td><b>Time / Error</b></td>
</tr>
<tr><td>SAVE_DOCUMENT</td><td>OK</td><td>0.406 s</td></tr>
<tr><td>GET_DOCUMENT</td><td>OK</td><td>0.332 s</td></tr>
<tr><td>DVK_SEND</td><td>OK</td><td>0.001 s</td></tr>
<tr><td>DVK_RECEIVE</td><td>OK</td><td>0.001 s</td></tr>
<tr><td>GET_USER_INFO</td><td>OK</td><td>0.143 s</td></tr>
<tr><td>NOTIFICATIONS</td><td>OK</td><td>0.001 s</td></tr>
<tr><td>ERROR_LOG</td><td>OK</td><td>0.001 s</td></tr>
<tr><td>SUMMARY_STATUS</td><td>OK</td><td>0.888 s</td></tr>
</table>

И я хочу, чтобы скрипт BASH выводил его так:

SAVE_DOCUMENT OK 0.475 s
GET_DOCUMENT OK 0.345 s
DVK_SEND OK 0.002 s
DVK_RECEIVE OK 0.001 s
GET_USER_INFO OK 4.465 s
NOTIFICATIONS OK 0.001 s
ERROR_LOG OK 0.002 s
SUMMARY_STATUS OK 5.294 s

Как это сделать?

До сих пор я пытался использовать sed, но я не знаю, как использовать его достаточно хорошо. Заголовок таблицы (Компонент, Состояние, Время / Ошибка) я исключил с помощью grep, используя grep "<tr><td>так что только строки начинающиеся с <tr><td> будет выбран для следующего разбора (sed). Это то, что я использовал: sed 's@<\([^<>][^<>]*\)>\([^<>]*\)</\1>@\2@g'Но потом <tr> теги все еще остаются и также не разделяют строки. Другими словами, результат этого сценария:

<tr>SAVE_DOCUMENTOK0.406 s</tr>

Полная команда скрипта, над которым я работаю:

cat $FILENAME | grep "<tr><td>" | sed 's@<\([^<>][^<>]*\)>\([^<>]*\)</\1>@\2@g'

7 ответов

Решение

Идти с (g)awk, он способен:-), вот решение, но, пожалуйста, обратите внимание: оно работает только с тем форматом таблицы HTML, который вы опубликовали.

 awk -F "</*td>|</*tr>" '/<\/*t[rd]>.*[A-Z][A-Z]/ {print $3, $5, $7 }' FILE

Здесь вы можете увидеть это в действии: https://ideone.com/zGfLe

Некоторое объяснение:

  1. -F устанавливает в качестве разделителя поля ввода регулярное выражение (любое из tr или td Открывающий или закрывающий тег

  2. тогда работает только в строках, которые соответствуют этим тегам И как минимум двум полям upercasse

  3. затем печатает необходимые поля.

НТН

Вы можете использовать Bash xpath (Perl-модульXML::XPath) для выполнения этой задачи очень легко:

xpath -e '//tr[position()>1]' test_input1.xml 2> /dev/null | sed -e 's/<\/*tr>//g' -e 's/<td>//g' -e 's/<\/td>/ /g'

Вы можете использовать html2text команда и отформатировать столбцы с помощью columnНапример:

$ html2text table.html | column -ts'|'

Component                                      Status  Time / Error
SAVE_DOCUMENT                                           OK            0.406 s     
GET_DOCUMENT                                            OK            0.332 s     
DVK_SEND                                                OK            0.001 s     
DVK_RECEIVE                                             OK            0.001 s     
GET_USER_INFO                                           OK            0.143 s     
NOTIFICATIONS                                           OK            0.001 s     
ERROR_LOG                                               OK            0.001 s     
SUMMARY_STATUS                                          OK            0.888 s     

затем разберите его дальше оттуда (например, cut, awk, ex).

Если вы хотите отсортировать сначала, вы можете использовать exсм. пример здесь или здесь.

Есть много способов сделать это, но вот один:

grep '^<tr><td>' < $FILENAME \
| sed \
    -e 's:<tr>::g'  \
    -e 's:</tr>::g' \
    -e 's:</td>::g' \
    -e 's:<td>: :g' \
| cut -c2-

Вы можете использовать больше sed (1) (-e 's:^ ::') вместо cut -c2- удалить пробел, но cut(1) не получает столько любви, сколько заслуживает. И обратная косая черта предназначена только для форматирования, вы можете удалить их, чтобы получить один лайнер, или оставить их и убедиться, что за ними сразу следует новая строка.

Основная стратегия состоит в том, чтобы медленно разделять HTML на части, а не пытаться делать все сразу с помощью одной непонятной груды синтаксиса регулярных выражений.

Разбор HTML с конвейером оболочки не самая лучшая идея, но вы можете сделать это, если известно, что HTML имеет очень специфический формат. Если будут различия, то вам будет лучше с реальным парсером HTML в Perl, Ruby, Python или даже C.

Решение, основанное на мультиплатформенном веб-интерфейсе CLI. xidel и XQuery:

xidel -s --xquery 'for $tr in //tr[position()>1] return join($tr/td, " ")' file

Для примера ввода это дает:

SAVE_DOCUMENT OK 0.406 s
GET_DOCUMENT OK 0.332 s
DVK_SEND OK 0.001 s
DVK_RECEIVE OK 0.001 s
GET_USER_INFO OK 0.143 s
NOTIFICATIONS OK 0.001 s
ERROR_LOG OK 0.001 s
SUMMARY_STATUS OK 0.888 s

Объяснение:

  • Запрос XQuery for $tr in //tr[position()>1] return join($tr/td, " ") обрабатывает tr элементы, начинающиеся со 2-го (position()>1, чтобы пропустить строку заголовка) в цикле, и объединяет значения дочернего td элементы ($tr/td) с одним пробелом в качестве разделителя.

  • -s марки xidel тихий (подавляет вывод информации о состоянии).


В то время как html2text удобен для отображения извлеченных данных, обеспечивая машинно-разборный вывод нетривиально, к сожалению:

html2text file | awk -F' *\\|' 'NR>2 {gsub(/^\||.\b/, ""); $1=$1; print}'

Команда Awk удаляет скрытые \b на основе (backspace) последовательности, которые html2text выводит по умолчанию и разбирает строки на поля |, а затем выводит их с пробелом в качестве разделителя (пробел является стандартным разделителем выходных полей Awk; например, чтобы изменить его на вкладку, используйте -v OFS='\t').

Примечание: использование -nobs подавлять возвратные последовательности в источнике не вариант, потому что тогда вы не сможете различить скрытые по умолчанию _ экземпляры, используемые для заполнения и фактические _ символы в данных.

Примечание: учитывая, что html2text казалось бы, неизменно использует | в качестве разделителя столбцов, вышеприведенное будет работать надежно, только если нет | экземпляры в извлекаемых данных.

Для полноты картины pandoc хорошо справляется с извлечением HTML-таблицы. Например,

      pandoc --from html --to plain table.txt
  ---------------- -------- --------------
  Component        Status   Time / Error
  SAVE_DOCUMENT    OK       0.406 s
  GET_DOCUMENT     OK       0.332 s
  DVK_SEND         OK       0.001 s
  DVK_RECEIVE      OK       0.001 s
  GET_USER_INFO    OK       0.143 s
  NOTIFICATIONS    OK       0.001 s
  ERROR_LOG        OK       0.001 s
  SUMMARY_STATUS   OK       0.888 s
  ---------------- -------- --------------

Вы можете проанализировать файл, используя редактор Ex (часть Vim), удалив теги HTML, например:

$ ex -s +'%s/<[^>]\+>/ /g' +'v/0/d' +'wq! /dev/stdout' table.html 
  SAVE_DOCUMENT  OK  0.406 s  
  GET_DOCUMENT  OK  0.332 s  
  DVK_SEND  OK  0.001 s  
  DVK_RECEIVE  OK  0.001 s  
  GET_USER_INFO  OK  0.143 s  
  NOTIFICATIONS  OK  0.001 s  
  ERROR_LOG  OK  0.001 s  
  SUMMARY_STATUS  OK  0.888 s 

Вот более короткая версия, напечатав весь файл без HTML-тегов:

$ ex +'%s/<[^>]\+>/ /g|%p' -scq! table.html

Объяснение:

  • %s/<[^>]\+>/ /g - Замените все HTML-теги на пустое место.
  • v/0/d - Дает все строки без 0,
  • wq! /dev/stdout - Выходит из редактора и записывает буфер в стандартный вывод.
Другие вопросы по тегам