解析不同列数的HTML表

时间:2015-07-10 21:42:21

标签: regex perl html-table

我需要解析HTML表中的监控数据以进行日志记录。

HTML文档中有多个表没有任何标识符,因此识别正确的TR需要即兴创作。

感兴趣的特定行是:

<TR>
    <TD>Signal to Noise Ratio</TD>
    <TD>35 dB</TD>
    <TD>35 dB</TD>
    <!-- MORE TDs continue here... -->
</TR>

因此,可以使用的标识符/常数是&#34;信噪比&#34; TR中的字符串,用于识别文档中正确的TD。

第一行中包含此行中标识字符串的TD个元素的数量是可变的。我需要将这些元素中的所有整数存储为变量,类似于:

my %data;
my @keys = qw(SNR1 SNR2 SNR3 SNR4);

my $content = LWP::Simple::get("http://192.168.100.1/cmSignalData.htm")
    or die "Couldn't get it!";

if ( $content =~ /<TD>(.+?) dB<\/TD>/ ) {
    $data{SNR1} = $1;
} 

for (@keys) {
    print "$_:" . $data{$_} . " ";
}
print "\n";

然后以完全相同的模式解析其他表中的其他TR元素。

3 个答案:

答案 0 :(得分:2)

不要使用Regex解析HTML。使用HTML解析器。

CPAN上有几个HTML解析器模块可用。我最喜欢的是Mojo :: DOM。你可以像下面这样使用它:

#!/usr/bin/perl
use strict;
use warnings;
use Mojo::DOM;

my $HTML = <<"EOF";
<table>
<TR>
<TD>Signal to Noise Ratio</TD>
<TD>35 dB</TD>
<TD>35 dB</TD>
</TR>
</table>
EOF

my $dom = Mojo::DOM->new( $HTML );

if ($dom->at('tr td')->text() eq 'Signal to Noise Ratio'){
   for my $e ($dom->find('td')->each) {
      if($e->text() =~ /(\d+)\sdB/){
          print $1."\n";
      }
   }
}

关于Mojo::DOMMojo::UserAgent的8分钟视频教程,请查看Mojocast Episode 5

答案 1 :(得分:2)

您可以使用XPath查询轻松获取所需的值,因为您在特定td节点之后查找同一级别的所有以下td个节点。

以下是使用HTML::TreeBuilder::XPath模块的示例:

use HTML::TreeBuilder::XPath;

my $tree = HTML::TreeBuilder::XPath->new;
$tree->parse_file("yourfile.html");

my @snr = $tree->findvalues('//td[.="Signal to Noise Ratio"]/following-sibling::td');
$tree->delete;

@snr = map /^(\d+)/, @snr;
print join(', ', @snr);

XPath是一种用于查询HTML / XML文档的树表示的语言DOM (Document Object Model) tree

查询详情:

//   # anywhere in the tree (*)
td   # a `td` element with the following "predicate" (embedded in square brackets):

[.="Signal to Noise Ratio"] # predicate: the text content of the current node (figured
                            # by a dot) is exactly "Signal to Noise Ratio"

/following-sibling::td # 'following-sibling::' is a kind of selector called "axis"
                       # that selects all nodes with the same parent node after the
                       # current element.
                       # 'td' selects only `td` elements in this node-set.

(*)如果你想要你可以更明确。您可以从根元素//td

中描述完整路径,而不是使用/html/body/center/table/tbody/tr/td

这种方法需要构建文档树以便能够查询它。它不是一种快速方法,但主要优点是您使用HTML结构而不是通配文本方法。

请注意,您可以避免数组map提取每个项目开头的数字。 XPath有几个字符串函数,包括substring-before

//td[.="Signal to Noise Ratio"]/following-sibling::td/substring-before(text(), " dB")

如果性能很重要,您可以使用像HTML::TokeParser::Simple之类的拉解析器尝试另一种方法。这不太方便编写,但它更快,因为没有构建DOM树,并且您将节省内存,因为您可以将HTML文件作为流读取并在需要时停止阅读将整个文件加载到内存中。

答案 2 :(得分:2)

这是使用 Mojolicious 的版本。它直接从您的pastebin存储库中提取HTML

for循环遍历所有表中的所有行。在其中,@columns数组设置为行中所有列(<td>元素)的文本内容

检查第一个元素,首先它是否存在,其次是Signal to Noise Ratio。如果是,那么全局数组@snr将设置为@columns剩余部分中的十进制数,last将停止搜索所需行

use strict;
use warnings;
use 5.010;

use Mojo;

my $ua = Mojo::UserAgent->new;

my $dom = $ua->get('http://pastebin.com/raw.php?i=73H5peKW')->res->dom;

my @snr;

for my $row ( $dom->find('table tr')->each ) {
    my @columns = $row->find('td')->map('text')->each;
    next unless @columns;
    if ( shift @columns eq 'Signal to Noise Ratio' ) {
        @snr = map /(\d+)/, @columns;
        last;
    }
}

say "@snr";

输出

35 35 34 34 34 34 34 34