2010-10-10 60 views
1

我有一堆HTML文件,我想要做的就是在每个HTML文件中查找关键字'From Argumbay',然后使用我所拥有的一些href进行更改。 我认为它起初非常简单,所以我做的是打开每个HTML文件并将其内容加载到一个数组(列表)中,然后查找每个关键字并将其替换为s ///,并将内容转储到该文件,有什么问题?有时关键字也可能出现在href中,在这种情况下,我不希望它被替换,或者它可能出现在某些标签等内部。如何在Perl中修改HTML文件?

一个例子:http://www.astrosociety.org/education/surf.html

,我想我的脚本,以取代一些HREF,我在$ HREF字“这里”的每一次出现,但正如你所看到的,还有另外一个“这里”这已经被href,我不希望它再次这个href。 在这种情况下,除了href之外,还有其他'这里除了href,但让我们假设有。

我想替换关键字只有当它只是文本,任何想法?

BOUUNTY编辑:嗨,我相信它是一个简单的事情,但似乎它会删除所有在HTML,SHTML文件中发现的评论(主要问题是它会清除SHTML中的SSI),我试过使用:store_comments 1)在调用递归函数之前在$ html上的方法,但无济于事。任何想法我在这里想念什么?

+1

没有看到你的代码,很难说出问题出在哪里。 – Ether 2010-10-10 15:30:54

+1

你可以给出示例HTML行吗? – Ruel 2010-10-10 15:34:00

+0

我添加了一个例子。 – snoofkin 2010-10-10 18:18:04

回答

7

要做到这一点与HTML::TreeBuilder,你会阅读文件,修改树,并写出来(到同一文件,或不同的文件)。这是相当复杂的,因为你试图将文本节点的一部分转换为标签,并且因为你的评论无法移动。

用HTML树中常见的成语是使用修改树递归函数:

use strict; 
use warnings; 
use 5.008; 

use File::Slurp 'read_file'; 
use HTML::TreeBuilder; 

sub replace_keyword 
{ 
    my $elt = shift; 

    return if $elt->is_empty; 

    $elt->normalize_content;  # Make sure text is contiguous 

    my $content = $elt->content_array_ref; 

    for (my $i = 0; $i < @$content; ++$i) { 
    if (ref $content->[$i]) { 
     # It's a child element, process it recursively: 
     replace_keyword($content->[$i]) 
      unless $content->[$i]->tag eq 'a'; # Don't descend into <a> 
    } else { 
     # It's text: 
     if ($content->[$i] =~ /here/) { # your keyword or regexp here 
     $elt->splice_content(
      $i, 1, # Replace this text element with... 
      substr($content->[$i], 0, $-[0]), # the pre-match text 
      # A hyperlink with the keyword itself: 
      [ a => { href => 'http://example.com' }, 
      substr($content->[$i], $-[0], $+[0] - $-[0]) ], 
      substr($content->[$i], $+[0]) # the post-match text 
     ); 
     } # end if text contains keyword 
    } # end else text 
    } # end for $i in content index 
} # end replace_keyword 


my $content = read_file('foo.shtml'); 

# Wrap the SHTML fragment so the comments don't move: 
my $html = HTML::TreeBuilder->new; 
$html->store_comments(1); 
$html->parse("<html><body>$content</body></html>"); 

my $body = $html->look_down(qw(_tag body)); 
replace_keyword($body); 

# Now strip the wrapper to get the SHTML fragment back: 
$content = $body->as_HTML; 
$content =~ s!^<body>\n?!!; 
$content =~ s!</body>\s*\z!!; 

print STDOUT $content; # Replace STDOUT with a suitable filehandle 

as_HTML输出将是语法正确的HTML,但不一定很好地格式化HTML供人观看的来源。如果需要,可以使用HTML::PrettyPrinter写出文件。

+0

WOOOOOOOOOOOOOOOOOOOOOOOWOW!认真的人,你从哪里来的?我不能要求更好的解决方案!惊人。它的工作原理非常完美,但是我不需要几个小时就能理解你在那里做了什么( - :非常感谢! – snoofkin 2010-10-11 08:39:34

+0

我使用了HTML-Tree,而且'substr'表达式只是复制出了' @ -',因为使用'$&'等会减慢你的程序的速度 – cjm 2010-10-11 16:19:22

+0

你也可能会搜索其他的StackOverflow问题,它们会提出相同的问题(并且经常有相同的答案)HTML :: TreeBuilder在这里频繁出现。 – 2010-10-11 17:44:51

3

如果标签在您的搜索和替换中很重要,则需要使用HTML::Parser

这个tutorial比带模块的文档更容易理解。

+0

我可以使用HTML :: TreeBuilder吗?我在问,因为我从来没有使用过任何一个。 – snoofkin 2010-10-10 15:58:29

+1

@ soulSurfer2010,是的HTML :: TreeBuilder可以帮助你做到这一点。 (它建立在HTML :: Parser之上。) – cjm 2010-10-10 16:07:50

+1

@ soulSurfer2010是的,它看起来也会起作用。我所做的真正的一点是,你需要真正解析HTML,而不仅仅是将正则表达式应用到源代码中,这正是我猜测你正在做什么的基础上你提供的什么小信息。 – 2010-10-10 16:09:33

0

如果你想去一个正则表达式,只有类型的方法,你就准备接受下列限制性条款:

  • 这不会在HTML中正常工作的意见
  • 这是不行的地方所述<>字符用来标记
  • 内其中使用<>字符,而不是标签
  • 这将无法工作的一部分,这将不起作用,其中一个标记跨越米(如果您一次只处理一行)

如果上述任何条件确实存在,那么您将不得不使用其他答案中概述的HTML/XML解析策略之一。

否则:

my $searchfor = "From Argumbay"; 
my $replacewith = "<a href='http://google.com/?s=Argumbay'>From_Argumbay</a>"; 

1 while $html =~ s/ 
    \A    # beginning of string 
    (    # group all non-searchfor text 
    (   # sub group non-tag followed by tag 
     [^<]*?  # non-tags (non-greedy) 
     <[^>]*> # whole tags 
    )*?   # zero or more (non-greedy) 
) 
    \Q$searchfor\E # search text 
/$1$replacewith/sx; 

注意,如果$searchfor比赛$replacetext(所以不要把“从Argumbay”回替换文本),这是不行的。

+0

今天访问本网站之前,我已经提出了一些类似的解决方案,但我不能接受这些规定,谢谢! – snoofkin 2010-10-11 08:40:28