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

bob*_*obo 4 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;
Run Code Online (Sandbox Code Playgroud)

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

如您所见,我收到的文字多于预期.

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

$index_of_body_end_tag - $index_of_body_start_tag + strlen($matched_body_start_tag)
Run Code Online (Sandbox Code Playgroud)

我没有看到这个公式有什么问题.

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

非常感谢大家.

编辑:

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

  $index_of_body_end_tag - ($index_of_body_start_tag + strlen($matched_body_start_tag));
Run Code Online (Sandbox Code Playgroud)

要么:

  $index_of_body_end_tag - $index_of_body_start_tag - strlen($matched_body_start_tag);
Run Code Online (Sandbox Code Playgroud)

lud*_*ign 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);
?>
Run Code Online (Sandbox Code Playgroud)

编辑:我正在更新我的答案,为您提供更好的解释为什么您的代码失败.

你有这个字符串:

<html>
<head>
</head>
<body>
<p>Some text</p>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

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

当你到达这部分代码时:

$index_of_body_end_tag = strpos($html, '</body>');
Run Code Online (Sandbox Code Playgroud)

你得到</ body>的正确位置(从第51位开始),但这会计算新行.

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

$index_of_body_start_tag + strlen($matched_body_start_tag)
Run Code Online (Sandbox Code Playgroud)

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

$index_of_body_end_tag - $index_of_body_start_tag + strlen($matched_body_start_tag)
Run Code Online (Sandbox Code Playgroud)

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

$index_of_body_end_tag - ($index_of_body_start_tag + strlen($matched_body_start_tag))
Run Code Online (Sandbox Code Playgroud)

评价为51-(25 + 6)= 51-31 = 20(16 + 4).

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