解析perl中的表数据有问题

时间:2010-11-26 09:50:14

标签: html regex perl parsing

我有一个类似模式的长htdoc,就像这样:

<td class="MODULE_PRODUCTS_CELL " align="center" valign="top" height="100">
<table width="100" summary="products"><tr>
<td align="center" height="75">
<a href="/collections.php?prod_id=50">
<img src="files/products_categories50_t.txt" border="0" alt="products" /></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中我想提取:

  1. collections.php?prod_id=50
  2. files/products_categories50_t.txt
  3. Buffer
  4. 我已经尝试过这段代码,

    #!/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";
         }    
    }
    

    此代码是试用版。然而,它只带来最后一次出现,而不是每次出现。为什么呢?

4 个答案:

答案 0 :(得分:57)

概要

在很少,有限的合理定义的HTML片段上使用模式快速而简单。但是在整个文档中使用它们,包含完全一般的,开放式的不可预见的怪癖HTML,虽然理论上可行,但实际上与使用已经为此明确目的编写的其他人的解析器相比太难了。有关在XML或HTML上使用模式的更一般性讨论,另请参阅this answer

NaïveRegexSolution

您已经要求提供正则表达式解决方案,因此我会为您提供此类解决方案。

#!/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" height="100">
<table width="100" summary="products">
    <tr>
        <td align="center" height="75">
            <a href="/collections.php?prod_id=50">
                <img src="files/products_categories50_t.txt" border="0" alt="products" />
            </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> 
                (?: (?&quoted_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> 
                (?: (?&quoted_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> 
        $
    }{$+{BODY}}gsx;
    return $string;
}

sub load_patterns { 

    $RX_SUBS = qr{ (?(DEFINE)

        (?<any_attribute> 
            \b \w+
            (?&WS) = (?&WS) 
            (?:
                (?&quoted_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" height="100">
<table width="100" summary="products">
    <tr>
        <td align="center" height="75">
            <a href="/collections.php?prod_id=50">
                <img src="files/products_categories50_t.txt" border="0" alt="products" />
            </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" alt="products" />
        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

选择是你的 - 还是 它?

这两个都解决了你的正则表达式的问题。 可能你可以使用我的两种方法中的第一种。我不能说,因为在这里看似所有这些问题,你没有告诉我们足够多的关于我们(也许还有你)的数据,以确定这种天真的方法是否足够。

如果没有,你有两个选择。

  1. 您可以使用我的第二种技术提供的更强大和灵活的方法。只要确保你理解它的所有方面,否则你将无法维护你的代码 - 其他人也不会。
  2. 使用HTML解析类。
  3. 我发现即使1000人中的1人也不太可能合理地做出这两个选择中的第一个。特别值得一提的是,那些像我第一个解决方案那样简单地请求正则表达式帮助的人将是一个能够管理我的第二个解决方案中给出的正则表达式的人。

    这真的让你只有一个“选择” - 如果我可以如此松散地使用这个词。

答案 1 :(得分:5)

您可能会发现使用XPath解析此问题比使用正则表达式更容易。你的数据虽然可以用更多的语义结构,但我想这可能不在你手中。

查看XML::XPath

来自使用Perl自动化系统管理

The 10-Minute XPath Tutorial也可能很方便。

答案 2 :(得分:0)

#!perl

while(<DATA>) {
    print "$1\n" while (m/href="([^"]+)/gi)
}

__DATA__
<td class="MODULE_PRODUCTS_CELL " align="center" valign="top" height="100">
<table width="100" summary="products">
    <tr>
        <td align="center" height="75">
            <a href="/collections.php?prod_id=50">
                <img src="files/products_categories50_t.txt" border="0" alt="products" />
            </a>
            <br/>
        </td>
    </tr>
    <tr>
        <td align="center">
            <a href="/collections.php?prod_id=50">
                <strong>Buffaer</strong><br />
            </a>
        <td>
    </tr>
</table>
</td

答案 3 :(得分:0)

首先,您的代码只读取输入的第一行。如果要遍历输入的所有行,则应使用:

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会更容易,更优雅。