如何解析HTML/XML并从中提取信息?
当前回答
我创建了一个名为PHPPowertools/DOM Query的库,它允许您像使用jQuery一样抓取HTML5和XML文档。
在后台,它使用symfony/DomCrawler将CSS选择器转换为XPath选择器。它总是使用相同的DomDocument,即使在将一个对象传递给另一个对象时也是如此,以确保良好的性能。
示例用法:
namespace PowerTools;
// Get file content
$htmlcode = file_get_contents('https://github.com');
// Define your DOMCrawler based on file string
$H = new DOM_Query($htmlcode);
// Define your DOMCrawler based on an existing DOM_Query instance
$H = new DOM_Query($H->select('body'));
// Passing a string (CSS selector)
$s = $H->select('div.foo');
// Passing an element object (DOM Element)
$s = $H->select($documentBody);
// Passing a DOM Query object
$s = $H->select( $H->select('p + p'));
// Select the body tag
$body = $H->select('body');
// Combine different classes as one selector to get all site blocks
$siteblocks = $body->select('.site-header, .masthead, .site-body, .site-footer');
// Nest your methods just like you would with jQuery
$siteblocks->select('button')->add('span')->addClass('icon icon-printer');
// Use a lambda function to set the text of all site blocks
$siteblocks->text(function( $i, $val) {
return $i . " - " . $val->attr('class');
});
// Append the following HTML to all site blocks
$siteblocks->append('<div class="site-center"></div>');
// Use a descendant selector to select the site's footer
$sitefooter = $body->select('.site-footer > .site-center');
// Set some attributes for the site's footer
$sitefooter->attr(array('id' => 'aweeesome', 'data-val' => 'see'));
// Use a lambda function to set the attributes of all site blocks
$siteblocks->attr('data-val', function( $i, $val) {
return $i . " - " . $val->attr('class') . " - photo by Kelly Clark";
});
// Select the parent of the site's footer
$sitefooterparent = $sitefooter->parent();
// Remove the class of all i-tags within the site's footer's parent
$sitefooterparent->select('i')->removeAttr('class');
// Wrap the site's footer within two nex selectors
$sitefooter->wrap('<section><div class="footer-wrapper"></div></section>');
[...]
支持的方法:
[x] $(1)[x] $.parseHTML[x] $.parseXML[x] $.parseJSON[x] $选择添加[x] $selection.addClass[x] $selection.after[x] $selection.append[x] $选择属性[x] $选择之前[x] $selection.children[x] $选择最接近[x] $selection.contents[x] $选择分离[x] $selection.每个[x] $selection.eq[x] $selection.empty(2)[x] $selection.find[x] $selection.first[x] $selection.get[x] $selection.insert之后[x] $selection.insertBefore[x] $selection.last[x] $selection.parent[x] $selection.parents[x] $selection.remove[x] $selection.removeAttr[x] $selection.removeClass[x] $selection.text[x] $selection.wrap
出于明显原因,重命名为“select”重命名为“void”,因为“empty”是PHP中的保留字
注:
该库还包括自己的零配置自动加载器,用于PSR-0兼容库。所包含的示例应该可以开箱即用,无需任何额外配置。或者,您可以将其与composer一起使用。
其他回答
我在这里没有提到的一种通用方法是通过Tidy运行HTML,它可以设置为输出保证有效的XHTML。然后可以在上面使用任何旧的XML库。
但对于您的具体问题,您应该看看这个项目:http://fivefilters.org/content-only/--这是Readability算法的一个修改版本,它旨在从页面中提取文本内容(而不是页眉和页脚)。
只需使用DOMDocument->loadHTML()就可以了。libxml的HTML解析算法非常好,速度也很快,与流行的观点相反,它不会对格式错误的HTML产生影响。
XML_HTMLMax相当稳定——即使不再维护它。另一种选择是通过HtmlTidy将HTML导入,然后用标准的XML工具解析它。
为什么不应该以及何时应该使用正则表达式?
首先,一个常见的误称:Regexp不用于“解析”HTML。然而,正则表达式可以“提取”数据。提取是它们的目的。与适当的SGML工具包或基线XML解析器相比,正则表达式HTML提取的主要缺点是它们的语法工作和不同的可靠性。
考虑制作一个稍微可靠的HTML提取正则表达式:
<a\s+class="?playbutton\d?[^>]+id="(\d+)".+? <a\s+class="[\w\s]*title
[\w\s]*"[^>]+href="(http://[^">]+)"[^>]*>([^<>]+)</a>.+?
比简单的phpQuery或QueryPath等效文件可读性差得多:
$div->find(".stationcool a")->attr("title");
然而,在某些特定的用例中,它们可以提供帮助。
许多DOM遍历前端不显示HTML注释<!--,然而,它们有时是用于提取的更有用的锚。特别是伪HTML变体<$var>或SGML残基很容易用正则表达式驯服。通常,正则表达式可以节省后期处理。然而,HTML实体通常需要手动管理。最后,对于提取<img src=urls等极其简单的任务,它们实际上是一个可能的工具。与SGML/XML解析器相比,速度优势主要用于这些非常基本的提取过程。
有时甚至建议使用正则表达式/<--内容-->(.+?)<--END-->/并使用更简单的HTML解析器前端处理其余部分。
注意:我实际上有一个应用程序,在那里我交替使用XML解析和正则表达式。就在上周,PyQuery解析中断,正则表达式仍然有效。是的,很奇怪,我自己也解释不了。但事情就是这样发生的。因此,请不要因为现实世界的考虑与正则表达式=邪恶模因不匹配就投票否决。但我们也不要对此投太多赞成票。这只是这个话题的一个旁注。
您可以尝试的另一个选项是QueryPath。它的灵感来自jQuery,但在服务器上使用PHP,并在Drupal中使用。