在 perl 中解析表数据的问题
Posted
技术标签:
【中文标题】在 perl 中解析表数据的问题【英文标题】:Trouble with parsing table data in perl 【发布时间】:2011-05-16 02:41:45 【问题描述】:我有一个很长的类似模式的 htdoc,如下所示:
<td class="MODULE_PRODUCTS_CELL " align="center" valign="top" >
<table summary="products"><tr>
<td align="center" >
<a href="/collections.php?prod_id=50">
<img src="files/products_categories50_t.txt" border="0" /></a><\br>
</td>
</tr>
<tr>
<td align="center">
<a href="/collections.php?prod_id=50"><strong>Buffer</strong><br />
</a>
<td>
</tr></table>
</td>
在上面我要提取的html中:
collections.php?prod_id=50
files/products_categories50_t.txt
Buffer
我已经尝试过这个代码,
#!/usr/local/bin/perl
use strict;
use warnings;
my $filename = 'sr.txt';
open(FILENAME,$filename);
my @str = <FILENAME>;
chomp(@str);
#print "@str";
foreach my $str(@str)
if ($str =~/<td class(.*)<a href(.*?)><\/td>/)
print "*****$2\n";
此代码是试用版。然而,它只带来最后一次出现,而不是每次出现。为什么?
【问题讨论】:
如果您希望我们努力解决您的问题,那么您可能应该努力确保其格式尽可能易于阅读。 ...或者,更好的是,使用[^>]*
而不是.*
或.*?
,因为您要查找任意数量的非>
字符,而不是任意数量的任意字符性格。否定字符类版本通常比非贪婪的“匹配任何东西”处理得更快,而且它更清楚地向人类读者表达了你的意图。
@Chris,不,这个答案不足以回答所有此类问题。这是个笑话;它没有解释事情。如果您真的认为这是回答人们的正确方法,请继续并使其成为真正的答案,以便我们对其进行投票。否则就是无脑鹦鹉学舌,对用户一点帮助也没有。
@tchrist - 不同意。我显然不认为它应该是一个答案,因为我没有将它作为一个答案发布,并且作为评论,对其他人来说可能更幽默,但它本质上与 OP 正在做的事情相同,答案确实说“使用 HTML 解析器”这是处理这种情况的正确方法(尽管经过很多可能不必要的设置)。评论与答案不同。这就是为什么我发表的评论有 6 个赞成票,而其他人发布的答案只有一个(和两个反对票)。
@DVK:我对给出“照我说的做,而不是照我做的”建议的虚伪感到非常不舒服。这里的人们几乎总是错误地描述问题和解决方案,这让我很恼火。他们过于随意地使用“parse”,这意味着无非是suss out 或munge。提问者从不解释真正的限制,而给出答案的人也几乎从不解释。 没有更深入理解的声音字节的死记硬背会过度简化成谎言,并导致盲目的货物崇拜编程。 正则表达式非常适合对 x/ʜᴛᴍʟ 进行词法分析,而且在受到良好约束的片段上也能很好地工作。解析是另一回事。
【参考方案1】:
总结
在少量、有限的、定义合理的 HTML 片段上使用模式既快速又简单。但是,在包含不可预见的怪癖的完全通用的、开放式的 HTML 的整个文档上使用它们,虽然理论上是可能的,但与使用已经为此目的而编写的其他人的解析器相比,实际上太难了。有关在 XML 或 HTML 上使用模式的更一般性讨论,另请参阅 this answer。
朴素的正则表达式解决方案
您要求提供正则表达式解决方案,所以我会为您提供。
#!/usr/bin/perl
use 5.10.0;
use strict;
use warnings;
$/ = undef;
$_ = <DATA>; # read all input
while (m < \s* img [^>]* src \s* = \s* ['"]? ([^<>'"]+) gsix)
print "IMG SRC=$1\n";
while (m < \s* a [^>]* href \s* = \s* ['"]? ([^<>'"]+) gsix)
print "A HREF=$1\n";
while (m < \s* strong [^>]* > (.*?) < \s* / \s* strong \s* > gsix)
print "STRONG=$1\n";
__END__
<td class="MODULE_PRODUCTS_CELL" align="center" valign="top" >
<table summary="products">
<tr>
<td align="center" >
<a href="/collections.php?prod_id=50">
<img src="files/products_categories50_t.txt" border="0" />
</a>
<br/>
</td>
</tr>
<tr>
<td align="center">
<a href="/collections.php?prod_id=50">
<strong>Buffer</strong><br />
</a>
<td>
</tr>
</table>
</td>
该程序在运行时会产生以下输出:
IMG SRC=files/products_categories50_t.txt
A HREF=/collections.php?prod_id=50
A HREF=/collections.php?prod_id=50
STRONG=Buffer
如果您非常确定它适用于您希望的特定 HTML 样本,那么请务必使用它。注意我做了几件你没有做的事情。其中之一不是一次处理一行 HTML。这实际上是行不通的。
但是,这种排序解决方案仅适用于极其有限形式的有效 HTML。只有当您可以保证您正在使用的 HTML 看起来确实符合您的预期时,您才能使用它。
问题是它通常看起来并不整洁。对于这些情况,强烈建议您使用 HTML 解析类。但是,似乎没有人向您展示执行此操作的代码。这不是很有帮助。
向导级正则表达式解决方案
我自己也将成为他们中的一员。因为我将向您展示一个更通用的解决方案来接近我认为您的看法,但与其他任何曾在 Stack Overflow 上发帖的人不同,我将使用正则表达式来做到这一点,只是为了向您展示可以做到,但您不希望这样做:
#!/usr/bin/perl
use 5.10.0;
use strict;
use warnings;
$/ = undef;
$_ = <DATA>; # read all input
our(
$RX_SUBS,
$tag_template_rx,
$script_tag_rx,
$style_tag_rx,
$strong_tag_rx,
$a_tag_rx,
$img_tag_rx,
);
# strip stuff we aren't supposed to look at
s <! DOCTYPE .*? > sx;
s <! \[ CDATA \[ .*? \]\] > gsx;
s $style_tag_rx .*? < (?&WS) / (?&WS) style (?&WS) > gsix;
s $script_tag_rx .*? < (?&WS) / (?&WS) script (?&WS) > gsix;
s <!-- .*? --> gsx;
while (/$img_tag_rx/g)
my $tag = $+TAG;
printf "IMG tag at %d: %s\n", pos(), $tag;
while ($tag =~
m
$RX_SUBS
\b src (?&WS) = (?&WS)
(?<VALUE>
(?: (?"ed_value) | (?&unquoted_value) )
)
gsix)
my $value = dequote($+VALUE);
print "\tSRC is $value\n";
while (/$a_tag_rx/g)
my $tag = $+TAG;
printf "A tag at %d: %s\n", pos(), $tag;
while ($tag =~
m
$RX_SUBS
\b href (?&WS) = (?&WS)
(?<VALUE>
(?: (?"ed_value) | (?&unquoted_value) )
)
gsix)
my $value = dequote($+VALUE);
print "\tHREF is $value\n";
while (m
$strong_tag_rx (?&WS)
(?<BODY> .*? ) (?&WS)
< (?&WS) / (?&WS) strong (?&WS) >
gsix)
my ($tag, $body) = @+ qw< TAG BODY > ;
printf "STRONG tag at %d: %s\n\tBODY=%s\n",
pos(), $+TAG, $+BODY;
exit;
sub dequote
my $string = shift();
$string =~ s
^
(?<quote> ["'] )
(?<BODY>
(?: (?! \k<quote> ) . ) *
)
\k<quote>
$
$+BODYgsx;
return $string;
sub load_patterns
$RX_SUBS = qr (?(DEFINE)
(?<any_attribute>
\b \w+
(?&WS) = (?&WS)
(?:
(?"ed_value)
| (?&unquoted_value)
)
)
(?<unquoted_value>
(?&unwhite_chunk)
)
(?<quoted_value>
(?<quote> ["'] )
(?: (?! \k<quote> ) . ) *
\k<quote>
)
(?<unwhite_chunk>
(?:
# (?! [<>'"] )
(?! > )
\S
) +
)
(?<WS> \s * )
(?<end_tag>
(?&html_end_tag)
| (?&xhtml_end_tag)
)
(?<html_end_tag> > )
(?<xhtml_end_tag> / > )
) # end DEFINE
six;
my $_TAG_SUBS = $RX_SUBS . q (?(DEFINE)
(?<attributes>
(?:
(?&WS)
(?&one_attribute)
) *
)
(?<one_attribute>
(?= (?&legal_attribute) )
(?&any_attribute)
)
(?<optional_attribute>
(?&permitted_attribute)
| (?&deprecated_attribute)
)
(?<legal_attribute>
(?: (?&required_attribute)
| (?&optional_attribute)
| (?&standard_attribute)
| (?&event_attribute)
# for LEGAL parse only, comment out next line
| (?&illegal_attribute)
)
)
(?<optional_attribute>
(?&permitted_attribute)
| (?&deprecated_attribute)
)
(?<illegal_attribute> \b \w+ \b )
(?<tag>
(?&start_tag)
(?&WS)
(?&attributes)
(?&WS)
(?&end_tag)
)
) # end DEFINE
; # this is a q tag, not a qr
$tag_template_rx = qr
$_TAG_SUBS
(?<TAG> (?&XXX_tag) )
(?(DEFINE)
(?<XXX_tag> (?&tag) )
(?<start_tag> < (?&WS) XXX \b )
(?<required_attribute> (*FAIL) )
(?<standard_attribute> (*FAIL) )
(?<event_attribute> (*FAIL) )
(?<permitted_attribute> (*FAIL) )
(?<deprecated_attribute> (*FAIL) )
) # end DEFINE
six;
$script_tag_rx = qr
$_TAG_SUBS
(?<TAG> (?&script_tag) )
(?(DEFINE)
(?<script_tag> (?&tag) )
(?<start_tag> < (?&WS) style \b )
(?<required_attribute> type )
(?<permitted_attribute>
charset
| defer
| src
| xml:space
)
(?<standard_attribute> (*FAIL) )
(?<event_attribute> (*FAIL) )
(?<deprecated_attribute> (*FAIL) )
) # end DEFINE
six;
$style_tag_rx = qr
$_TAG_SUBS
(?<TAG> (?&style_tag) )
(?(DEFINE)
(?<style_tag> (?&tag) )
(?<start_tag> < (?&WS) style \b )
(?<required_attribute> type )
(?<permitted_attribute> media )
(?<standard_attribute>
dir
| lang
| title
| xml:lang
)
(?<event_attribute> (*FAIL) )
(?<permitted_attribute> (*FAIL) )
(?<deprecated_attribute> (*FAIL) )
) # end define
six;
$strong_tag_rx = qr
$_TAG_SUBS
(?<TAG> (?&strong_tag) )
(?(DEFINE)
(?<strong_tag> (?&tag) )
(?<start_tag>
< (?&WS)
strong
\b
)
(?<standard_attribute>
class
| dir
| ltr
| id
| lang
| style
| title
| xml:lang
)
(?<event_attribute>
on click
on dbl click
on mouse down
on mouse move
on mouse out
on mouse over
on mouse up
on key down
on key press
on key up
)
(?<required_attribute> (*FAIL) )
(?<permitted_attribute> (*FAIL) )
(?<optional_attribute> (*FAIL) )
(?<deprecated_attribute> (*FAIL) )
) # end DEFINE
six;
$a_tag_rx = qr
$_TAG_SUBS
(?<TAG> (?&a_tag) )
(?(DEFINE)
(?<a_tag> (?&tag) )
(?<start_tag>
< (?&WS)
a
\b
)
(?<permitted_attribute>
charset
| coords
| href
| href lang
| name
| rel
| rev
| shape
| rect
| circle
| poly
| target
)
(?<standard_attribute>
access key
| class
| dir
| ltr
| id
| lang
| style
| tab index
| title
| xml:lang
)
(?<event_attribute>
on blur
| on click
| on dbl click
| on focus
| on mouse down
| on mouse move
| on mouse out
| on mouse over
| on mouse up
| on key down
| on key press
on key up
)
(?<required_attribute> (*FAIL) )
(?<deprecated_attribute> (*FAIL) )
) # end define
xi;
$img_tag_rx = qr
$_TAG_SUBS
(?<TAG> (?&image_tag) )
(?(DEFINE)
(?<image_tag> (?&tag) )
(?<start_tag>
< (?&WS)
img
\b
)
(?<required_attribute>
alt
| src
)
# NB: The white space in string literals
# below DOES NOT COUNT! It's just
# there for legibility.
(?<permitted_attribute>
height
| is map
| long desc
| use map
| width
)
(?<deprecated_attribute>
align
| border
| hspace
| vspace
)
(?<standard_attribute>
class
| dir
| id
| style
| title
| xml:lang
)
(?<event_attribute>
on abort
| on click
| on dbl click
| on mouse down
| on mouse out
| on key down
| on key press
| on key up
)
###########################
) # end DEFINE
six;
UNITCHECK load_patterns()
__END__
<td class="MODULE_PRODUCTS_CELL" align="center" valign="top" >
<table summary="products">
<tr>
<td align="center" >
<a href="/collections.php?prod_id=50">
<img src="files/products_categories50_t.txt" border="0" />
</a>
<br/>
</td>
</tr>
<tr>
<td align="center">
<a href="/collections.php?prod_id=50">
<strong>Buffer</strong><br />
</a>
<td>
</tr>
</table>
</td>
该程序在运行时会产生以下输出:
IMG tag at 304: <img src="files/products_categories50_t.txt" border="0" />
SRC is files/products_categories50_t.txt
A tag at 214: <a href="/collections.php?prod_id=50">
HREF is /collections.php?prod_id=50
A tag at 451: <a href="/collections.php?prod_id=50">
HREF is /collections.php?prod_id=50
STRONG tag at 491: <strong>
BODY=Buffer
选择是你的——还是是它?
这两个都解决了你的正则表达式问题。 可能您将能够使用我的两种方法中的第一种。我不能说,因为就像这里提出的所有此类问题一样,您还没有告诉我们足够的数据让我们(也许还有您)确定这种幼稚的方法是否足够。
如果没有,你有两个选择。
-
您可以使用我的第二种技术提供的更强大、更灵活的方法。只需确保您了解它的所有方面,否则您将无法维护您的代码,其他任何人也一样。
使用 HTML 解析类。
我发现即使是 1000 人中的一个人也不太可能合理地做出这两个选择中的第一个。特别是,我发现像我的第一个解决方案中那样简单的正则表达式寻求帮助的人极不可能是能够管理我的第二个解决方案中给出的正则表达式的人。
这真的让你只有一个“选择”——如果我可以这么随意地使用这个词的话。
【讨论】:
我向反对者重复一遍:如果您不同意我建议他们不要为此使用正则表达式的建议,请提供替代方案。我怀疑你们都没有阅读我写的东西,并且不知何故认为我在建议与我真实的相反的东西。请不要傻。【参考方案2】:您可能会发现使用 XPath 解析它比使用正则表达式更容易。不过,您的数据可以在语义上更加结构化,但我想这可能不在您的掌控之中。
看看XML::XPath。
The 10-Minute XPath Tutorial 来自 Automating System Administration with Perl 也可能很方便。
【讨论】:
如果它在 perl 正则表达式中会很棒 @user510749:你说如果这是一个 perl 正则表达式会很棒。我不确定接下来会发生什么,但我在本页其他地方的回答中为您提供了两种不同的 perl 正则表达式方法。他们是否很棒,我留给你判断。顺便说一句,如果你编辑你的用户资料,你可以给自己取一个名字,而不仅仅是@user510749。 我发现HTML::TreeBuilder::XPath 的 API 比 XML::XPath 有用得多。 我不熟悉 H::T::XPath,在这种情况下它看起来确实更有用一些。但我试图使用 XPath 而不是正则表达式。 @daxim:这也是我的经历。【参考方案3】:#!perl
while(<DATA>)
print "$1\n" while (m/href="([^"]+)/gi)
__DATA__
<td class="MODULE_PRODUCTS_CELL " align="center" valign="top" >
<table summary="products">
<tr>
<td align="center" >
<a href="/collections.php?prod_id=50">
<img src="files/products_categories50_t.txt" border="0" />
</a>
<br/>
</td>
</tr>
<tr>
<td align="center">
<a href="/collections.php?prod_id=50">
<strong>Buffaer</strong><br />
</a>
<td>
</tr>
</table>
</td
【讨论】:
如果你要否决这个解决方案,你应该说明原因,因为它似乎符合 OP 的要求。 是的,这是怎么回事? :( 套用 Knuth 的话说:任何使用正则表达式解析 HTML 的人都生活在罪恶之中。【参考方案4】:首先,您的代码只读取输入的第一行。如果你想遍历输入的所有行,你应该使用这个:
while($str = <FILENAME>)
chomp $str;
假设您的输入格式正确,href 属性始终位于“a”标签之后,src 属性始终位于“img”标签之后,并且您的 URL 中没有空格,您也没有每行有多个强标签,那么您可以使用以下内容:
open(FILENAME, 'sr.txt') || die "$!\n";
while($str = <FILENAME>)
chomp $str;
if( $str =~ /<a href=\"(\S+)"/ )
print "$1\n";
elsif( $str =~ /<img src="(\S+)"/ )
print "$1\n";
elsif( $str =~ /<strong>(.*)<\/strong>/ )
print "$1\n";
这段代码很丑陋,但它可以满足你的需求。按照 ptomli 的建议,使用 XPath 进行解析会更容易,也更优雅。
【讨论】:
在所有其他条件相同的情况下,在这些行中看到$_ =~
确实有点奇怪。
@tchrist 编辑解决方案以匹配问题中使用的 $str 变量以上是关于在 perl 中解析表数据的问题的主要内容,如果未能解决你的问题,请参考以下文章
当 Perl 的 DBI 在准备语句时遇到错误时,如何避免程序退出?
写个脚本使用perl或shell对比oracle表数据,急啊,高分悬赏!