Извлечение основного текста 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);
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).
:) Надеюсь, это поможет вам понять, почему важно расставлять приоритеты. (Извините, что ввел вас в заблуждение относительно новых строк, это допустимо только в примере регулярного выражения, который я привел выше).
Лично я бы не стал использовать регулярное выражение.
<?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>
Проблема заключается в вашем 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>
Кто-то, вероятно, уже обнаружил вашу ошибку, я не читал все ответы.
Алгебра ошибочна.
Кстати, впервые вижу 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)
);