我有这样的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>
答案 0 :(得分:5)
提取嵌入式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都可以正确处理此问题。
答案 1 :(得分:2)
此问题的难点在于,所呈现的文档混合了格式-它具有有效的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>
parser option recover
使上面的解析得以通过
一个真值打开恢复模式,该模式允许解析损坏的XML或HTML数据。 [...]
尽管如此有用,但由于我们故意使用不良数据(或此处不合格的数据),因此请特别谨慎。这种情况带来了两个这样的问题。
正则表达式。该示例处理<pre>
下的示例,但可能还有更多。我们需要检查输入,并可能需要更改不同数据的代码。
这利用了这样的观察:类似XML的“标签”是由实体(<
等)给定的,它们在解析过程中保持不变,以后才解码。但是...
...这不是一条规则,如果某些不是以这种方式给出(而是<key>
),那么那些可以使库解析文档放入(略微)不同的树。这再次需要检查输入,并可能需要对任何新数据进行代码调整。
感谢ikegami提出了首先解析数据然后才处理实体,进行讨论以及上面的XML代码的要点。上面的与XML相关的代码的原始版本首先被解码,因此最终得到了稍微不同的树。
还要注意,HTML::TreeBuilder
确实设置了ignore_unknown来处理此数据。那么问题就在于这些新的“标签”(<key>
等)仅是数据,因此对所获得的树的任何实际使用都可能必须依靠正则表达式。
处理此数据的另一种方法是使用灵活的高级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...
这将使用attributes
和contents
的API为元素<key>
收集views进行解析。
它可能原则上适合您的问题,因为它仅接受<...>
的语义作为元素。但是这些并没有被视为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>