繁体   English   中英

从Perl中的HTMl / XML标记中提取文本

[英]Extract text from HTMl/XML tags in Perl

我有这样的HTTPS响应

<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;
</pre>
        </p>
        <hr>
        <i>
            <small>Some text</small>
        </i>
        <hr/>
    </body>
</html>
  • 密钥的名称是静态的,我需要使用变量来获取特定值。
  • 我正在使用decision_entities将文本解析为html
  • 有时密钥会在响应中发布两次,但它的值相同。

XML::LibXML在这里没有多大帮助,因为它不是一个正确的XML文件/字符串。

我尝试使用正则表达式来实现它

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>

一旦提取了嵌入的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:正则表达式匹配

您可能可以使用正则表达式模式匹配。

use HTML::Entities qw( decode_entities );

my $xml = decode_entities( ( $html =~ m{<pre>[^&]*(.*?)</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都正确处理。

这个问题的难点在于所呈现的文档混合了格式 - 它具有有效的HTML结构,但也具有类似XML的元素,这些元素在没有特定模式的情况下显得“被抛入”。 有很多方法可以解开这些部分,即使它们不是防弹的,也需要权衡利弊。

在这种情况下, XML :: LibXML可以完成整个工作,因为它可以处理坏数据,但会注意警告。

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;
</pre>
        </p>
        <hr>
        <i>
            <small>Some text</small>
        </i>
        <hr/>
    </body>
</html>

解析器选项recover允许上述解析通过

真值启用恢复模式,允许人们解析损坏的XML或HTML数据。 [...]

虽然这很有用,但它当然会引起极其谨慎,因为我们故意使用不良数据(或者更确切地说,这里是不符合要求的数据)。 这个案例带来了两个这样的问题

  • 实体需要正则表达式。 该示例处理<pre>下的那些,但可能还有更多。 我们需要检查输入,可能需要对不同数据进行代码更改。

  • 这利用了类似XML的“标签”由实体( &lt; etc)给出的观察结果,它们在解析期间保持原样并且仅在稍后解码。 但是......

  • ...这不是一个规则,如果有一些不是这样的(而是作为<key> ),那么那些可以使库将文档解析成(略微) 不同的树 这再次需要检查输入,并且可能需要对任何新数据进行代码调整。

感谢ikegami提出了首先解析数据的问题,然后才讨论实体,讨论以及上面的XML代码。 上面的XML相关代码的原始版本首先被解码,因此结果略有不同。

另请注意, HTML::TreeBuilder使用ignore_unknown set处理此数据。 然后问题是这些新的“标签”( <key> etc)只是它的数据,因此获得的树的任何实际用途都可能不得不依赖于正则表达式。


另一种处理这些数据的方法是使用灵活的高级HTML解析器Marpa :: HTML

一个非常基本的演示

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​​为attributescontents解析元素<key>收集视图

原则上可能适合您的问题,因为它只接受<...>作为元素的语义。 但是那些不被视为XML,如果您的数据依赖于XML而不是显示,那么可能是一个缺点。 当然,这是一种不同的方法,有自己的规则。

请注意,模块的基本逻辑和用法是每个coderef returns ,并且此返回用于它触发的元素; 其余的文字没有变化。 因此,这对于更改文档的特定元素是很自然的。

我上面使用的不同,只是收集有关“标签”的信息。 该代码打印

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

For attribute "name=some variable" the <key> has: 
    <value>1024</value>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM