【问题标题】:How can I extract the text from links on an HTML page in Perl?如何从 Perl 中的 HTML 页面上的链接中提取文本?
【发布时间】:2012-11-15 03:32:21
【问题描述】:

我不是 Perl 专家,但我编写了一个 Perl 脚本来解析 HTML 页面并按所有 href 标签过滤:

输出如下图:

href="?Name">Name</a>
href="?Desc">Hourly Details</a>
href="/24x7/2012/11-November/">Data
href="./00:00:00/">00:00:00/</a>
href="./01:00:00/">01:00:00/</a>
href="./02:00:00/">02:00:00/</a>
href="./03:00:00/">03:00:00/</a>
href="./04:00:00/">04:00:00/</a>
href="./05:00:00/">05:00:00/</a>
href="./06:00:00/">06:00:00/</a>
href="./07:00:00/">07:00:00/</a>
href="./08:00:00/">08:00:00/</a>
href="./09:00:00/">09:00:00/</a>
href="./10:00:00/">10:00:00/</a>
href="./11:00:00/">11:00:00/</a>
href="./12:00:00/">12:00:00/</a>
href="./13:00:00/">13:00:00/</a>
href="./14:00:00/">14:00:00/</a>
href="./15:00:00/">15:00:00/</a>
href="./16:00:00/">16:00:00/</a>
href="./17:00:00/">17:00:00/</a>
href="./18:00:00/">18:00:00/</a>
href="./19:00:00/">19:00:00/</a>
href="./20:00:00/">20:00:00/</a>
href="./21:00:00/">21:00:00/</a>
href="./22:00:00/">22:00:00/</a>
href="./23:00:00/">23:00:00/</a>

现在我想从“00:00:00”到“23:00:00”提取href标签中的值,同时排除其他值。结果值将被添加到具有 URL 的字符串中:

http://x.download.com/00:00:00
------URL------------/..href../
..............................
http://x.download.com/23:00:00

但是通过尝试以下代码:

foreach (@tag) {
    if (m/href/) {
        if ($_ =~ /"\/24/ && $_ =~ /"\/[0-9]/) {
            my $href  = $_;
            my $start = index($href, "\"");
            my $end   = rindex($href, "\"");
            my $link  = substr($href, $start + 1, $end - $start - 1);
            print "Follow: " . $url . $link . "\n";

        }
    }
}

打印:

Follow: http://x.download.com/24x7/2012/11-November/

我的正则表达式应该是什么才能实现所需的目标?

【问题讨论】:

    标签: perl html-parsing


    【解决方案1】:

    首先,我们需要指定一个正则表达式,它可以将军事时间精确到秒。

    my $regex 
        = qr{  # curly brackets instead of slashes
               # so that we can use literal slashes in expression
    
        "   # a quote
        \.  # a literal dot
        /   # a forward slash
        (   # begin capture group
    
           (?:              # begin uncaptured sub-group
               [01] \d      # a '0' or '1' followed by a digit
           |   2    [0-3]   # a '2' followed by 0-3
           )                # end grouping
           (?:         # begin repetition grouping
             :         # a literal colon               
             [0-5] \d  # digits 0-5 followed by any digit
           ){2}        # exactly twice
         )  # end capture
    
         /  # a forward slash
         "  # close quote
    }x; # <- x-option allows annotated regex
    ...
    

    这相当于下面的正则表达式:

    my $regex = qr/"\.\/((?:[01]\d|2[0-3])(:[0-5]\d){2})\/"/;
    

    如果你的分和秒永远都是'00:00',那么表达式就更简单了:

    my $regex = qr{"\./((?:[01]\d|2[0-3]):00:00)/"};
    

    然后您可以测试并通过在 list 上下文中进行匹配来检索值:

    if ( my ( $link ) = m/$regex/ ) { 
        say "http://x.download.com/$link";
    }
    

    如果测试不匹配,$link 将未定义。如果确实匹配,则将其声明为(一个)列表,匹配操作会将第一个捕获分配给变量。

    【讨论】:

    • 谢谢,但我得到的最终链接如下:x.download.com./23:00:00 - 请注意x.download.com 后面的一个额外点
    • @Prakash,我认为这将是您的提取代码,我没有更改,但在下一个版本中,我添加了我的提取代码。最好在捕获正则表达式中指定要跳过的内容。
    • 感谢您提供的解释和示例。我确实看到了问题所要求的预期输出
    【解决方案2】:

    这可以通过正则表达式非常简单地完成,如下面的程序所示。它查找紧跟在&gt; 之后的一串数字或冒号(因此查找元素的文本内容,而不是像您的那样查找href 属性值)并将该字符串捕获到$1。

    但我更愿意看到使用适当的 HTML 解析器从头到尾解决问题,例如 HTML::TreeBuilder 要么 Mojo::DOM.

    use strict;
    use warnings;
    
    my @tag = <DATA>;
    
    foreach (@tag) {
      next unless />([\d:]+)/;
      print "http://x.download.com/$1\n";
    }
    
    __DATA__
    href="?Name">Name</a>
    href="?Desc">Hourly Details</a>
    href="/24x7/2012/11-November/">Data
    href="./00:00:00/">00:00:00/</a>
    href="./01:00:00/">01:00:00/</a>
    href="./02:00:00/">02:00:00/</a>
    href="./03:00:00/">03:00:00/</a>
    href="./04:00:00/">04:00:00/</a>
    href="./05:00:00/">05:00:00/</a>
    href="./06:00:00/">06:00:00/</a>
    href="./07:00:00/">07:00:00/</a>
    href="./08:00:00/">08:00:00/</a>
    href="./09:00:00/">09:00:00/</a>
    href="./10:00:00/">10:00:00/</a>
    

    输出

    http://x.download.com/00:00:00
    http://x.download.com/01:00:00
    http://x.download.com/02:00:00
    http://x.download.com/03:00:00
    http://x.download.com/04:00:00
    http://x.download.com/05:00:00
    http://x.download.com/06:00:00
    http://x.download.com/07:00:00
    http://x.download.com/08:00:00
    http://x.download.com/09:00:00
    http://x.download.com/10:00:00
    

    【讨论】:

    • 很抱歉,您能否提供我在 while 循环中出现 时应该使用的内容 - 我在程序中遇到错误。如果您可以像上面的示例中那样使用@tag 变量,那将会很有帮助
    • 我使用了DATA,以便可以在 Perl 源代码中包含示例数据。我已经修改了我的解决方案,以便它在处理之前将数据读入@tag,按照您的要求
    【解决方案3】:

    你不想用正则表达式来做。您需要一个合适的 HTML 解析器,而正则表达式无法完成这项工作。

    您如何获取网页?如果您使用的是 WWW::Mechanize,那么从您获取的页面中提取链接是一个方法调用,因为 WWW::Mechanize 会为您解析 HTML。

    use WWW::Mechanize;
    
    my $mech = WWW::Mechanize->new();
    $mech->get( $url );
    
    my @links = $mech->links();
    for my $link ( @links ) {
        say $link->text, ' -> ', $link->url; # Show the text and the URL
    }
    

    您需要根据需要重新格式化,但这会给您一个想法。

    【讨论】:

    • 我正在使用 $browser->get( $url );获取网页,然后 $contents = $response->content;我的@breakup = split('', $contents);获取页面内容
    • 什么是$browser?你用什么课来做get?
    • 我正在使用 LWP lib 来获取网页。使用 LWP::Simple;my $browser; $browser->get($url);获取网页,然后 $contents = $response->content;我的@breakup = split('', $contents);获取页面内容
    • 所以很容易切换到机械化。 Mechanize 建立在 LWP 之上。然后你可以使用上面的代码sn -p,你的链接提取为你处理,你不必处理解析。您仍然可以直接使用 $mech-&gt;content 访问内容,就像使用 LWP::Simple 对象一样。
    • @Prakash,除了 Andy 在这里所说的之外,您还可以使用捕获正则表达式过滤链接,例如 say "http://x.download.com/$_" foreach map { $_-&gt;url =~ m/$regex/ } $mech-&gt;links;
    猜你喜欢
    • 2011-06-04
    • 2010-09-20
    • 1970-01-01
    • 2016-02-07
    • 2018-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多