Как выполнить синтаксический анализ файла HTML в окне Solaris 10 UNIX, чтобы поместить все значения элементов <td>в файл CSV? - PullRequest
1 голос
/ 19 января 2012

Я довольно хорошо знаком с PHP, включая командную строку, немного знаком с сценариями BASH, и не имею опыта работы с Perl или другими языками, но готов использовать все, что работает.

Файл HTML, который я пытаюсь проанализировать, содержит более 700 000 строк, 61 МБ. Я не могу изменить источник, который создает таблицу HTML, только загрузить всю таблицу с помощью wget http://10.1.1.2/file.pl.

Вот пример формата HTML-кода, который я пытаюсь проанализировать:

<HTML>
  <HEAD>
    <TITLE>Objects</TITLE>
    <STYLE type="text/css">
    a:hover
    {
    color:red
    }
    </STYLE>
    </HEAD>
  <BODY>
  <IMG src="http://10.1.1.2/images/logo.gif"/>
  <BR/><BR/>
  <TABLE border="0">
    <TR>
      <TH>Objects</TH>
    </TR>
    <TR>
      <TD><HR style="width:227px"></TD>
    </TR>
  </TABLE>
  <table border=1 cellpadding=5 cellspacing=0><tr><th><b>Subtype</b></th><th><b>Object</b>    </th></tr>
    <tr><td>10GigEthernet</td><td>SNFCCAMK34T-TenGigE0/10/0/0</td></tr>
    <tr><td>10GigEthernet</td><td>SNFCCAMK34T-TenGigE0/13/0/0</td></tr>
    <tr><td>10GigEthernet</td><td>SNFCCAMK34T-TenGigE0/13/3/0</td></tr>
    <tr><td>10GigEthernet</td><td>SNFCCAMK34T-TenGigE0/3/0/0</td></tr>
    <tr><td>10GigEthernet</td><td>SNFCCAMK34T-TenGigE0/3/0/0-5</td></tr>
    ... 700,000 more lines ...
  </table>        </BODY>
</HTML>

Что бы я хотел в CSV:

Subtype,Object
10GigEthernet,SNFCCAMK34T-TenGigE0/10/0/0
10GigEthernet,SNFCCAMK34T-TenGigE0/13/0/0
10GigEthernet,SNFCCAMK34T-TenGigE0/13/3/0
10GigEthernet,SNFCCAMK34T-TenGigE0/3/0/0
10GigEthernet,SNFCCAMK34T-TenGigE0/3/0/0-5

Буду признателен за любую помощь, которую вы можете оказать! Заранее спасибо.

Результат от кода @ shellter:

# wget http://10.1.1.2/reports/file.pl
--2012-01-19 06:56:59--  http://10.1.1.2/reports/file.pl
Connecting to 10.1.1.2... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified
Saving to: `file.pl'

    [          <=>                          ] 61,000,000  1.01M/s   in 58s     

2012-01-19 06:58:00 (1.01 MB/s) - `file.pl' saved [61000000]

# sed -n '/<\/td>/{
>            s@<tr><td>@@;
>            s@</td>@XaYbZc@;
>            s@<td>@@;
>            s@</td></tr>@@;
>            s/XaYbZc/,/
>            s/^    //
>            p
>           }' file.pl > routerList.csv
# ls -l
total 203408
-rw-r--r--   1 root     root     61000000 Jan 19 06:58 file.pl
-rw-r--r--   1 root     root     42708247 Jan 19 06:58 routerList.csv
# head routerList.csv
10GigEthernetn,SNFCCAMK34T-TenGigE0/10/0/0
10GigEthernetn,SNFCCAMK34T-TenGigE0/13/0/0
10GigEthernetn,SNFCCAMK34T-TenGigE0/13/3/0
10GigEthernetn,SNFCCAMK34T-TenGigE0/3/0/0
10GigEthernetn,SNFCCAMK34T-TenGigE0/3/0/0-5

Ответы [ 5 ]

1 голос
/ 19 января 2012

Хотя я вынужден согласиться с большинством комментариев, таких как «использовать DOM или XPATH и т. Д.», В этом случае вам повезло, что все данные, которые вы хотите обработать, находятся в одной строке.Если в этих данных когда-либо будут разрывы строк, то это не будет работать, и будет практически невозможно получить работающее решение.Поэтому, предупредив об этих проблемах, попробуйте это

 wget http://10.1.1.2/file.pl

 sed -n '/<\/td>/{
           s@<tr><td>@@;
           s@</td>@XaYbZc@;
           s@<td>@@;
           s@</td></tr>@@;
           s/XaYbZc/,/
           s/^    //
           p
          }' file.pl > routerList.csv

cat routerList.csv
10GigEthernet,SNFCCAMK34T-TenGigE0/10/0/0
10GigEthernet,SNFCCAMK34T-TenGigE0/13/0/0
10GigEthernet,SNFCCAMK34T-TenGigE0/13/3/0
10GigEthernet,SNFCCAMK34T-TenGigE0/3/0/0
10GigEthernet,SNFCCAMK34T-TenGigE0/3/0/0-5

Сценарий sed использует символ '@' в качестве разделителя раздела совпадения / замены.

Сначала мы берем первое <tr><td> ви удалите ее,

Затем мы берем первое </td> и заменяем его на XaYbZc в качестве временного маркера.

Удаляем оставшееся отверстие <td>.

Удалите завершающий символ </td></tr>

Замените временный XaYbZc на ','

Удалите 4 пробела в начале строки.

Распечатайте буфер.(Готово!)

Надеюсь, это поможет.

0 голосов
/ 18 ноября 2012

Все ответы на данный момент говорят: «Вы должны сделать это правильно», а затем показать, как это сделать «Неправильный путь». Вот пример правильного пути. В этой версии используется синтаксический анализатор DOM (в частности, Mojo::DOM, хотя другие будут работать аналогично) и Text::CSV.

#!/usr/bin/env perl

use strict;
use warnings;

# Use this for real
#use Mojo::UserAgent;
#my $ua = Mojo::UserAgent->new;
#my $dom = $ua->get('http://10.1.1.2/file.pl')->res->dom;

# Use this for test
use Mojo::DOM;
my $dom = Mojo::DOM->new(do { local $/; <DATA> });

# Common code (test and real)

use Text::CSV;
my $csv = Text::CSV->new;
my $output;

sub append_row {
  return unless @_;
  $csv->combine(@_) or die $csv->status();
  $output .= $csv->string() . "\n"; 
}

my $table = $dom->find('table')->[1];
append_row( $table->find('th')->pluck('all_text')->each );

$table->find('tr')->each(sub{
  append_row( $_->find('td')->pluck('text')->each );
});

print $output;


__DATA__
<HTML>
  <HEAD>
    <TITLE>Objects</TITLE>
    <STYLE type="text/css">
    a:hover
    {
    color:red
    }
    </STYLE>
    </HEAD>
  <BODY>
  <IMG src="http://10.1.1.2/images/logo.gif"/>
  <BR/><BR/>
  <TABLE border="0">
    <TR>
      <TH>Objects</TH>
    </TR>
    <TR>
      <TD><HR style="width:227px"></TD>
    </TR>
  </TABLE>
  <table border=1 cellpadding=5 cellspacing=0><tr><th><b>Subtype</b></th><th><b>Object</b>    </th></tr>
    <tr><td>10GigEthernet</td><td>SNFCCAMK34T-TenGigE0/10/0/0</td></tr>
    <tr><td>10GigEthernet</td><td>SNFCCAMK34T-TenGigE0/13/0/0</td></tr>
    <tr><td>10GigEthernet</td><td>SNFCCAMK34T-TenGigE0/13/3/0</td></tr>
    <tr><td>10GigEthernet</td><td>SNFCCAMK34T-TenGigE0/3/0/0</td></tr>
    <tr><td>10GigEthernet</td><td>SNFCCAMK34T-TenGigE0/3/0/0-5</td></tr>
    ... 700,000 more lines ...
  </table>        </BODY>
</HTML>

В результате

Subtype,Object
10GigEthernet,SNFCCAMK34T-TenGigE0/10/0/0
10GigEthernet,SNFCCAMK34T-TenGigE0/13/0/0
10GigEthernet,SNFCCAMK34T-TenGigE0/13/3/0
10GigEthernet,SNFCCAMK34T-TenGigE0/3/0/0
10GigEthernet,SNFCCAMK34T-TenGigE0/3/0/0-5

очень похож на другие, но обрабатывает все виды крайних случаев. На мой взгляд, с современными парсерами DOM (или даже XPath) сделать это правильно проще, чем создавать регулярное выражение в любом случае, плюс вы избежите всех ловушек, возникающих из-за неправильного выполнения; так почему бы просто не сделать это правильно первым делом?

0 голосов
/ 19 января 2012

Это может работать для вас:

 sed '1i\Subtype,Object'$'\n''/^\s*<tr><td>/!d;s/\s*<tr>\|<\/tr>\s*//g;s/<td>\([^<]*\)<\/td>/\1,/g;s/.$//' file
Subtype,Object
10GigEthernet,SNFCCAMK34T-TenGigE0/10/0/0
10GigEthernet,SNFCCAMK34T-TenGigE0/13/0/0
10GigEthernet,SNFCCAMK34T-TenGigE0/13/3/0
10GigEthernet,SNFCCAMK34T-TenGigE0/3/0/0
10GigEthernet,SNFCCAMK34T-TenGigE0/3/0/0-5
0 голосов
/ 19 января 2012

Быстро и грязно с Perl и его XML::LibXML модулем (который не входит в стандартную комплектацию Perl, но обычно его легко установить, если вы знаете, как устанавливать модули CPAN):

/tmp % xpath -He '//td//text()' test.html | perl -pe '$x=1-$x and s#$/#,#'
10GigEthernet,SNFCCAMK34T-TenGigE0/10/0/0
10GigEthernet,SNFCCAMK34T-TenGigE0/13/0/0
10GigEthernet,SNFCCAMK34T-TenGigE0/13/3/0
10GigEthernet,SNFCCAMK34T-TenGigE0/3/0/0
10GigEthernet,SNFCCAMK34T-TenGigE0/3/0/0-5

Здесь xpath - это простой Perl-скрипт, который я написал для выбора материала из документов XML / HTML с использованием XPath. Вторая команда Perl - быстрый и грязный способ переформатировать результаты в формат с двумя столбцами, это потерпит неудачу, если у вашего документа есть другие виды <td/> s, которых вы не хотите видеть в выводе.

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

0 голосов
/ 19 января 2012

Я бы отказался от использования Right Way (используя настоящий парсер) и просто обработал бы его с помощью регулярного выражения.

Это (на Perl) хрупкое и подверженное ошибкам, но должно быть настолько быстрым, насколько вы можете ...

print "$1,$2\n" while $html =~ /<tr><td>([^<]+)<\/td><td>([^<]+)/g;
...