Perl:替换符合特定条件的链接(html)

时间:2013-09-03 22:33:30

标签: regex perl

在我的论坛上,我想自动将 rel =“nofollow”添加到指向外部网站的链接。例如,某人使用以下文本创建帖子:

Link 1: <a href="http://www.external1.com" target="_blank">External Link 1</A>
Link 2: <a href="http://www.myforum.com">Local Link 1</A>
Link 3: <a href="http://www.external2.com">External Link 2</A>
Link 4: <a href="http://www.myforum.com/test" ALT="Local">Local Link 2</A>

使用Perl,我希望将其更改为:

Link 1: <a href="http://www.external1.com" target="_blank" rel="nofollow">External Link 1</A>
Link 2: <a href="http://www.myforum.com">Local Link 1</A>
Link 3: <a href="http://www.external2.com" rel="nofollow">External Link 2</A>
Link 4: <a href="http://www.myforum.com/test" ALT="Local">Local Link 2</A>

我可以使用相当多的代码来完成这项工作,但我希望我可以使用一个或多个正则表达式执行此操作。但我无法弄清楚如何。

2 个答案:

答案 0 :(得分:1)

正则表达式可以在有限的场景中工作,但是你永远不应该使用正则表达式来解析HTML

  

每当你试图用正则表达式解析HTML时,邪恶的孩子就会为处女的鲜血哭泣,而俄罗斯的黑客会把你的webapp用来。

     

- 来自RegEx match open tags except XHTML self-contained tags

我非常喜欢Mojo套件,因为这允许我们使用非常少的代码使用正确的解析器。我们可以使用CSS选择器来找到有趣的元素:

use strict; use warnings;
use autodie;
use Mojo;
use File::Slurp;

for my $filename (@ARGV) {
  my $dom = Mojo::DOM->new(scalar read_file $filename);

  for my $link ($dom->find('a[href]')->each) {
    $link->attr(rel => 'nofollow')
      if $link->attr('href') !~ m(\Ahttps?://www[.]myforum[.]com(?:/|\z));
  }

  write_file "$filename~", "$dom";
  rename "$filename~" => $filename;
}

调用:perl mark-links-as-nofollow.pl *.html对数据进行测试会产生输出:

Link 1: <a href="http://www.external1.com" rel="nofollow" target="_blank">External Link 1</a>
Link 2: <a href="http://www.myforum.com">Local Link 1</a>
Link 3: <a href="http://www.external2.com" rel="nofollow">External Link 2</a>
Link 4: <a alt="Local" href="http://www.myforum.com/test">Local Link 2</a>

为什么我使用临时文件和rename?在大多数文件系统上,文件可以原子方式重命名,而写入文件则需要一些时间。所以其他进程可能会看到半文件。

答案 1 :(得分:0)

我会使用正则表达式gobal和eval标志进行回调,例如:

#!/usr/bin/perl

use strict;

my $internal_link = qr'href="https?:\/\/(?:www\.)?myforum\.com';

my $html = '
Lorem ipsum
<a href="http://www.external1.com" target="_blank">External Link 1</A>
Lorem ipsum
<a href="http://www.myforum.com">Local Link 1</A>
Lorem ipsum
<a href="http://www.external2.com">External Link 2</A>
Lorem ipsum
<a href="http://www.myforum.com/test" ALT="Local">Local Link 2</A>
';

$html =~ s/<a ([^>]+)>/"<a ". replace_externals($1). ">"/eg;

print $html;

sub replace_externals {
    my ($inner) = @_;
    return $inner =~ $internal_link ? $inner : "$inner rel=\"nofollow\"";
}

或者你肯定可以使用negative look-aheads,但那只会弄乱可读性。