Извлечение основного текста HTML-документа с помощью PHP


Я знаю, что для этой цели лучше использовать 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);
Author: bobo, 2011-02-06

4 answers

Проблема в том, что в вашей строке есть новые строки, где. в шаблоне совпадают только отдельные строки, вам нужно добавить модификатор /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>');

Вы получаете правильное положение

(начиная с позиции 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 (символы, которые вы должны прочитать), но вы только имейте 16 печатаемых символов текста между и телом> и 4 непечатаемых символа (новая строка после и новая строка перед телом>). И вот в чем проблема, вам нужно сгруппировать вычисления (расставить приоритеты) следующим образом:

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

Оценивается в 51 - (25 + 6) = 51 - 31 = 20 (16 + 4).

:) Надеюсь, это поможет вам понять, почему важно расставлять приоритеты. (Извините, что ввел вас в заблуждение относительно новых строк, это допустимо только в примере регулярного выражения, который я привел выше).

 11
Author: ludesign, 2011-02-06 02:55:49

Лично я бы не стал использовать регулярное выражение.

<?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>

 4
Author: jhine, 2011-02-06 02:07:58

Проблема заключается в вашем 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>
 2
Author: netcoder, 2011-02-06 02:21:03

Кто-то, вероятно, уже обнаружил вашу ошибку, я не читал все ответы.
Алгебра ошибочна.

Код здесь

Кстати, впервые вижу 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)
       );
 1
Author: sln, 2011-02-06 05:39:53