使用PHP获取所有h1标记的值

16
我想要接收一个包含给定文本中所有h1标签值的数组。
例如,如果输入字符串如下:
<h1>hello</h1>
<p>random text</p>
<h1>title number two!</h1>
我需要收到一个包含以下内容的数组:
titles[0] = 'hello',
titles[1] = 'title number two!'
我已经知道如何获得字符串中第一个h1的值,但我需要给定字符串中所有h1标签的值。 我当前正在使用以下代码来获取第一个标签:
function getTextBetweenTags($string, $tagname) 
 {
  $pattern = "/<$tagname ?.*>(.*)<\/$tagname>/";
  preg_match($pattern, $string, $matches);
  return $matches[1];
 }

我传递了要解析的字符串,并将$tagname设置为"h1"。

但实际上这段代码不是我自己写的,我一直在尝试编辑它以使其按照我的意愿运行,但似乎没有什么效果。

我希望有人能帮我解决问题。

提前感谢。


1
最少你应该使用 preg_match_all。请查看 http://simplehtmldom.sourceforge.net/。 - fabrik
4个回答

36

你可以使用 simplehtmldom 库:

function getTextBetweenTags($string, $tagname) {
    // Create DOM from string
    $html = str_get_html($string);

    $titles = array();
    // Find all tags 
    foreach($html->find($tagname) as $element) {
        $titles[] = $element->plaintext;
    }
}

2
simplehtmldom比DOMDocument更快,还是只用于那些没有DOMDocument存在的情况(尽管它默认是启用的)? - Wrikken
1
@Wrikken 这是用户空间代码,所以我怀疑它不会更快。不知道为什么人们对它如此着迷(可能是名称中的“简单”),特别是因为还有 Zend_DomphpqueryFluentDom 等替代品。 - Gordon
@Wrikken 它并不更快(几乎相同), 但处理无效的 HTML 更好。非 UTF 编码也遇到的问题要少得多… - Sergey Eremin
2
@kgb 如果您使用loadHTML加载无效的HTML,则DOM可以正常工作。唯一不起作用的是getElementById,这仅仅是由于回退到HTML4.0 DTD。然后,您仍然可以通过XPath查询节点ID。此外,您根本不必使用@抑制错误。您可以使用libxml_use_internal_errors并通过自定义错误处理程序处理任何错误。SimpleHTMLDom对于HTML并不更适用。它甚至不使用libxml而是使用字符串函数解析HTML。 - Gordon
2
不要使用内置的c扩展来做完全相同的事情(说真的,如果PHP核心中已经内置了完全相同的东西,为什么还要用PHP做这些事情呢?)...改用DomDocument代替... - ircmaxell
显示剩余5条评论

25
function getTextBetweenTags($string, $tagname){
    $d = new DOMDocument();
    $d->loadHTML($string);
    $return = array();
    foreach($d->getElementsByTagName($tagname) as $item){
        $return[] = $item->textContent;
    }
    return $return;
}

10

替代DOM。在内存有限时使用。

$html = <<< HTML
<html>
<h1>hello<span>world</span></h1>
<p>random text</p>
<h1>title number two!</h1>
</html>
HTML;

$reader = new XMLReader;
$reader->xml($html);
while($reader->read() !== FALSE) {
    if($reader->name === 'h1' && $reader->nodeType === XMLReader::ELEMENT) {
        echo $reader->readString();
    }
}

谢谢,不过我仍在使用DOM方法。还是非常感谢你抽出时间来回答 :) - Pieter888
1
@Pieter 是的,如果 Wrikken 还没有提供 DOM 解决方案,我已经提供了。 - Gordon

6
 function getTextBetweenH1($string)
 {
    $pattern = "/<h1>(.*?)<\/h1>/";
    preg_match_all($pattern, $string, $matches);
    return ($matches[1]);
 }

12
在这里使用正则表达式非常好。他并没有解析HTML,而是匹配<h1></h1>之间的内容,这本质上就是一个正则匹配。使用正则表达式来匹配一个正则语言非常合适。不要听信那些人瞎吹的“天哪,如果涉及到HTML就不能使用正则表达式”的废话。他不是在试图匹配整个HTML,而只是一个非常小的、恰好是正则语言的子集。 - Daniel Egeberg
2
@Daniel,如果<h1>标签中有属性怎么办?如果标题包含元素子级会怎样? - Gordon
2
@Gordon:属性问题可以使用此正则表达式解决:#<h1(?:"(?:[^\\\"]|\\\.)*"|\'(?:[^\\\\\']|\\\.)*\'|[^\'">])*>(.*?)</h1>#i(我认为仍然描述了一个常规语言,因此可以用有限状态机表示)。子元素的问题不存在,因为无论如何都不可能在另一个<h1>中存在<h1>。编辑:该正则表达式是针对单引号PHP字符串编写的。 - Daniel Egeberg
2
@Daniel,你必须承认这完全无法阅读 :) 此外,h1中可以有内联元素。那么span呢?strongs?ems?此页面的h1内部有一个链接。正则表达式没有TextNodes的概念。它只知道字符串。 - Gordon
2
这个正则表达式仍然可以工作,即使H1元素内有行内元素... 在我看来,它是否难以阅读并不重要,因为它非常适合设置和忘记的功能。 - evilunix
十年后,如果你真正懂得并且知道如何编写正则表达式的话,这段代码仍然是非常易读的。我喜欢这个解决方案,正则表达式也很简单。对于双引号匹配,只需要使用(' | ")即可。 - Mike Kormendy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接