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

时间:2019-07-17 01:52:07

标签: html xml perl text-processing

我有一个这样的文件格式:

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">
</>
  

<p class="ul1">标记内可能有一个或几个单词。

{@ {1}}单行有可能吗?

先谢谢了。任何帮助表示赞赏。

3 个答案:

答案 0 :(得分:5)

使用模块解析文件并遍历所需的元素(类<p>的{​​{1}})。从每个中提取那些用逗号分隔的短语,并在它们周围包装链接;然后用新内容替换元素。最后将更改后的树写出来。

使用HTML::TreeBuilder(及其主力HTML::Element

ul1

在您的情况下,元素(use warnings; use strict; use feature 'say'; use HTML::Entities; use HTML::TreeBuilder; my $file = shift // die "Usage: $0 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::TidyHTML::PrettyPrinter之类的工具进行传递。

单线吗?不,太多了。并且请不要使用正则表达式,因为这会给您带来麻烦。它需要紧密的工作才能正确完成,容易陷入越野车,对最小的细节敏感,并且即使输入的更改很小,也很脆弱。这就是它可以进行的工作。有图书馆的原因。

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

.html

产生与上述相同的HTML(效果更好,请注意无需处理实体)。

较新的模块版本提供了new_tag方法,通过该方法可以在上面创建其他链接

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转义为一个)。添加此方法时的主要文档不要说,请参见changelog(2018年5月,据称在v5.28中;适用于我的5.29.2)。

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

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

更新 已明确的是,给定的标记片段不仅是大概完整的HTML文档的一部分,而且还是一个自定义格式的文件(如上所述)使用HTML;除了必需的更改之外,其余所有都需要保留。

一个特别令人不快的细节被证明是<!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::TreeBuilderMojo::DOM 中的每一个在解析时都将其丢弃。我找不到让他们坚持下去的方法。

Marpa::HTML根据需要处理了整个片段,更改了要询问的内容,而其余部分则保留下来。

XML::LibXML

use warnings; use strict; use feature 'say'; use Path::Tiny; use Marpa::HTML qw(html); my $file = shift // die "Usage: $0 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; 的{​​{1}}标记的处理与上面相同:以逗号分割内容并将每段内容包装到<p>标记中,然后使用{ {1}}

此打印(添加了换行符和缩进以提高可读性)

ul1

此模块的整体方法适用于此类任务

  

<a>是一个非常宽松的HTML解析器。 ,不会拒绝任何文档,也不会满足HTML标准的要求。

此处处理了自定义的类似HTML的标记,将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

对HTML进行非常宽松的处理的示例,请参见this post

答案 1 :(得分:1)

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;
'

答案 2 :(得分:0)

单线:

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