在模式匹配中绕过可选字段

时间:2019-02-27 15:58:45

标签: perl

我正在尝试提取玩家的姓名和总数,但是在某些情况下,列表中的玩家编号后面还有一个额外的html标签。因此,当出现该字段时,我该如何绕过它。我不能在括号内加上括号,因为它将尝试匹配括号,对吗?

<tr><td>10<td>MANNY MACHADO - FA</td><td>37</td></tr>
<tr><td>107</td><td>ALEDMYS DIAZ - HOU</td><td>18</td></tr>

while($content =~ /<tr><td>\d+?\S+?<td>(.*?)\s-.*?<\/td><td>(\d+?)</g) {
  my $player = $1;
  my $total = $2;
  print "\nPlayer => $player  Total => $total\n";
}

我尝试使用“ \ S +?”绕过它,但是在这种情况下,它不会打印出播放器数量小于10的任何内容。

4 个答案:

答案 0 :(得分:1)

对于HTML,XML等使用正则表达式通常是一个不好的想法。

相反,您应该使用适当的解析器将其转换为DOM,然后在DOM域中实现算法。以您的示例为例:

  • 从文件或字符串中解析HTML
  • (在文档中找到正确的表-由于我没有完整的HTML,因此在示例中未列出)
  • 循环遍历表中的行
  • 从一行的列中提取您要查找的信息
#!/usr/bin/perl
use warnings;
use strict;

use HTML::TreeBuilder;

my $parser = new HTML::TreeBuilder;

my $root = $parser->parse_file(\*DATA)
    or die "HTML\n";

foreach my $row ($root->look_down(_tag => 'tr')) {
    if (my @columns = $row->look_down(_tag => 'td')) {
        my $player  = $columns[1]->as_text();
        my $total   = $columns[2]->as_text();
        print "Player => $player  Total => $total\n";
    }
}

exit 0;

__DATA__
<body>
  <tr><td>10<td>MANNY MACHADO - FA</td><td>37</td></tr>
  <tr><td>107</td><td>ALEDMYS DIAZ - HOU</td><td>18</td></tr>
</body>

试运行:

$ perl dummy.pl
Player => MANNY MACHADO - FA  Total => 37
Player => ALEDMYS DIAZ - HOU  Total => 18

答案 1 :(得分:1)

使用Mojo::DOM

use strict;
use warnings;
use Mojo::DOM;

my $html = <<'EOD';
<tr><td>10<td>MANNY MACHADO - FA</td><td>37</td></tr>
<tr><td>107</td><td>ALEDMYS DIAZ - HOU</td><td>18</td></tr>
EOD

my $dom = Mojo::DOM->new($html);
foreach my $tr ($dom->find('tr')->each) {
  my @cells = $tr->children('td')->each;
  my $player = $cells[1]->all_text;
  my $total = $cells[2]->all_text;

  # or alternatively
  my $player = $tr->at('td:nth-of-type(2)')->all_text;
  my $total = $tr->at('td:nth-of-type(3)')->all_text;

  print "\nPlayer => $player  Total => $total\n";
}

答案 2 :(得分:0)

您需要匹配可选的</tr>,因此可以在正则表达式中使用以下(?:<\/tr>)?进行匹配。由于开头是?:,因此这会导致一个非捕获组匹配0或1次。所以您的新正则表达式是

/<tr><td>\d+(?:<\/td>)?<td>(.*?)\s-.*?<\/td><td>(\d+?)</g

通常我会添加一些关于不使用正则表达式来解析HTML的信息,但是由于格式不正确的HTML,我会让它通过。但是,如果您可以控制创建HTML的内容,请尝试对其进行修复,以使<td></td>标签保持平衡。

答案 3 :(得分:0)

我也是一个愿意使用适当的HTML或XML模块来提取信息的人,就像上面已经说过的其他人一样。因此,我不会对此进行详细说明。

如果我必须从您显示的格式错误的html中提取内容,我会坚持采用多步骤的方法。

  1. 清理
  2. 提取
  3. 更多清理

为进行清理,我将首先检查常见问题。在这种情况下,每行都以<tr>开头,因此我愿意为此寻找行,在一些可选的空格之后跳过那些不是以<tr>开头的行:

while (<>) {
    next unless /^\s*<tr>/;

我注意到的下一个共同之处是,每个有趣的字段都以td开头。因此,我将其替换为更简单的选项卡,例如选项卡。假设已经有选项卡,我首先将其替换为空格:

    tr/\t/ /;
    s/<td>/\t/g;

现在我拥有的是一些标签,这些标签散布在我真正需要的数据周围。我真正需要的数据前面有一个制表符。因此,让我们删除标签:

    s/<.*?>//g;

最后我可以提取我的数据了:

    my($dummy, $number, $player, $total)= split /\t/;

但是由于播放器中附加了一些内容(在-之后),我们也将其删除

    $player=~ s/\s-.*//;
    print "\nPlayer => $player  Total => $total\n";
}

将其放在一起并使用DATA:

while (<DATA>) {
    next unless /^\s*<tr>/;
    tr/\t/ /;
    s/<td>/\t/g;
    s/<.*?>//g;
    my($dummy, $number, $player, $total)= split /\t/;
    $player=~ s/\s-.*//;
    print "\nPlayer => $player  Total => $total\n";
}
__DATA__
<tr><td>10<td>MANNY MACHADO - FA</td><td>37</td></tr>
<tr><td>107</td><td>ALEDMYS DIAZ - HOU</td><td>18</td></tr>

请准备好您可能会遇到带有更多空白的数据,否则该方法将失败。

示例:

<tr>
  <td>10
  <td>MANNY MACHADO - FA</td>
  <td>37</td>
</tr>
<tr><td>107</td>
    <td>ALEDMYS DIAZ - HOU</td>
    <td>18</td>
</tr>