[英]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 <localconfig>
<key name="ssl_default">
<value>sha256</value>
</key>
</title>
</head>
<body>
<h2>Some h2</h2>
<p>some text:
<pre> text <localconfig>
<key name="ssl_default">
<value>sha256</value>
</key>
<key name="some variable">
<value>1024</value>
</key>
</localconfig>
</pre>
</p>
<hr>
<i>
<small>Some text</small>
</i>
<hr/>
</body>
</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 <localconfig>
<key name="ssl_default">
<value>sha256</value>
</key>
</title>
</head>
<body>
<h2>Some h2</h2>
<p>some text:
<pre> text <localconfig>
<key name="ssl_default">
<value>sha256</value>
</key>
<key name="some variable">
<value>1024</value>
</key>
</localconfig>
</pre>
</p>
<hr>
<i>
<small>Some text</small>
</i>
<hr/>
</body>
</html>
解析器选项recover
允许上述解析通过
真值启用恢复模式,允许人们解析损坏的XML或HTML数据。 [...]
虽然这很有用,但它当然会引起极其谨慎,因为我们故意使用不良数据(或者更确切地说,这里是不符合要求的数据)。 这个案例带来了两个这样的问题
实体需要正则表达式。 该示例处理<pre>
下的那些,但可能还有更多。 我们需要检查输入,可能需要对不同数据进行代码更改。
这利用了类似XML的“标签”由实体( <
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为attributes
和contents
解析元素<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.