Как извлечь данные из 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
Некоторое объяснение:
-F
устанавливает в качестве разделителя поля ввода регулярное выражение (любое изtr
илиtd
Открывающий или закрывающий тегтогда работает только в строках, которые соответствуют этим тегам И как минимум двум полям upercasse
затем печатает необходимые поля.
НТН
Вы можете использовать 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
- Выходит из редактора и записывает буфер в стандартный вывод.