2010-10-10 94 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