使用PHP提取HTML文档的正文文本

时间:2011-02-06 01:42:38

标签: php regex text text-processing html-content-extraction

我知道最好为此目的使用DOM,但让我们尝试以这种方式提取文本:

<?php


$html=<<<EOD
<html>
<head>
</head>
<body>
<p>Some text</p>
</body>
</html>
EOD;


        preg_match('/<body.*?>/', $html, $matches, PREG_OFFSET_CAPTURE);

        if (empty($matches))
            exit;

        $matched_body_start_tag = $matches[0][0];
        $index_of_body_start_tag = $matches[0][1];

        $index_of_body_end_tag = strpos($html, '</body>');


        $body = substr(
                        $html,
                        $index_of_body_start_tag + strlen($matched_body_start_tag),
                        $index_of_body_end_tag - $index_of_body_start_tag + strlen($matched_body_start_tag)
        );

echo $body;

结果可以在这里看到:http://ideone.com/vH2FZ

正如您所看到的,我收到的文字多于预期。

有些事情我不明白,为了获得substr($string, $start, $length)函数的正确长度,我正在使用:

$index_of_body_end_tag - $index_of_body_start_tag + strlen($matched_body_start_tag)

我认为这个公式没有任何问题。

有人可以建议问题出在哪里吗?

非常感谢大家。

修改

非常感谢你们所有人。我脑子里只有一个小虫。在阅读了你的答案之后,我现在明白了问题是什么,它应该是:

  $index_of_body_end_tag - ($index_of_body_start_tag + strlen($matched_body_start_tag));

或者:

  $index_of_body_end_tag - $index_of_body_start_tag - strlen($matched_body_start_tag);

4 个答案:

答案 0 :(得分:11)

问题是你的字符串中有新行。在模式中只匹配单行,你需要添加/ s修饰符来制作。匹配多行

这是我的解决方案,我更喜欢这种方式。

<?php

$html=<<<EOD
<html>
<head>
</head>
<body buu="grger"     ga="Gag">
<p>Some text</p>
</body>
</html>
EOD;

    // get anything between <body> and </body> where <body can="have_as many" attributes="as required">
    if (preg_match('/(?:<body[^>]*>)(.*)<\/body>/isU', $html, $matches)) {
        $body = $matches[1];
    }
    // outputing all matches for debugging purposes
    var_dump($matches);
?>

编辑:我正在更新我的答案,以便为您解释代码失败的原因提供更好的解释。

你有这个字符串:

<html>
<head>
</head>
<body>
<p>Some text</p>
</body>
</html>

一切似乎都很好,但实际上每行都有非打印字符(换行符)。 您有53个可打印字符和7个不可打印字符(新行,\ n = =每个新行实际上有2个字符)。

当你到达这部分代码时:

$index_of_body_end_tag = strpos($html, '</body>');

您获得&lt; / body&gt;的正确位置(从第51位开始)但这会计算新线。

所以当你到达这行代码时:

$index_of_body_start_tag + strlen($matched_body_start_tag)

评估为31(包括新行),并且:

$index_of_body_end_tag - $index_of_body_start_tag + strlen($matched_body_start_tag)

评估为51 - 25 + 6 = 32(您必须阅读的字符),但在&lt; body&gt;之间只有16个可打印字符的文字。和&lt; / body&gt;和4个不可打印的字符(&lt; body&gt;之后的新行和&lt; / body&gt;之前的新行)。这就是问题所在,您必须将计算(优先级)分组,如下所示:

$index_of_body_end_tag - ($index_of_body_start_tag + strlen($matched_body_start_tag))

评估为51 - (25 + 6)= 51 - 31 = 20(16 + 4)。

:)希望这可以帮助您理解为什么优先排序很重要。 (很抱歉误导了你的新行,它只在我上面给出的正则表达式中有效。)

答案 1 :(得分:4)

就个人而言,我不会使用正则表达式。

<?php

$html = <<<EOD

<html>
    <head>
        <title>Example</title>
    </head>
    <body>
        <h1>foobar</h1>
    </body>
</html>

EOD;

$s = strpos($html, '<body>') + strlen('<body>');
$f = '</body>';

echo trim(substr($html, $s, strpos($html, $f) - $s));

?>

返回<h1>foobar</h1>

答案 2 :(得分:2)

问题在于substr计算结束索引。你应该一路减去:

$index_of_body_end_tag - $index_of_body_start_tag - strlen($matched_body_start_tag)

但你在做:

+ strlen($matched_body_start_tag)

尽管如此,考虑到你可以使用preg_match 来做这件事似乎有点矫枉过正。您只需要确保使用s修饰符匹配新行:

preg_match('/<body[^>]*>(.*?)<\/body>/s', $html, $matches);
echo $matches[1];

输出:

<p>Some text</p>

答案 3 :(得分:1)

有些人可能已经发现了你的错误,我没有看到所有的回复 代数是错误的。

code is here

顺便说一句,第一次看到ideone.com,这很酷。

$body = substr( 
          $html, 
          $index_of_body_start_tag + strlen($matched_body_start_tag),
          $index_of_body_end_tag - ($index_of_body_start_tag + strlen($matched_body_start_tag))
        );

或..

$body = substr(
          $html,
          $index_of_body_start_tag + strlen($matched_body_start_tag),
          $index_of_body_end_tag - $index_of_body_start_tag - strlen($matched_body_start_tag)
       );