如何解析HTML/XML并从中提取信息?
我更喜欢使用其中一个本地XML扩展, 因为它们与PHP捆绑在一起,通常比所有第三方库更快,并且可以完全控制标记。
DOM扩展允许您通过PHP 5中的DOM API对XML文档进行操作。它是W3C的文档对象模型核心级别3的实现,这是一个平台和语言中立的接口,允许程序和脚本动态访问和更新文档的内容、结构和样式。
DOM能够解析和修改现实世界(破碎的)HTML,并且它可以执行XPath查询。它基于libxml。
要想熟练使用DOM需要一些时间,但我认为这段时间值得花费。由于DOM是一个语言不可知的接口,所以你会发现许多语言都有其实现,因此如果您需要更改编程语言,那么您很可能已经知道如何使用该语言的DOM API。
如何使用DOM扩展已经在StackOverflow上得到了详细介绍,因此如果您选择使用它,可以确信您遇到的大多数问题都可以通过搜索/浏览Stack Overflow来解决。
XMLReader扩展是一个XML拉取解析器。阅读器充当游标,在文档流上向前移动,并在途中停留在每个节点。
像DOM一样,XMLReader也基于libxml。我不知道如何触发HTML解析器模块,因此使用XMLReader解析损坏的HTML可能比使用DOM更不稳健,因为您可以明确告诉它使用libxml的HTML解析器模块。
另一个答案中提供了基本用法示例。
此扩展允许您创建XML解析器,然后为不同的XML事件定义处理程序。每个XML解析器还有一些可以调整的参数。
XML解析器库也基于libxml,并实现了SAX风格的XML推送解析器。它可能比DOM或SimpleXML更适合内存管理,但比XMLReader实现的拉取解析器更难使用。
如果您想使用第三方库,我建议使用一个实际上使用DOM/libxml的库,而不是字符串解析。
FluentDOM为PHP中的DOMDocument提供了类似于jQuery的流畅XML接口。选择器使用XPath或CSS编写(使用CSS到XPath转换器)。当前版本扩展了DOM,实现了标准接口,并添加了来自DOM Living Standard的功能。FluentDOM可以加载JSON、CSV、JsonML、RabbitFish等格式。可以通过Composer安装。
Wa72\HtmlPageDom
是一个用于使用DOM轻松操作HTML文档的PHP库。它需要Symfony2组件中的DomCrawler进行遍历DOM树,并通过添加操作HTML文档的DOM树的方法来扩展它。
phpQuery是基于jQuery JavaScript库的服务器端、可链接的、CSS3选择器驱动的文档对象模型(DOM)API。该库使用PHP5编写,并提供额外的命令行界面(CLI)。
虽然被描述为“废弃且有缺陷:使用需谨慎”,但似乎仍在最低限度地维护。
Laminas\Dom组件(前身为Zend_DOM)提供了处理DOM文档和结构的工具。目前,我们提供Laminas\Dom\Query
,它提供了一个统一的接口,利用XPath和CSS选择器查询DOM文档。
此软件包被认为已经功能完备,并处于仅安全性维护模式。
fDOMDocument扩展了标准DOM以在所有错误情况下使用异常而不是PHP警告或通知。他们还添加了各种自定义方法和快捷方式,以方便使用DOM并简化其用法。
sabre/xml是一个库,它包装和扩展XMLReader和XMLWriter类以创建一个简单的“xml到对象/数组”映射系统和设计模式。写入和读取XML是单遍的,因此可以在大型XML文件上快速且需要低内存。
FluidXML是一个PHP库,用于使用简洁流畅的API操作XML。它利用XPath和流畅编程模式使操作变得有趣且有效。
构建在DOM/libxml之上的好处是,由于您基于本机扩展,因此可以获得良好的性能。然而,并非所有第三方库都采用这种方法。以下是其中一些:
- 一个用PHP5+编写的HTML DOM解析器,让您以非常简单的方式操纵HTML!
- 需要PHP 5+。
- 支持无效的HTML。
- 像jQuery一样使用选择器在HTML页面上查找标签。
- 在一行中提取HTML内容。
我通常不推荐使用此解析器。代码库很糟糕,解析器本身相当缓慢且占用内存。并非所有jQuery选择器(例如子选择器)都可行。任何基于libxml的库都应该轻松胜过它。
PHPHtmlParser是一个简单、灵活的HTML解析器,允许您使用任何CSS选择器(如jQuery)选择标签。目标是为开发需要快速、简便的方式来抓取HTML的工具提供帮助,无论它是否有效!这个项目最初是由sunra/php-simple-html-dom-parser支持的,但支持似乎已经停止了,所以这个项目是我对他以前的工作的适应。
再次强调,我不建议使用这个解析器。它的速度相对较慢,CPU 使用率高。同时,也没有清除已创建 DOM 对象内存的功能。这些问题尤其在嵌套循环中表现得更为明显。文档本身存在不准确和拼写错误,自 2016 年 4 月 14 日以来也没有修复响应。
您可以使用上述方法解析HTML5,但由于HTML5允许标记的存在,可能会出现一些怪异情况。因此,对于HTML5,您可能需要考虑使用专用解析器。请注意,这些解析器是用PHP编写的,因此与低级语言中的编译扩展相比,性能较慢且内存使用增加。
HTML5DOMDocument扩展了原生的DOMDocument库。它修复了一些错误并添加了一些新功能。
- 保留HTML实体(DOMDocument不会)
- 保留空标签(DOMDocument不会)
- 允许插入HTML代码,将正确的部分移到它们应该去的地方(head元素插入到head中,body元素插入到body中)
- 允许使用CSS选择器查询DOM(目前可用的有:
*
、tagname
、tagname#id
、#id
、tagname.classname
、.classname
、tagname.classname.classname2
、.classname.classname2
、tagname[attribute-selector]
、[attribute-selector]
、div, p
、div p
、div > p
、div + p
和p ~ ul
)- 添加对element->classList的支持。
- 添加对element->innerHTML的支持。
- 添加对element->outerHTML的支持。
一个HTML5序列化器 支持PHP命名空间 Composer支持 基于事件的(SAX-like)解析器 DOM树构建器 与QueryPath的互操作性 运行在PHP 5.3.0或更新版本HTML5是一个符合标准的HTML5解析器和编写器,完全由PHP编写。它稳定可靠,被许多生产网站使用,并已经超过500万次下载。
HTML5提供以下功能。
最后也是最不推荐的,你可以使用正则表达式从HTML中提取数据。一般来说,在HTML上使用正则表达式是不被鼓励的。
大多数在网上找到的匹配标记的片段都很脆弱。在大多数情况下,它们只适用于非常特定的HTML片段。微小的标记更改,比如在某个地方添加空格或添加或更改标记中的属性,可能会导致正则表达式失败,如果没有正确编写,则会出现问题。在对HTML使用RegEx之前,您应该知道自己在做什么。
HTML解析器已经知道HTML的语法规则。必须为您编写的每个新正则表达式教授正则表达式。在某些情况下,RegEx很好,但这确实取决于您的用例。
您可以编写更可靠的解析器, 但使用正则表达式编写完整且可靠的自定义解析器是浪费时间的,因为前面提到的库已经存在并且在此方面执行得更好。
还请参阅以克苏鲁方式解析HTML
如果你想花点钱,可以看看:
我与PHP Architect或作者没有关联。
注意:正如其名称所示,它可以用于简单的任务。它使用正则表达式而不是HTML解析器,因此在处理更复杂的任务时速度会相对较慢。它的大部分代码库是在2008年编写的,自那时以来只进行了小幅改进。它不遵循现代PHP编码标准,并且难以合并到现代的PSR兼容项目中。
// Create DOM from URL or file
$html = file_get_html('http://www.example.com/');
// Find all images
foreach($html->find('img') as $element)
echo $element->src . '<br>';
// Find all links
foreach($html->find('a') as $element)
echo $element->href . '<br>';
// Create DOM from string
$html = str_get_html('<div id="hello">Hello</div><div id="world">World</div>');
$html->find('div', 1)->class = 'bar';
$html->find('div[id=hello]', 0)->innertext = 'foo';
echo $html;
// Dump contents (without tags) from HTML
echo file_get_html('http://www.google.com/')->plaintext;
// Create DOM from URL
$html = file_get_html('http://slashdot.org/');
// Find all article blocks
foreach($html->find('div.article') as $article) {
$item['title'] = $article->find('div.title', 0)->plaintext;
$item['intro'] = $article->find('div.intro', 0)->plaintext;
$item['details'] = $article->find('div.details', 0)->plaintext;
$articles[] = $item;
}
print_r($articles);
只需使用DOMDocument->loadHTML()即可完成。libxml的HTML解析算法非常出色且快速,并且与流行观点相反,不会因HTML格式错误而出错。
为什么你不应该(以及何时应该)使用正则表达式?
首先,一个常见的错误:正则表达式并不适用于 "解析" 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");
然而,有一些特定的用例它们可以帮上忙。
<!--
,但是注释有时更适合于提取。特别是伪 HTML 变体 <$var>
或 SGML 残留物可以使用正则表达式轻松处理。有时候,事先使用正则表达式 /<!--CONTENT-->(.+?)<!--END-->/
提取 HTML 片段并使用更简单的 HTML 解析器前端处理其余部分甚至是明智的选择。
注意:我实际上有一个应用程序,在其中交替使用 XML 解析和正则表达式。就在上周,PyQuery 解析崩溃了,但正则表达式仍然有效。是的,很奇怪,我自己也无法解释。但就这样发生了。
所以,请不要因为它不符合 "正则表达式=邪恶" 的梗而将现实考虑投票下降。 但是也请不要过于高估它。这只是这个主题的一个侧面说明。
DOMComment
可以读取注释,因此没有必要使用正则表达式来实现。 - GordonDOM
使用 libxml
库,而 libxml
有一个独立的 HTML 解析器模块。当使用 loadHTML()
方法加载 HTML 时,会自动使用该解析器模块,因此可以很好地加载“现实世界”(也就是有缺陷的)HTML。 - GordonphpQuery 和 QueryPath 在复制 jQuery 流畅 API 方面非常相似。这也是它们是在 PHP 中进行 HTML 解析的两种最简单方法之一的原因。
QueryPath 示例
基本上,你首先需要从一个 HTML 字符串创建一个可查询的 DOM 树:
$qp = qp("<html><body><h1>title</h1>..."); // or give filename or URL
生成的对象包含HTML文档的完整树形结构表示。它可以使用DOM方法遍历。但通常的方法是像jQuery一样使用CSS选择器:
$qp->find("div.classname")->children()->...;
foreach ($qp->find("p img") as $img) {
print qp($img)->attr("src");
}
大多数情况下,您会使用简单的#id
和.class
或DIV
标签选择器来进行->find()
。但是您也可以使用XPath语句,有时它们更快。此外,像->children()
和->text()
以及特别是->attr()
这样的通常的jQuery方法简化了提取正确的HTML片段。(并且已经解码了它们的SGML实体。) $qp->xpath("//div/p[1]"); // get first paragraph in a div
QueryPath还允许将新标签注入流中(->append
),然后输出和美化更新后的文档(->writeHTML
)。它不仅可以解析格式不正确的HTML,还可以解析各种带有命名空间的XML方言,并且可以从HTML微格式(XFN、vCard)中提取数据。
$qp->find("a[target=_blank]")->toggleClass("usability-blunder");
.
phpQuery或QueryPath?
通常情况下,QueryPath更适合文档操作。虽然phpQuery也实现了一些伪AJAX方法(仅是HTTP请求)以更接近jQuery的方式进行操作,但因为功能较少,所以phpQuery通常比QueryPath快。
有关差异的进一步信息,请参见这篇来自tagbyte.org的互联网存档比较文章。(原始来源已经丢失,所以这里是一个互联网存档链接。是的,你仍然可以找到丢失的页面,人们。)
优点
->find("a img, a object, div a")
简单的HTML DOM是一个伟大的开源解析器:
它以面向对象的方式处理DOM元素,并且新的迭代版本对不规范的代码有很好的支持。还有一些像JavaScript中一样的优秀函数,比如“find”函数,它会返回所有该标签名称元素的实例。
我在许多不同类型的网页上测试过这个工具,并认为它运作得非常好。
这里没有提到的一种通用方法是将HTML通过Tidy处理,可以设置为输出保证有效的XHTML。然后您可以在其上使用任何旧的XML库。
但对于您的具体问题,您应该查看这个项目:http://fivefilters.org/content-only/ -- 它是Readability算法的修改版本,旨在从页面中提取文本内容(而不是标题和页脚)。
这通常被称为屏幕抓取。我用的库是Simple HTML Dom解析器。