[英]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.