如何使用Perl从HTML中删除外部链接?

0 html perl

我试图从HTML文档中删除外部链接,但保留锚点,但我没有太多运气.以下正则表达式

$html =~ s/<a href=".+?\.htm">(.+?)<\/a>/$1/sig;
Run Code Online (Sandbox Code Playgroud)

将匹配锚标记的开头和外部链接标记的结尾,例如

<a HREF="#FN1" name="01">1</a>
some other html
<a href="155.htm">No. 155
</a> <!-- end tag not necessarily on the same line -->
Run Code Online (Sandbox Code Playgroud)

所以我最终没有任何东西而不是

<a HREF="#FN1" name="01">1</a>
some other html
Run Code Online (Sandbox Code Playgroud)

事实上,所有锚点的href属性都是大写的,所以我知道我可以做一个区分大小写的匹配,但我不想依赖它将来总是如此.

我可以更改的内容是否只匹配一个a标签?

Sin*_*nür 11

回应Chris Lutz的评论,我希望以下表明使用解析器非常简单(特别是如果你希望能够处理你还没有看过的输入<a class="external" href="...">),而不是使用脆弱的解决方案s///.

如果你打算采取这s///条路线,至少说实话,确实依赖href属性是大写,而不是提出灵活的幻觉.

编辑:按流行需求;-),这里是使用HTML :: TokeParser :: Simple的版本.仅使用HTML :: TokeParser查看版本的编辑历史记录.

#!/usr/bin/perl

use strict; use warnings;
use HTML::TokeParser::Simple;

my $parser = HTML::TokeParser::Simple->new(\*DATA);

while ( my $token = $parser->get_token ) {
    if ($token->is_start_tag('a')) {
        my $href = $token->get_attr('href');
        if (defined $href and $href !~ /^#/) {
            print $parser->get_trimmed_text('/a');
            $parser->get_token; # discard </a>
            next;
        }
    }
    print $token->as_is;
}

__DATA__
<a HREF="#FN1" name="01">1</a>
some other html
<a href="155.htm">No. 155
</a> <!-- end tag not necessarily on the same line -->
<a class="external" href="http://example.com">An example you
might not have considered</a>

<p>Maybe you did not consider <a
href="test.html">click here >>></a>
either</p>
Run Code Online (Sandbox Code Playgroud)

输出:

C:\Temp> hjk
<a HREF="#FN1" name="01">1</a>
some other html
No. 155 <!-- end tag not necessarily on the same line -->
An example you might not have considered

<p>Maybe you did not consider click here >>>
either</p>
Run Code Online (Sandbox Code Playgroud)

注意:如果链接的文件具有.html扩展名而不是,则检查为"正确"的正则表达式解决方案会中断.htm.鉴于此,我发现你不关心大写HREF属性是没有根据的.如果你真的想要快速和肮脏,你不应该打扰别的东西,你应该依靠全部大写HREF并完成它.但是,如果您希望确保代码适用于更多种类的文档并且更长时间,则应使用正确的解析器.

  • @Kinopiko:1.这是正确的*,与您的解决方案不同,它可以在任何情况下中断.2.代码应该由有能力*的人阅读.参考文献不是进入的陡峭障碍.对初学者来说,完全理解参考文献比完全理解正则表达式要容易得多.3.我更喜欢"HTML :: TokeParser :: Simple"以获得更易读的界面,但如果你不能花一点时间查看文档,那么你再次失败了. (4认同)
  • 并且4.使用了一个模块,因为*这不是一个小问题*.如果你认为它是一个微不足道的问题,你得到一个*错误*的解决方案,就像原始海报一样,和你的一样.适合这项任务的模块几乎可以保证不那么多. (4认同)
  • 他可能没有考虑过的例子+1.在这种情况下,这就是我应该反对正则表达式的论点. (3认同)
  • 我同意这些神奇的数字令人困惑.这是HTML :: TokeParser的属性,而不是一般的"不用正则表达式解析".使用XML :: LibXML的W3C DOM的实现会更清晰,但更冗长. (3认同)
  • 谁关心CPAN模块经常做什么?只需要您需要的CPAN模块. (2认同)

Axe*_*man 6

有点像SAX类型解析器是HTML::Parser:

use strict;
use warnings;

use English qw<$OS_ERROR>;
use HTML::Parser;
use List::Util qw<first>;

my $omitted;

sub tag_handler { 
    my ( $self, $tag_name, $text, $attr_hashref ) = @_;
    if ( $tag_name eq 'a' ) { 
        my $href = first {; defined } @$attr_hashref{ qw<href HREF> };
        $omitted = substr( $href, 0, 7 ) eq 'http://';
        return if $omitted;
    }
    print $text;
}

sub end_handler { 
    my $tag_name = shift;
    if ( $tag_name eq 'a' && $omitted ) { 
        $omitted = false;
        return;
    }
    print shift;
}

my $parser
    = HTML::Parser->new( api_version => 3
                       , default_h   => [ sub { print shift; }, 'text' ]
                       , start_h     => [ \&tag_handler, 'self,tagname,text,attr' ]
                       , end_h       => [ \&end_handler, 'tagname,text' ]
                       );
$parser->parse_file( $path_to_file ) or die $OS_ERROR;
Run Code Online (Sandbox Code Playgroud)