Как извлечь теги заголовков в PHP из строки?

Как из строки, содержащей много HTML, извлечь весь текст из тегов <h1><h2>etc в новую переменную?

Я хотел бы захватить весь текст из этих элементов и сохранить их в новой переменной как значения, разделенные запятыми.

Возможно ли использование preg_match_all()?


person bluedaniel    schedule 14.01.2010    source источник
comment
Когда возникает вопрос: «Как мне извлечь что-то из HTML», ответ - НИКОГДА не использовать регулярные выражения. Вместо этого см. Обсуждение Надежный, зрелый анализатор HTML для PHP.   -  person Tony Miller    schedule 14.01.2010


Ответы (7)


Если вы действительно хотите использовать регулярные выражения, я думаю, что:

preg_match_all('/<h[0-6]>([^</h[0-6]>*)</h/i', $string, $matches);

должен работать, пока теги заголовка не вложены. Как говорили другие, если вы не контролируете HTML, регулярные выражения не лучший способ сделать это.

person Scott Saunders    schedule 14.01.2010
comment
Это не удастся во многих вероятных случаях - например, когда теги заголовков имеют атрибуты. Это ненадежное и ненадежное решение. Допустимый парсер DOM должен использоваться с действительным HTML. - person mickmackusa; 25.05.2020

Сначала вам нужно очистить HTML ($ html_str в примере) с помощью tidy:

$tidy_config = array(
    "indent"               => true,
    "output-xml"           => true,
    "output-xhtml"         => false,
    "drop-empty-paras"     => false,
    "hide-comments"        => true,
    "numeric-entities"     => true,
    "doctype"              => "omit",
    "char-encoding"        => "utf8",
    "repeated-attributes"  => "keep-last"
);

$xml_str = tidy_repair_string($html_str, $tidy_config);

Затем вы можете загрузить XML ($ xml_str) в DOMDocument:

$doc = DOMDocument::loadXML($xml_str);

И, наконец, вы можете использовать метод Хории Драгомира:

$list = $doc->getElementsByTagName("h1");
for ($i = 0; $i < $list->length; $i++) {
    print($list->item($i)->nodeValue . "<br/>\n");
}

Или вы также можете использовать XPath для более сложных запросов в DOMDocument (см. http://www.php.net/manual/en/class.domxpath.php)

$xpath = new DOMXPath($doc);
$list = $xpath->evaluate("//h1");
person c0deaddict    schedule 14.01.2010
comment
что-то сломалось в этом скрипте, попробуйте его с помощью простого HTML, например '‹html› ‹body› ‹h1› Test ‹/h1› ‹br› ‹/body› ‹/html›' - person bluedaniel; 14.01.2010
comment
Какая у вас ошибка? Если я попробую приведенный вами пример, он будет работать так, как ожидалось, и напечатает Test - person c0deaddict; 14.01.2010
comment
как и во всем приведенном выше коде, с единственным изменением: $ xml_str = tidy_repair_string ('‹html› ‹body› ‹h1› Test ‹/h1› ‹br› ‹/body› ‹/html›', $ tidy_config); ??? Это ломает мой скрипт, там ошибка, которая вылетает как опечатка или что-то в этом роде - person bluedaniel; 14.01.2010
comment
Я предполагаю, что модуль tidy не включен в вашем php.ini. Если вы используете xampp (или какой-либо другой AMP), раскомментируйте строку extension = php_tidy.dll. Если вы используете Ubuntu, используйте apt-get install php5-tidy, чтобы установить и включить его. - person c0deaddict; 14.01.2010

также обратите внимание на собственный DOMDocument php-класс.

Вы можете использовать $domdoc->getElementsByTagName('h1') для получения заголовков.

person Horia Dragomir    schedule 14.01.2010

Я знаю, что это очень старый пост, но я хотел бы упомянуть лучший способ, которым я смог коллективно захватить теги заголовков.

<h1>title</h1> and <h2>title 2</h2>

Этот метод (работает как регулярное выражение, однако PHP действует немного иначе.)

/<\s*h[1-2](?:.*)>(.*)</\s*h/i

используйте это в preg_match

|<\s*h[1-2](?:.*)>(.*)</\s*h|Ui

$group[1] будет включать все, что находится между тегом заголовка. $group[0] это все <h1>test</h

Это будет учитывать пробелы, и если кто-то добавит "класс / идентификатор"

<h1 class="classname">test</h1>

класс / идентификатор (группа) игнорируется.

ПРИМЕЧАНИЕ. Когда я анализирую HTML-теги, я всегда удаляю и заменяю все пробелы, разрывы строк, табуляции и т. д. на 1 пробел. Это сводит к минимуму многострочные символы, точки ... и очень большое количество пробелов, которые в некоторых случаях могут мешать форматированию регулярных выражений.

  • конечно, я беру только 1-2 тега заголовка, измените его на 0-9, чтобы захватить все.
  • Если у кого-то еще есть мод, который нужно добавить, или исправление в мой код, ответьте, я бы очень хотел знать.
  • Напротив, поскольку Regex плохо работает с HTML, это очень открытый аргумент. Потому что, если вы создадите свои функции php и выражения регулярных выражений, чтобы полностью избавиться от мусора и подготовить html для конкретных выражений регулярных выражений, вы прекрасно сможете получить то, что ищете. Вы можете создать достаточно функций регулярных выражений, чтобы заменить любительскую работу с html.

Вот ссылка на тестовую страницу тест регулярного выражения

person tmarois    schedule 23.09.2012
comment
Я думаю, вы имели в виду |<\s*h[1-2](?:.*)>(.*)</\s*h[1-2]>|Ui, иначе он не захватит весь элемент и не отключит закрывающий тег ›Также обратите внимание, что это не будет совпадать, если в заголовке есть разрывы строк (<br/>), что вполне возможно при работе с пользовательским контентом - person cameronjonesweb; 07.11.2017
comment
Никто не должен реализовывать этот совет в своем собственном приложении, если введен правильный HTML. Всегда используйте законный парсер DOM, пока парсер DOM не предоставит желаемый результат. Я люблю регулярные выражения (очень), но для этого есть время и место - это не время и не место. Вот лишь несколько причин, по которым этот ответ регулярного выражения не сработает: 3v4l.org/LXXSQ Не ошибитесь, Regex DOM-игнорирует. IOW, он не знает, соответствует ли оно подстрокам, которые ЯВЛЯЮТСЯ элементами DOM, или просто ВИДОМ, как элементы DOM, на основе дизайна шаблона. - person mickmackusa; 25.05.2020
comment
@mickmackusa Да, это действительно старый пост. Сегодня есть более эффективные способы сделать это, однако я отсканировал более 5 миллионов веб-страниц, используя эту простую технологию регулярных выражений, и не потерпел неудачу. Я бы сказал, что большинство страниц действительно достаточно действительны, чтобы использовать регулярное выражение. Возможно, эти проблемы возникнут на страницах нижнего уровня. Но да, если вы хотите быть в безопасности, используйте встроенные методы php для извлечения HTML-элементов. - person tmarois; 27.05.2020

Рекомендуется не использовать регулярное выражение для этой работы и использовать что-нибудь SimpleHTMLDOM parser

person codaddict    schedule 14.01.2010
comment
Я обнаружил, что регулярное выражение лучше в этом. Парсер DOM в некоторой степени ненадежен для веб-страниц с поврежденным HTML или недопустимыми структурами HTML. - person datasn.io; 30.10.2012
comment
@ datasn.io До тех пор, пока вы не сможете точно определить, что входящий документ содержит недопустимый / поврежденный html, вашим выбором синтаксического анализа по умолчанию должен быть парсер DOM, а не регулярное выражение. Используйте регулярное выражение только тогда, когда допустимый синтаксический анализатор не может удовлетворить ваши потребности. - person mickmackusa; 25.05.2020
comment
@codaddict, это не очень щедрый ответ. - person mickmackusa; 25.05.2020

Вероятно, вам лучше использовать парсер HTML. Но для действительно простых сценариев подойдет что-то вроде этого:

if (preg_match_all('/<h\d>([^<]*)<\/h\d>/iU', $str, $matches)) {
    // $matches contains all instances of h1-h6
}
person Emil H    schedule 14.01.2010

Я просто хочу поделиться своим решением:

function get_all_headings( $content ) {
    preg_match_all( '/\<(h[1-6])\>(.*)<\/h[1-6]>/i', $content, $matches );

    $r = array();
    if( !empty( $matches[1] ) && !empty( $matches[2] ) ){
        $tags = $matches[1];
        $titles = $matches[2];
        foreach ($tags as $i => $tag) {
            $r[] = array( 'tag' => $tag, 'title' => $titles[ $i ] );
        }
    }

    return $r;
}

Эта функция вернет пустой массив, если заголовки не найдены или что-то вроде этого:

array (
    array (
        'tag' => 'h1',
        'title' => 'This is a title',
    ),
    array (
        'tag' => 'h2',
        'title' => 'This is the second title',
    ),
)
person Andrew Surdu    schedule 19.06.2017