Извлечение текста из тегов HTMl / XML в Perl - PullRequest
2 голосов
/ 28 июня 2019

У меня есть HTTPS-ответ, подобный этому


    
        
        Some tittle <localconfig>
  <key name="ssl_default">
    <value>sha256</value>
  </key>


    
    
        Some h2
        some text:

 text <localconfig>
  <key name="ssl_default">
    <value>sha256</value>
  </key>
  <key name="some variable">
    <value>1024</value>
  </key>
</localconfig>

Некоторый текст
  • Имя ключа является статическим, и мне нужно использовать переменную для получения определенных значений.
  • Я использую define_entities для разбора текста на html
  • Иногда ключ публикуется дважды в ответе, но это одно и то же значение.

XML::LibXML здесь мало помогаютэто неверный XML-файл / строка.

Я пытался использовать Regex, чтобы получить его вот так

sub get_key {
    my $start = '<key name="'.$_[0].'">\n<value>';
    print $_[1];
    my $end = "</value>";
    print " [*] Trying to get $_[0]\n";
    print "Start: $start  --- End $end";
    if($_[1] =~ /\b$start\b(.*?)\b$end\b/s){
        my $result = $1;
        print $result, "\n\n";
        return $result;
    }
}

get_key("string_to_search", $string_from_response);

Мне нужно извлечь ключ между ключом и значением

<key name="variable">
 <value>Grab me</value>
</key>

Ответы [ 2 ]

5 голосов
/ 28 июня 2019

После того, как вы извлекли встроенный XML-документ, вы должны использовать правильный синтаксический анализатор XML.

use XML::LibXML qw( );

my $xml_doc = XML::LibXML->new->parse_string($xml);

for my $key_node ($xml_doc->findnodes("/localconfig/key")) {
   my $key = $key_node->getAttribute("name");
   my $val = $key_node->findvalue("value/text()");
   say "$key: $val";
}

Так что у нас возникает вопрос, как извлечь XML-документ.

Вариант 1: XML :: LibXML

Вы можете использовать XML :: LibXML и просто указать ему игнорировать ошибку (ложный тег </p>).

my $html_doc = XML::LibXML->new( recover => 2 )->parse_html_fh($html);
my $xml = encode_utf8( $html_doc->findvalue('/html/body/pre/text()') =~ s/^[^<]*//r );

Вариант 2: совпадение с регулярным выражением

Возможно, вам не помешает использование сопоставления с регулярным выражением.

<code>use HTML::Entities qw( decode_entities );

my $xml = decode_entities( ( $html =~ m{<pre>[^&]*(.*?)
} s) [0]);

Вариант 3: Mojo :: DOM

Вы можете использовать Mojo :: DOM для извлечения встроенного XML-документа.

use Encode    qw( decode encode_utf8 );
use Mojo::DOM qw( );

my $decoded_html = decode($encoding, $html);
my $html_doc = Mojo::DOM->new($decoded_html);    
my $xml = encode_utf8( $html_doc->at('html > body > pre')->text =~ s/^[^<]*//r );

Проблема с Mojo :: DOM заключается в том, что вам нужно знать кодировку документа перед тем, как передать документ в синтаксический анализатор (потому что вы должны передать его в декодированном виде), но вам нужно проанализировать документ, чтобы извлечь кодировкудокумент из документа.

(Конечно, вы также можете использовать Mojo :: DOM для анализа XML).


Обратите внимание, что фрагмент HTML <p><pre></pre></p> означает <p></p><pre></pre> и XML :: LibXML, и Mojo :: DOM обрабатывают это правильно.

2 голосов
/ 28 июня 2019

Сложная часть этой проблемы заключается в том, что представленный документ смешивает форматы - он имеет допустимую структуру HTML, но также и с XML-подобными элементами, которые выглядят «добавленными» без определенного шаблона.Есть способы распутать эти части, даже если они не являются пуленепробиваемыми и имеют компромисс.

В этом случае XML :: LibXML может выполнить всю работу, так как это может иметь дело с неверными данными, но обратите внимание на предупреждения.

<code>use warnings;
use strict;
use feature 'say';

use Encode qw(encode_utf8); 
use XML::LibXML;

my $html_doc = XML::LibXML->new(recover => 2)->parse_html_fh(\*DATA);
my $xml = encode_utf8( 
    $doc->findvalue('/html/body/pre/text()') =~ s/^[^<]*//r 
);
my $xml_doc = XML::LibXML->new->parse_string($xml);

say for $xml_doc->findnodes('//key');  # node object stringifies

__DATA__
<html>
    <head> 
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <title>Some tittle &lt;localconfig&gt;
  &lt;key name="ssl_default"&gt;
    &lt;value&gt;sha256&lt;/value&gt;
  &lt;/key&gt;

</title>
    </head>
    <body>
        <h2>Some h2</h2>
        <p>some text:

            <pre>    text &lt;localconfig&gt;
  &lt;key name="ssl_default"&gt;
    &lt;value&gt;sha256&lt;/value&gt;
  &lt;/key&gt;
  &lt;key name="some variable"&gt;
    &lt;value&gt;1024&lt;/value&gt;
  &lt;/key&gt;
&lt;/localconfig&gt;

Некоторый текст

Опция синтаксического анализа recover - это то, что позволяет вышеуказанному анализу проходить через

Включается истинное значениережим восстановления, который позволяет анализировать поврежденные данные XML или HTML.[...]

Как бы это ни было полезно, это, конечно, требует особой осторожности, поскольку мы умышленно используем неверные данные (или, скорее, несоответствующие данные здесь).В этом случае возникают две такие проблемы.

  • Для сущностей требуется регулярное выражение.Пример имеет дело с теми, кто под <pre>, но может быть и больше.Нам нужно проверить ввод и, возможно, потребовать изменения кода для разных данных.

  • При этом используется наблюдение, что XML-подобные «теги» задаются сущностями (&lt; и т. Д.), которые остаются, как они есть во время синтаксического анализа и только расшифрованы позже.Однако ...

  • ... это не правило, и если некоторые не заданы таким образом (а скорее как <key>), тоони могут заставить библиотеку анализировать документ в (немного) другое дерево .Это снова требует проверки ввода и, возможно, корректировки кода для любых новых данных.

Спасибо ikegami за то, что он поднял вопрос о первом разборе данных и только после этого имел дело с сущностями, для обсуждения и для XML-кода выше,Первоначальная версия кода, связанного с XML выше, сначала была декодирована и, таким образом, получила немного другое дерево.

Также обратите внимание, что HTML::TreeBuilder обрабатывает эти данные с установленным ignore_unknown .Тогда проблема в том, что эти новые «теги» (<key> и т. Д.) Являются просто данными для него, поэтому любое практическое использование полученного дерева, вероятно, должно полагаться на регулярное выражение.


Еще один способработать с этими данными можно с помощью гибкого высокоуровневого анализатора HTML, Marpa :: HTML .

A очень базовое демо

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

use Marpa::HTML qw(html);
use HTML::Entities qw(decode_entities);    

my $input = do { local $/; <DATA> };    
my $html = decode_entities($input);

my (@attrs, @cont);

my $marpa_key = Marpa::HTML::html( 
    \$html,
    {
        'key' => sub {
            push @attrs, Marpa::HTML::attributes();
            push @cont, Marpa::HTML::contents();
        },
    }
);

for my $i (0..$#cont) {
    say "For attribute \"name=$attrs[$i]->{name}\" the <key> has: $cont[$i]"
}

__DATA__
...the same as in the first example, data from the question...

Это собирает просмотров при анализе, используя API для attributes и contents, дляэлемент <key>.

Может в принципе быть подходящим для вашей задачи, поскольку он принимает простую семантику <...> в качестве элемента.Но они не рассматриваются как XML, что может быть одним недостатком, если ваши данные полагаются на XML больше, чем показано.И, конечно, это другой подход со своими собственными правилами.

Обратите внимание, что основная логика и использование модуля заключается в том, что каждый код кода returns, и этот возврат используетсяза элемент, по которому он выстрелил;остальная часть текста остается неизменной.Так что это естественно для изменения отдельных элементов документа.

Я использовал это по-другому выше, только для сбора информации о «тегах».Этот код печатает

For attribute "name=ssl_default" the <key> has: 
    <value>sha256</value>

For attribute "name=some variable" the <key> has: 
    <value>1024</value>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...