簡體   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