使用正则表达式解析HTML是一个坏主意,但它似乎适合这种情况。
描述:给定.html文件,我必须解析内部链接,提取缩进级别,链接文本及其驻留的页码到外部.txt文件,然后传递给其他人。< / p>
所以给出了这个示例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 “Offering“</FONT>
</A></DIV></TD>
<TD> </TD>
<TD nowrap align="right" valign="top"> </TD>
<TD align="right" valign="top">1</TD>
<TD nowrap valign="top"> </TD>
</TR>
<TR valign="bottom">
<TD valign="top"><DIV style="margin-left:15px; text-indent:-0px"><A href="#102">Sales & Property
</A></DIV></TD>
<TD> </TD>
<TD nowrap align="right" valign="top"> </TD>
<TD align="right" valign="top">2</TD>
<TD nowrap valign="top"> </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));
};
这将正确地提取第二个,但不是第一个,因为&gt;&lt;和HTML代码中的其他符号。
如果我将最后一个捕获组更改为.+
或.*
,我会获得整个HTML文件(在第一个<Div><A>
和最后一个</A>
之间。似乎模式从头开始,但从文件末尾向后匹配。
以下是在线正则表达式构建器的链接:http://regexr.com?2s0po
它正确地找到了我需要的东西,但是在Perl中我没有得到相同的结果(只是提到的整个文件)。
我似乎无法编写任何可以正确捕获每个组的内容 - 您会认为“光标”会向前移动并停在它从文件开头看到的第一个</A>
。
非常感谢任何帮助或意见或指导。 - 谢谢你。
答案 0 :(得分:3)
解析HTML或类似结构时,必须小心使用正则表达式。你正在尝试的正则表达式有两个问题:
这是一个处理这些问题的正则表达式:
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";
}
答案 1 :(得分:2)
我不会用正则表达式来做这件事。
例如,使用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 “Offering“</FONT>
</A></DIV></TD>
<TD> </TD>
<TD nowrap align="right" valign="top"> </TD>
<TD align="right" valign="top">1</TD>
<TD nowrap valign="top"> </TD>
</TR>
<TR valign="bottom">
<TD valign="top"><DIV style="margin-left:15px; text-indent:-0px"><A href="#102">Sales & Property
</A></DIV></TD>
<TD> </TD>
<TD nowrap align="right" valign="top"> </TD>
<TD align="right" valign="top">2</TD>
<TD nowrap valign="top"> </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
答案 2 :(得分:1)
您可以尝试使用.+?
或.*?
进行非贪婪的匹配,以防止其对文件的其余部分进行诽谤。