Perl 在 HTML/XML 标签内的单词周围添加 <a></a>

Perl add <a></a> around words within an HTML/XML tag

我有一个格式如下的文件:

Eye color
<p class="ul">Eye color, color</p> <p class="ul1">blue, cornflower blue, steely blue</p> <p class="ul1">velvet brown</p> <link rel="stylesheet" href="a.css">
</>
weasel
<p class="ul">weasel</p> <p class="ul1">musteline</p> <link rel="stylesheet" href="a.css">
</>

, 分隔的 <p class="ul1"> 中的每个单词都应包含在 <a> 标记中,如下所示:

Eye color
<p class="ul">Eye color, color</p> <p class="ul1"><a href="entry://blue">blue</a>, <a href="entry://cornflower blue">cornflower blue</a>, <a href="entry://steely blue">steely blue</a></p> <p class="ul1"><a href="entry://velvet brown">velvet brown</a></p> <link rel="stylesheet" href="a.css">
</>
weasel
<p class="ul">weasel</p> <p class="ul1"><a href="entry://musteline">musteline</a></p> <link rel="stylesheet" href="a.css">
</>

There could be one or several words within the <p class="ul1"> tag.

这在 Perl 单行中可能吗?

提前致谢。感谢任何帮助。

One-liner:

cat text | perl -pE 's{<p class="ul1">\K.*?(?=<\/p>)}{ join ", ", map {qq|<a href="entry://$_">$_</a>|} split /, */, $& }eg'

使用模块解析文件并迭代所需的元素(<p> of class ul1)。从每个短语中提取 comma-separated 个短语,并将 link 包裹在它们周围;然后用新内容替换元素。最后把变化的树写出来

使用HTML::TreeBuilder (with its workhorse HTML::Element)

use warnings;
use strict;
use feature 'say';

use HTML::Entities;
use HTML::TreeBuilder;

my $file = shift // die "Usage: [=10=] file\n";

my $tree = HTML::TreeBuilder->new_from_file($file);

foreach my $elem ($tree->look_down(_tag => "p", class => "ul1")) {   
    my @new_content;
    for ($elem->content_list) { 
        my @w = split /\s*,\s*/; 
        my $wrapped = join ", ", 
            map { qq(<a href="entry://$_">).$_.q(</a>) } @w; 
        push @new_content, $wrapped;
    }
    $elem->delete_content;
    $elem->push_content( @new_content );
}; 

say decode_entities $tree->as_HTML; 

在您的情况下,元素 ($elem) 将在 content_list 中有一个项目,因此您不必将修改后的内容收集到数组 (@new_content) 中,但可以只处理一件,这简化了代码。使用上面的列表当然没有坏处。

我将这个程序的输出重定向到一个 .html 文件。生成的文件在换行符上非常节俭。如果 HTML 很重要,请使用 HTML::Tidy or HTML::PrettyPrinter.

之类的工具通过

在one-liner?呐,太多了。并且请不要使用正则表达式,因为在路上会遇到麻烦;它需要密切的工作才能做到正确,很容易出现错误,对最小的细节很敏感,并且对输入的最细微变化也很脆弱。那时它 可以 完成这项工作。有图书馆的原因。

这项工作的另一个好工具是 Mojo::DOM。例如

use Mojo::DOM;
use Path::Tiny;  # only to read the file into a string easily

my $html = path($file)->slurp;

my $dom = Mojo::DOM->new($html);

foreach my $elem ($dom->find('p.ul1')->each) {
    my @w = split /,/, $elem->text;
    my $new = join ', ',
        map { qq(<a href="entry://$_">).$_.q(</a>) } @w;
    $elem->replace( $new );
}

say $dom;

产生与上面相同的 HTML(只是更好,并且注意不需要处理实体)。

较新的模块版本提供了 new_tag 方法,可以将上面的附加 link 制作为

my $new = join ', ', 
   map { $e->new_tag('a', 'href' => "entry://$_", $_) } @w; 

什么处理了一些微妙的需求(HTML 转义了一个)。添加此方法时,主要文档 不要说 ,请参阅 changelog(2018 年 5 月,所以应该在 v5.28 中;它适用于我的 5.29.2)。

我将显示的示例填充到此文件以进行测试:

<!DOCTYPE html>  <title>Eye color</title> <body>
<p class="ul">Eye color, color</p> 
<p class="ul1">blue, cornflower blue, steely blue</p> 
<p class="ul1">velvet brown</p> <link rel="stylesheet" href="a.css"></>
weasel
<p class="ul">weasel</p> 
<p class="ul1">musteline</p> <link rel="stylesheet" href="a.css"></>
</body> </html>

更新 已明确给定的标记片段不仅仅是一个可能完整的 HTML 文档的片段,而是一个文件(如所述)如图所示,作为使用 HTML 的自定义格式;除了所需的更改外,其余部分需要保留。

一个特别不愉快的细节被证明是</>部分; HTML::TreeBuilderMojo::DOMXML::LibXML 中的每一个在解析时都将其丢弃。我找不到让他们保留那块的方法。

Marpa::HTML按要求处理了整个片段,改变了要求的内容,而保留了其余部分。

use warnings;
use strict;
use feature 'say';
use Path::Tiny;

use Marpa::HTML qw(html);

my $file = shift // die "Usage: [=14=] file\n";
my $html = path($file)->slurp;

my $marpa = Marpa::HTML::html( 
    $html,
    {
        'p.ul1' => sub {
            return join ', ', 
                map { qq(<a href="entry://$_">).$_.q(</a>) } 
                split /\s*,\s*/, Marpa::HTML::contents();
        },
    }
);  

say $$marpa; 

classul1<p>标签的处理和之前一样:用逗号分割内容,每片包裹成一个<a>标签,然后用 ,

加入他们

这会打印(添加 line-breaks 和缩进以提高可读性)

Eye color
<p class="ul">Eye color, color</p> 
<a href="entry://blue">blue</a>, 
    <a href="entry://cornflower blue">cornflower blue</a>, 
    <a href="entry://steely blue">steely blue</a> 
    <a href="entry://velvet brown">velvet brown</a> 
<link rel="stylesheet" href="a.css">
</>
weasel
<p class="ul">weasel</p> <a href="entry://musteline">musteline</a> 
<link rel="stylesheet" href="a.css">
</>

这个模块的整体方法适合这样的任务

Marpa::HTML is an extremely liberal HTML parser. Marpa::HTML does not reject any documents, no mater how poorly they fit the HTML standards.

这里它处理了一段自定义的 HTML-like 标记,留下 </> 之类的东西。


请参阅 以获取使用 XML::LibXML

非常宽松地处理 HTML 的示例
perl -0777 -MWeb::Query=wq -lne'
    my $w = wq $_; my $sep = ", ";
    $w->filter("p.ul1")->each(sub {
        my (undef, $e) = @_;
        $e->html(join $sep, map {
            qq(<a href="entry://$_">$_</a>)
        } split $sep, $e->text);
    });
    print $w->as_html;
'