2010-10-10 3 views
1

У меня есть куча файлов HTML, и я хочу, чтобы это было в каждом HTML-файле для ключевого слова «From Argumbay» и изменить его с помощью некоторого href, который у меня есть. Сначала я подумал, что это очень просто, так что я сделал это, я открывал каждый HTML-файл и загружал его содержимое в массив (список), затем я искал каждое ключевое слово и заменял его на s /// и выгружал содержимое в файл, что проблема? иногда ключевое слово может также отображаться в href, которое в этом случае я не хочу, чтобы оно было заменено, или оно может появляться внутри некоторых тегов и т. д.Как я могу изменить HTML-файлы в Perl?

Пример: http://www.astrosociety.org/education/surf.html

Я хотел бы мой сценарий, чтобы заменить каждое вхождение слова «здесь» с некоторым HREF, что я имею в $ HREF, но как вы можете видеть, есть еще «здесь», который я уже не хочу, чтобы это снова было. В этом случае есть дополнительные «здесь, кроме как из href, но давайте предположим, что есть.

Я хочу заменить ключевое слово, только если его просто текст, любая идея?

BOUUNTY EDIT: Привет, я считаю, что это просто, но похоже, что он удаляет все комментарии, найденные в файле HTML SHTML (основная проблема заключается в том, что он удаляет SSI в SHTML), я попытался использовать: store_comments (1) на $ html до вызова рекурсивной функции, но безрезультатно. любая идея, чего я здесь не хватает?

+1

Без просмотра вашего кода было бы трудно сказать, где проблема. – Ether

+1

Можете ли вы дать примеры строк HTML? – Ruel

+0

Я добавил пример. – snoofkin

ответ

7

Для этого вам необходимо прочитать файл, изменить дерево и записать его (в тот же файл или в другой файл). Это довольно сложно, потому что вы пытаетесь преобразовать часть текстового узла в тег и потому, что у вас есть комментарии, которые не могут перемещаться.

Общий идиома с 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

WOOOOOOOOOOOOOOOOOOOOOOOOOOW! Серьезно человек, откуда вы пришли? Я не мог попросить лучшего решения! Удивительно. Он отлично работает, но мне не нужно несколько часов, чтобы понять, что вы там сделали (-: Большое спасибо! – snoofkin

+0

Я использую HTML-дерево совсем немного. Кроме того, выражения 'substr' просто копируются из документов для' @ -', потому что использование '$ &' и т. д. замедляет вашу программу. – cjm

+0

Вы также можете искать другие вопросы StackOverflow, которые задают одно и то же (и часто имеют одинаковый ответ). HTML :: TreeBuilder делает частые появления здесь. –

3

Если теги имеют значение при поиске и замене, вам необходимо использовать HTML::Parser.

Это tutorial выглядит немного легче, чем документация с модулем.

+0

Могу ли я использовать HTML :: TreeBuilder вместо? Я спрашиваю, почему я никогда не использовал их. – snoofkin

+1

@ soulSurfer2010, да HTML :: TreeBuilder может помочь вам в этом. (Он построен поверх HTML :: Parser.) – cjm

+1

@ soulSurfer2010 Да, похоже, что это тоже сработает. Реальная точка, которую я делал, заключается в том, что вам нужно будет анализировать HTML-код, а не просто применять регулярные выражения к источнику, что я и предполагаю, что вы делаете, основываясь на том, какую небольшую информацию вы предоставили. –

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