正则表达式以获取HTML表格内容

时间:2011-09-06 09:47:14

标签: c++ html regex html-parsing

我在这里偶然发现了一些挑战:如何在正则表达式的帮助下以HTML格式获取表格的内容。让我们说这是我们的表:

<table someprop=2 id="the_table" otherprop="val">
    <tr>
        <td>First row, first cell</td>
        <td>Second cell</td>
    </tr>
    <tr>
        <td>Second row</td>
        <td>...</td>
    </tr>
    <tr>
        <td>Another row, first cell</td>
        <td>Last cell</td>
    </tr>
</table>

我已经找到了一个有效的方法,但涉及多个正则表达式,分步执行:

  1. 获取正确的表并将其行放在后引用1中(文档中可能有多个):

    <table[^>]*?id="the_table"[^>]*?>(.*?)</table>

  2. 获取表格的行并将单元格放在后面引用1:

    <tr.*?>(.*?)</tr>

  3. 最后在反向引用1中获取单元格内容:

    <td.*?>(.*?)</td>

  4. 现在这一切都很好,但是使用一个花哨的正则表达式来完成这一切将会更加令人敬畏......有人知道这是否可能?

2 个答案:

答案 0 :(得分:3)

确实没有可能适用于任意数量的表数据的正则表达式解决方案,并将每个单元格放入单独的后向引用中。这是因为通过反向引用,您需要为要创建的每个backref创建一个独特的开放式窗口,并且您不知道自己有多少个单元格。

使用一种或另一种循环来提取数据没有任何问题。例如,在最后一个,在Perl中,这是因为$tr已经包含您需要的行:

@td = ( $tr =~ m{<td.*?>(.*?)</td>}sg );

现在$td[0]将包含第一个<td>$td[1]将包含第二个,等等。如果你想要一个二维数组,你可以将它包装在这样的循环中填充新的@cells变量:

our $table;  # assume has full table in it
my @cells;
while(my($tr) =~ $table = m{<tr.*?>(.*?)</tr>}sg) {
    push @cells, [ $tr =~ m{<td.*?>(.*?)</td>}sg ];
}

现在你可以进行二维寻址,允许$cells[0][0]等。外部显式循环一次处理一行,内部隐式循环拉出所有单元格。

这将适用于您展示的预制样本数据。如果这对你来说足够好,那就太好了。使用它并继续前进。

那可能是什么错误?

但是,您的模式中有很多关于数据内容的假设,我不知道您知道的那些。首先,请注意我是如何使用/s以便它不会卡在换行符上的。

但主要问题是最小匹配并不总是你想要的。至少,不是一般情况。有时它们并不像你想象的那么小,匹配的比你想要的多,有时它们只是不够匹配。

例如,如果字符串为:

,则<i>(.*?)</i>之类的模式将获得比您想要的更多的模式
<i>foo<i>bar</i>ness</i>

因为您最终会匹配字符串<i>foo<i>bar</i>

另一个常见问题(不包括不常见的问题)是像<tag.*?>这样的模式可能匹配得太少,例如

<img alt=">more" src="somewhere">

现在,如果您使用简单的<img.*?>,则只会捕获<img alt=">,这当然是错误的。

我认为最后一个主要问题是你必须完全忽略解析中的某些事情。这个嵌入式评论的最简单的演示(也是<script><style>, and CDATA`),因为你可以有像

这样的东西
<i> some <!-- secret</i>  --> stuff </i>

会抛出类似<i>(.*?)</i>的内容。

当然,有很多方法可以解决这些问题。一旦你这样做了,这真的是相当多的努力,你会发现你已经构建了一个真正的解析器,完全有很多辅助逻辑,而不仅仅是一个模式。

即便如此,您只处理格式正确的输入字符串。错误恢复和轻柔失败是一种完全不同的艺术。

答案 1 :(得分:2)

在知道OP需要c ++解决方案之前添加了这个答案......

由于使用正则表达式解析html在技术上是错误的,我将提供更好的解决方案。您可以使用js获取数据并将其放入二维数组中。我在示例中使用了jQuery。

var data = [];
$('table tr').each(function(i, n){
    var $tr = $(n);
    data[i] = [];
    $tr.find('td').text(function(j, text){
        data[i].push(text);
    });
});

示例的jsfiddle:http://jsfiddle.net/gislikonrad/twzM7/

修改

如果你想要一个简单的javascript方式(不使用jQuery),那么这可能更适合你:

var data = [];
var rows = document.getElementById('the_table').getElementsByTagName('tr');
for(var i = 0; i < rows.length; i++){
    var d = rows[i].getElementsByTagName('td');
    data[i] = [];
    for(var j = 0; j < d.length; j++){
       data[i].push(d[j].innerText);     
    }
}

这两个函数都以相同的方式返回数据。