Perl регулярное выражение только для анализа вперед;не конец, чтобы начать - PullRequest
1 голос
/ 21 августа 2010

Парсинг HTML с помощью регулярных выражений - плохая идея, но она подходит для этой ситуации.

Описание: Учитывая файл .html, я должен проанализировать внутренние ссылки, извлечь уровень отступа, текст ссылки и номер страницы, на которой она находится, во внешний файл .txt, который затем передается кому-то еще.

Итак, учитывая этот пример HTML:

<TR valign="bottom">
    <TD valign="top"><DIV style="margin-left:0px; text-indent:-0px"><A href="#101"><FONT style="font-variant:small-caps;">The &#147;Offering&#147;</FONT>
</A></DIV></TD>
    <TD>&nbsp;</TD>
    <TD nowrap align="right" valign="top">&nbsp;</TD>
    <TD align="right" valign="top">1</TD>
    <TD nowrap valign="top">&nbsp;</TD>
</TR>
<TR valign="bottom">
    <TD valign="top"><DIV style="margin-left:15px; text-indent:-0px"><A href="#102">Sales &#038; Property
</A></DIV></TD>
    <TD>&nbsp;</TD>
    <TD nowrap align="right" valign="top">&nbsp;</TD>
    <TD align="right" valign="top">2</TD>
    <TD nowrap valign="top">&nbsp;</TD>
</TR>

Внешний файл выдаст:

0|The "Offering"|4
15|Sales & Property|5

(номера страниц различаются, потому что это фактический номер страницы, а не ссылка на фолио).

У меня это в основном разобрано, за исключением 1 части, когда текст ссылки содержит дополнительные коды HTML, такие как тег <Font> в первой ссылке.

Вот мое регулярное выражение для извлечения ссылок (примечание $ string содержит HTML выше):

while ($string =~ m/<DIV style="margin-left:([0-9]+)px; text-indent:[-0-9]+px"><A href="#([0-9]+)">([a-zA-Z0-9\.,:;&#\s]+)<\/A>/gi) {
    push(@indents,$1);
    push(@linkIDs,$2);
    push(@names,escapeHTML($3));
};

Это правильно извлечет второй, но не первый, из-за> <и других символов в коде HTML. </p>

Если я изменю эту последнюю группу захвата на .+ или .*, я получу весь файл HTML (ну, между первым <Div><A> и последним </A>. Кажется, что шаблон начинается с начало, но совпадает с концом файла назад.

Вот ссылка на онлайн-конструктор регулярных выражений: http://regexr.com? 2s0po
Он правильно находит то, что мне нужно, но в Perl я не получаю те же результаты (только весь упомянутый файл).

Кажется, я не могу написать что-нибудь, что будет правильно захватывать каждую группу - вы могли бы подумать, что «курсор» переместится вперед и остановится на первой </A>, которую он увидел с начала файла.

Любая помощь или мнения или рекомендации будут с благодарностью. -Спасибо.

Ответы [ 3 ]

3 голосов
/ 21 августа 2010

Вы должны быть осторожны с регулярным выражением при разборе HTML или подобных структур. Есть два вопроса с регулярным выражением, которые вы пытаетесь:

  1. Вложенные теги (шрифт-тег в первой записи)
  2. Разрывы строк (перед первым закрывающим тегом привязки)

Вот регулярное выражение, которое имеет дело с этими:

use HTML::Entities;
while ($string =~ m/<DIV style="margin-left:([0-9]+)px; text-indent:[-0-9]+px"><A href="#([0-9]+)">(.*?)<\/A>/gis) {
    my $indent = $1;
    my $page = $2;
    (my $name = $3) =~ s/\s+$//;
    $name =~ s/^\s+//;
    $name =~ s/<.*?>//g;
    print $indent, '|', decode_entities($name), '|', $page, "\n";
}
2 голосов
/ 21 августа 2010

Я бы не стал делать это с регулярным выражением.

Например, HTML :: TreeBuilder можно построить дерево с

#! /usr/bin/perl

use warnings;
use strict;

use HTML::TreeBuilder;
use HTML::TreeBuilder::XPath;

my $root = HTML::TreeBuilder->new_from_content(<<'EOHTML');
<TR valign="bottom">
    <TD valign="top"><DIV style="margin-left:0px; text-indent:-0px"><A href="#101"><FONT style="font-variant:small-caps;">The &#147;Offering&#147;</FONT>
</A></DIV></TD>
    <TD>&nbsp;</TD>
    <TD nowrap align="right" valign="top">&nbsp;</TD>
    <TD align="right" valign="top">1</TD>
    <TD nowrap valign="top">&nbsp;</TD>
</TR>
<TR valign="bottom">
    <TD valign="top"><DIV style="margin-left:15px; text-indent:-0px"><A href="#102">Sales &#038; Property
</A></DIV></TD>
    <TD>&nbsp;</TD>
    <TD nowrap align="right" valign="top">&nbsp;</TD>
    <TD align="right" valign="top">2</TD>
    <TD nowrap valign="top">&nbsp;</TD>
</TR>
EOHTML

, а затем извлеките ссылки и отступы, используя HTML :: TreeBuilder :: XPath :

sub all_text {
  my($root) = @_;

  ref $root
    ? join "" => map all_text($_) => $root->content_list
    : $root;
}

foreach my $div ($root->findnodes('/html/body//div[.//a]')) {
  my $indent =
    $div->attr('style') =~ /\bmargin-left:\s*(\d+)/ ? $1 : 0;

  foreach my $a ($div->findnodes('.//a')) {
    (my $text = all_text $a) =~ s/\s+\z//;
    print "$indent|$text|FIXME\n";
  }
}

Выход:

0|The �Offering�|FIXME
15|Sales & Property|FIXME
1 голос
/ 21 августа 2010

Вы можете попробовать не жадное совпадение, используя .+? или .*?, чтобы не допустить его выпадения до конца файла.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...