Как наиболее надежно сохранить HTML-объекты при обработке документов HTML с Mojo :: DOM? - PullRequest
4 голосов
/ 13 марта 2019

Я использую Mojo :: DOM , чтобы идентифицировать и распечатать фразы (означающие строки текста между выбранными тегами HTML) в сотнях документов HTML, которые я извлекаю из существующего содержимого в Movable Typeсистема управления контентом.

Я пишу эти фразы в файл, чтобы их можно было перевести на другие языки следующим образом:

        $dom = Mojo::DOM->new(Mojo::Util::decode('UTF-8', $page->text));

    ##########
    #
    # Break down the Body into phrases. This is done by listing the tags and tag combinations that
    # surround each block of text that we're looking to capture.
    #
    ##########

        print FILE "\n\t### Body\n\n";        

        for my $phrase ( $dom->find('h1, h2, h2 b, h3, p, p strong, span, a, caption, th, li, li a')->map('text')->each ) {

            print_phrase($phrase); # utility function to write out the phrase to a file

        }

Когда Mojo :: DOM обнаружил встроенные HTML-сущности(например, ™ и  ) он преобразовывал эти объекты в закодированные символы, а не передавал их как написано.Я хотел, чтобы сущности передавались как написано.

Я понял, что могу использовать Mojo :: Util :: decode для передачи этих сущностей HTML в файл, который я пишу.Проблема в том, что " Вы можете вызывать decode 'UTF-8' только для строки, которая содержит действительный UTF-8. Если этого не произойдет, например, потому что он уже преобразован в символы Perl, он вернетundef. "

Если это так, я должен либо попытаться выяснить, как проверить кодировку текущей HTML-страницы перед вызовом Mojo::Util::decode('UTF-8', $page->text), либо использовать другой метод для сохранения закодированногоСущности HTML.

Как наиболее надежно сохранить закодированные сущности HTML при обработке документов HTML с Mojo :: DOM?

Ответы [ 2 ]

3 голосов
/ 13 марта 2019

Похоже, что при сопоставлении с текстом вы заменяете сущности XML, но когда вы вместо этого работаете с узлами и используете их содержимое, сущности сохраняются. Этот минимальный пример:

#!/usr/bin/perl
use strict;
use warnings;
use Mojo::DOM;

my $dom = Mojo::DOM->new('<p>this &amp; &quot;that&quot;</p>');
for my $phrase ($dom->find('p')->each) {
    print $phrase->content(), "\n";
}

печать:

this &amp; &quot;that&quot;

Если вы хотите сохранить цикл и карту, замените map('text') на map('content') следующим образом:

for my $phrase ($dom->find('p')->map('content')->each) {

Если у вас есть вложенные теги и вы хотите найти только тексты (но не печатать имена этих вложенных тегов, а только их содержимое), вам нужно будет сканировать дерево DOM:

#!/usr/bin/perl
use strict;
use warnings;
use Mojo::DOM;

my $dom = Mojo::DOM->new('<p><i>this &amp; <b>&quot;</b><b>that</b><b>&quot;</b></i></p><p>done</p>');

for my $node (@{$dom->find('p')->to_array}) {
    print_content($node);
}

sub print_content {
    my ($node) = @_;
    if ($node->type eq "text") {
        print $node->content(), "\n";
    }
    if ($node->type eq "tag") {    
        for my $child ($node->child_nodes->each) {
            print_content($child);
        }
    }
}

который печатает:

this & 
"
that
"
done
0 голосов
/ 10 апреля 2019

Благодаря тестированию мы с коллегами смогли определить, что Mojo::DOM->new() автоматически декодирует символы амперсанда (&), делая невозможным сохранение сущностей HTML как написанных.Чтобы обойти это, мы добавили следующую подпрограмму для двойного кодирования амперсанда:

sub encode_amp {
    my ($text) = @_;

    ##########
    #
    # We discovered that we need to encode ampersand
    # characters being passed into Mojo::DOM->new() to avoid HTML entities being decoded
    # automatically by Mojo::DOM::Util::html_unescape().
    #
    # What we're doing is calling $dom = Mojo::DOM->new(encode_amp($string)) which double encodes
    # any incoming ampersand or &amp; characters.
    #
    #
    ##########   

    $text .= '';           # Suppress uninitialized value warnings
    $text =~ s!&!&amp;!g;  # HTML encode ampersand characters
    return $text;
}

Позже в скрипте мы пропускаем $page->text - encode_amp() при создании нового объекта Mojo::DOM.

    $dom = Mojo::DOM->new(encode_amp($page->text));

##########
#
# Break down the Body into phrases. This is done by listing the tags and tag combinations that
# surround each block of text that we're looking to capture.
#
# Note that "h2 b" is an important tag combination for capturing major headings on pages
# in this theme. The tags "span" and "a" are also.
#
# We added caption and th to support tables.
#
# We added li and li a to support ol (ordered lists) and ul (unordered lists).
#
# We got the complicated map('descendant_nodes') logic from @Grinnz on StackOverflow, see:
# /9338977/kak-naibolee-nadezhno-sohranit-html-obekty-pri-obrabotke-dokumentov-html-s-mojo-domcomment97006305_55131737
#
#
# Original set of selectors in $dom->find() below is as follows:
#   'h1, h2, h2 b, h3, p, p strong, span, a, caption, th, li, li a'
#
##########

    print FILE "\n\t### Body\n\n";        

    for my $phrase ( $dom->find('h1, h2, h2 b, h3, p, p strong, span, a, caption, th, li, li a')->
        map('descendant_nodes')->map('each')->grep(sub { $_->type eq 'text' })->map('content')->uniq->each ) {           

        print_phrase($phrase);

    }

Блок кода выше включает в себя предыдущие предложения @Grinnz, как видно из комментариев к этому вопросу.Спасибо также @Robert за его ответ, который хорошо наблюдал за тем, как работает Mojo::DOM.

Этот код определенно работает для моего приложения.

...