您如何在 PHP 中解析和处理 HTML/XML?

Posted

技术标签:

【中文标题】您如何在 PHP 中解析和处理 HTML/XML?【英文标题】:How do you parse and process HTML/XML in PHP? 【发布时间】:2011-04-04 09:46:17 【问题描述】:

如何解析 html/XML 并从中提取信息?

【问题讨论】:

【参考方案1】:

原生 XML 扩展

我更喜欢使用 native XML extensions 之一,因为它们与 php 捆绑在一起,通常比所有 3rd 方库都快,并且为我提供了对标记所需的所有控制。

DOM

DOM 扩展允许您使用 PHP 5 通过 DOM API 对 XML 文档进行操作。它是 W3C 文档对象模型核心级别 3 的实现,这是一个平台和语言中立的接口,允许程序和脚本动态访问和更新文档的内容、结构和样式。

DOM 能够解析和修改现实世界(损坏的)HTML,它可以做到XPath queries。它基于libxml。

使用 DOM 需要一些时间来提高工作效率,但 IMO 非常值得。由于 DOM 是一个与语言无关的接口,您会发现多种语言的实现,因此如果您需要更改您的编程语言,那么您很可能已经知道如何使用该语言的 DOM API。

如何使用 DOM 扩展已经在 *** 上covered extensively,所以如果您选择使用它,您可以确定您遇到的大部分问题都可以通过搜索/浏览 Stack Overflow 来解决。

basic usage example 和 general conceptual overview 在其他答案中可用。

XMLReader

XMLReader 扩展是一个 XML 拉式解析器。阅读器充当文档流上的光标,并在途中的每个节点处停止。

XMLReader 和 DOM 一样,都是基于 libxml。我不知道如何触发 HTML 解析器模块,因此使用 XMLReader 解析损坏的 HTML 可能不如使用 DOM 强大,在这种情况下您可以明确告诉它使用 libxml 的 HTML 解析器模块。

basic usage example 在另一个答案中可用。

XML Parser

此扩展允许您创建 XML 解析器,然后为不同的 XML 事件定义处理程序。每个 XML 解析器还有一些可以调整的参数。

XML Parser 库也是基于 libxml,并实现了SAX 样式的 XML 推送解析器。它可能是比 DOM 或 SimpleXML 更好的内存管理选择,但比 XMLReader 实现的拉式解析器更难使用。

SimpleXml

SimpleXML 扩展提供了一个非常简单易用的工具集,可以将 XML 转换为可以使用普通属性选择器和数组迭代器处理的对象。

当您知道 HTML 是有效的 XHTML 时,SimpleXML 是一个选项。如果您需要解析损坏的 HTML,甚至不要考虑 SimpleXml,因为它会阻塞。

有basic usage example,还有lots of additional examples in the PHP Manual。


第 3 方库(基于 libxml)

如果您更喜欢使用第 3 方库,我建议您使用实际上在下面使用 DOM/libxml 的库,而不是字符串解析。

FluentDom

FluentDOM 为 PHP 中的 DOMDocument 提供了一个类似 jQuery 的 fluent XML 接口。选择器是用 XPath 或 CSS 编写的(使用 CSS 到 XPath 转换器)。当前版本扩展了 DOM 实现标准接口并添加了 DOM 生活标准的功能。 FluentDOM 可以加载 JSON、CSV、JsonML、RabbitFish 等格式。可以通过 Composer 安装。

HtmlPageDom

Wa72\HtmlPageDom 是一个 PHP 库,用于轻松操作 HTML 使用 DOM 的文档。它需要DomCrawler from Symfony2 components 进行遍历 DOM 树并通过添加操作方法来扩展它 HTML 文档的 DOM 树。

phpQuery

phpQuery 是一个基于 jQuery javascript 库的服务器端、可链接、CSS3 选择器驱动的文档对象模型 (DOM) API。 该库是用 PHP5 编写的,并提供了额外的命令行界面 (CLI)。

这被描述为“废弃软件和漏洞:使用风险自负”,但似乎维护最少。

laminas-dom

Laminas\Dom 组件(以前称为 Zend_DOM)提供了用于处理 DOM 文档和结构的工具。目前,我们提供Laminas\Dom\Query,它为使用 XPath 和 CSS 选择器查询 DOM 文档提供了一个统一的接口。

这个包被认为是功能完整的,现在处于仅安全维护模式。

fDOMDocument

fDOMDocument 扩展了标准 DOM 以在所有错误情况下使用异常,而不是 PHP 警告或通知。为了方便和简化 DOM 的使用,他们还添加了各种自定义方法和快捷方式。

sabre/xml

sabre/xml 是一个包装和扩展 XMLReader 和 XMLWriter 类的库,以创建一个简单的“xml 到对象/数组”映射系统和设计模式。写入和读取 XML 是一次性的,因此速度很快,并且在大型 xml 文件上需要的内存较少。

FluidXML

FluidXML 是一个 PHP 库,用于使用简洁流畅的 API 操作 XML。 它利用 XPath 和流畅的编程模式变得有趣且有效。


第 3 方(不基于 libxml)

基于 DOM/libxml 构建的好处是您可以立即获得良好的性能,因为您是基于本机扩展的。然而,并不是所有的第 3 方库都走这条路。下面列出了其中一些

PHP Simple HTML DOM Parser

用 PHP5+ 编写的 HTML DOM 解析器让您可以非常轻松地操作 HTML! 需要 PHP 5+。 支持无效的 HTML。 使用类似于 jQuery 的选择器在 HTML 页面上查找标签。 在一行中从 HTML 中提取内容。

我一般不推荐这个解析器。代码库很糟糕,解析器本身相当慢且内存不足。并非所有的 jQuery 选择器(例如 child selectors)都是可能的。任何基于 libxml 的库都应该轻松胜过这一点。

PHP Html Parser

PHPHtmlParser 是一个简单、灵活的 html 解析器,它允许您使用任何 css 选择器(如 jQuery)来选择标签。目标是协助开发需要快速、简单的方法来抓取 html 的工具,无论它是否有效!这个项目最初是由 sunra/php-simple-html-dom-parser 支持的,但支持似乎已经停止,所以这个项目是我对他以前工作的改编。

同样,我不推荐这个解析器。由于 CPU 使用率很高,所以速度相当慢。也没有清除已创建 DOM 对象内存的功能。这些问题特别适用于嵌套循环。文档本身不准确且拼写错误,自 2016 年 4 月 14 日起对修复没有任何回应。


HTML 5

您可以使用上面的方法来解析 HTML5,但 there can be quirks 由于 HTML5 允许的标记。因此,对于 HTML5,您可能需要考虑使用专用解析器。请注意,这些是用 PHP 编写的,因此与使用低级语言编译的扩展相比,性能较慢且内存使用量增加。

HTML5DomDocument

HTML5DOMDocument 扩展了本机 DOMDocument 库。它修复了一些错误并添加了一些新功能。

保留 html 实体(DOMDocument 不保留) 保留空标签(DOMDocument 不保留) 允许插入将正确部分移动到适当位置的 HTML 代码(头部元素插入头部,主体元素插入正文) 允许使用 CSS 选择器查询 DOM(当前可用:*tagnametagname#id#idtagname.classname.classnametagname.classname.classname2.classname.classname2、@9876 [attribute-selector]div, pdiv pdiv > pdiv + pp ~ ul。) 添加对 element->classList 的支持。 添加对 element->innerHTML 的支持。 添加对 element->outerHTML 的支持。

HTML5

HTML5 是完全用 PHP 编写的符合标准的 HTML5 解析器和编写器。它很稳定,并在许多生产网站中使用,下载量超过 500 万次。

HTML5 提供以下功能。

HTML5 序列化程序 支持 PHP 命名空间 作曲家支持 基于事件(类似 SAX)的解析器 DOM 树构建器 与 QueryPath 的互操作性 在 PHP 5.3.0 或更高版本上运行

正则表达式

最后也是最不推荐,您可以使用regular expressions 从 HTML 中提取数据。通常不鼓励在 HTML 上使用正则表达式。

您会在网络上找到用于匹配标记的大多数 sn-ps 都很脆弱。在大多数情况下,它们只适用于非常特殊的 HTML 片段。微小的标记更改,例如在某处添加空格,或添加或更改标记中的属性,可能会使 RegEx 在编写不正确时失败。在 HTML 上使用 RegEx 之前,您应该知道自己在做什么。

HTML 解析器已经知道 HTML 的语法规则。必须为您编写的每个新 RegEx 教授正则表达式。正则表达式在某些情况下很好,但这实际上取决于您的用例。

您 can write more reliable parsers,但是使用正则表达式编写一个完整且可靠的自定义解析器是浪费时间,因为上述库已经存在并且在这方面做得更好。

另见Parsing Html The Cthulhu Way


书籍

如果你想花点钱,看看

PHP Architect's Guide to Webscraping with PHP

我不隶属于 PHP Architect 或作者。

【讨论】:

@Naveed 取决于您的需求。我不需要 CSS 选择器查询,这就是我专门使用带有 XPath 的 DOM 的原因。 phpQuery 旨在成为一个 jQuery 端口。 Zend_Dom 是轻量级的。你真的必须检查一下,看看你最喜欢哪一个。 @Ms2ger 大部分,但不完全。就像上面已经指出的那样,您可以使用基于 libxml 的解析器,但在某些特殊情况下会阻塞。如果您需要最大的兼容性,最好使用专用的解析器。我更喜欢保持区别。 你不使用 PHP Simple HTML DOM Parser 的观点似乎没有实际意义。 截至 2012 年 3 月 29 日,DOM 不支持 html5,XMLReader 不支持 HTML,最后一次提交 html5lib for PHP 是在 2009 年 9 月。使用什么来解析 HTML5、HTML4 和 XHTML? @Nasha 我故意从上面的列表中排除了臭名昭著的 Zalgo 咆哮,因为它本身并没有太大帮助,并且自从它被编写以来就导致了相当多的货物崇拜。无论正则表达式作为解决方案多么合适,人们都会被该链接打倒。要获得更平衡的意见,请参阅我 确实 包含的链接,并通过***.com/questions/4245008/… 的 cmets【参考方案2】:

试试Simple HTML DOM Parser

用 PHP 5+ 编写的 HTML DOM 解析器,可让您以非常简单的方式操作 HTML! 需要 PHP 5+。 支持无效的 HTML。 使用类似于 jQuery 的选择器在 HTML 页面上查找标签。 在一行中从 HTML 中提取内容。 Download

注意:顾名思义,它可以用于简单的任务。它使用正则表达式而不是 HTML 解析器,因此对于更复杂的任务会相当慢。它的大部分代码库是在 2008 年编写的,从那时起只做了很小的改进。它不遵循现代 PHP 编码标准,并且很难融入现代 PSR 兼容项目。

Examples:

如何获取 HTML 元素:

// 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>';

如何修改 HTML 元素:

// 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;

从 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);

【讨论】:

首先我需要准备一些东西,比如糟糕的 DOM,无效代码,还有针对 DNSBL 引擎的 js 分析,这也将用于查找恶意站点/内容,以及我围绕我构建的框架构建了我的网站,它需要干净、可读且结构良好。 SimpleDim 很棒,但代码有点乱 @Robert 您可能还想查看htmlpurifier.org 以了解与安全相关的内容。 他有一个正确的观点:simpleHTMLDOM 很难扩展,除非你使用装饰器模式,我觉得它很笨拙。我发现自己不寒而栗只是自己对底层类进行更改。 我所做的是通过 tidy 运行我的 html,然后将其发送到 SimpleDOM。 我目前正在使用它,将它作为项目的一部分运行以处理数百个 url。它变得非常缓慢,并且定期超时持续存在。这是一个很棒的初学者脚本,直观易学,但对于更高级的项目来说太基础了。【参考方案3】:

只需使用DOMDocument->loadHTML() 即可。 libxml 的 HTML 解析算法非常好和快速,而且与流行的看法相反,不会因格式错误的 HTML 而窒息。

【讨论】:

是的。它还可以与 PHP 的内置 XPath 和 XSLTProcessor 类一起使用,这些类非常适合提取内容。 对于真正混乱的 HTML,您总是可以在将其交给 DOM 之前通过 htmltidy 运行它。每当我需要从 HTML 中抓取数据时,我总是使用 DOM,或者至少是 simplexml。 加载格式错误的 HTML 的另一件事是调用 libxml_use_internal_errors(true) 以防止出现停止解析的警告。 我已经使用 DOMDocument 解析了大约 1000 个 html 源(使用不同字符集编码的各种语言),没有任何问题。您可能会遇到编码问题,但它们并非不可克服。您需要知道 3 件事:1) loadHTML 使用元标记的字符集来确定编码 2) 如果 html 内容不包含此信息,#2 会导致错误的编码检测 3) 错误的 UTF-8 字符可能会使解析器出错。在这种情况下,请结合使用 mb_detect_encoding() 和 Simplepie RSS Parser 的编码/转换/去除错误的 UTF-8 字符代码来解决问题。 DOM 确实支持 XPath,看看DOMXPath。【参考方案4】:

为什么不应该以及什么时候应该使用正则表达式?

首先,一个常见的误称:正则表达式不适用于 "parsing" 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 cmets &lt;!--,但它们有时是更有用的提取锚。特别是伪 HTML 变体 &lt;$var&gt; 或 SGML 残基很容易用正则表达式驯服。 通常正则表达式可以节省后期处理。然而,HTML 实体通常需要手动维护。 最后,对于非常简单的任务,例如提取

有时甚至建议使用正则表达式 /&lt;!--CONTENT--&gt;(.+?)&lt;!--END--&gt;/ 预提取 HTML 的 sn-p 并使用更简单的 HTML 解析器前端处理剩余部分。

注意:我实际上有这个app,我在其中交替使用 XML 解析和正则表达式。就在上周,PyQuery 解析失败了,而正则表达式仍然有效。是的,很奇怪,我自己也无法解释。但事情就这样发生了。 所以请不要仅仅因为它与 regex=evil meme 不匹配而对现实世界的考虑投反对票。 但我们也不要对此投票过多。这只是本主题的旁注。

【讨论】:

DOMComment 可以读取 cmets,因此没有理由为此使用 Regex。 SGML 工具包或 XML 解析器都不适合解析现实世界的 HTML。为此,只有专用的 HTML 解析器是合适的。 @Alohci DOM 使用 libxml 和 libxml has a separate HTML parser 模块,这将在使用 loadHTML() 加载 HTML 时使用,因此它可以非常多地加载“真实世界”(读取损坏)HTML。 好吧,就您的“现实世界考虑”观点发表评论。当然,在解析 HTML 时,Regex 有一些有用的情况。还有一些使用 GOTO 的有用情况。变量变量也有一些有用的情况。因此,没有任何特定的实现是明确地使用它的代码腐烂。但这是一个非常强烈的警告信号。普通的开发人员不太可能有足够的细微差别来区分差异。所以一般来说,Regex GOTO 和 Variable-Variables 都是邪恶的。有非邪恶的用途,但那些是例外(而且很少见)......(恕我直言) @mario:实际上,HTML可以使用正则表达式“正确”解析,尽管通常需要其中几个才能完成一项公平的工作。在一般情况下,这只是一种皇家痛苦。在具有明确输入的特定情况下,它几乎是微不足道的。这些是人们应该使用正则表达式的情况。对于一般情况,您确实需要大而古老的饥饿重型解析器,尽管临时用户并不总是清楚在哪里画出这条线。无论哪种代码更简单,更容易,获胜。【参考方案5】:

请注意,此答案推荐现在已废弃 10 多年的库。

phpQuery 和QueryPath 在复制流畅的 jQuery API 方面非常相似。这也是为什么它们是在 PHP 中正确解析 HTML 的两种最简单的方法。

查询路径示例

基本上你首先从一个 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");
 

大多数情况下,您想为-&gt;find() 使用简单的#id.classDIV 标签选择器。但您也可以使用XPath 语句,有时会更快。还有典型的 jQuery 方法,如 -&gt;children()-&gt;text(),尤其是 -&gt;attr(),简化了提取正确的 HTML sn-ps。 (并且已经解码了他们的 SGML 实体。)

 $qp->xpath("//div/p[1]");  // get first paragraph in a div

QueryPath 还允许将新标签注入流 (-&gt;append),然后输出和美化更新的文档 (-&gt;writeHTML)。它不仅可以解析格式错误的 HTML,还可以解析各种 XML 方言(带有命名空间),甚至可以从 HTML 微格式(XFN、vCard)中提取数据。

 $qp->find("a[target=_blank]")->toggleClass("usability-blunder");

.

phpQuery 还是 QueryPath?

通常 QueryPath 更适合处理文档。虽然 phpQuery 还实现了一些伪 AJAX 方法(只是 HTTP 请求)以更接近 jQuery。据说phpQuery往往比QueryPath快(因为整体功能少)。

有关差异的更多信息,请参阅this comparison on the wayback machine from tagbyte.org。 (原始来源丢失了,所以这里有一个互联网档案链接。是的,你仍然可以找到丢失的页面,人们。)

优势

简单性和可靠性 简单易用的替代品-&gt;find("a img, a object, div a") 正确的数据反转义(与正则表达式 grepping 相比)

【讨论】:

【参考方案6】:

Simple HTML DOM 是一个很棒的开源解析器:

simplehtmldom.sourceforge

它以面向对象的方式处理 DOM 元素,新的迭代对不兼容的代码有很多覆盖。还有一些很棒的函数,就像您在 JavaScript 中看到的那样,例如“find”函数,它将返回该标记名称元素的所有实例。

我已经在许多工具中使用了它,在许多不同类型的网页上对其进行了测试,我认为它效果很好。

【讨论】:

【参考方案7】:

我在这里没有看到的一种通用方法是通过Tidy 运行HTML,它可以设置为输出保证有效的XHTML。然后你可以在上面使用任何旧的 XML 库。

但是对于您的具体问题,您应该看看这个项目:http://fivefilters.org/content-only/ - 它是 Readability 算法的修改版本,旨在仅提取文本内容(不是页眉和页脚)页面。

【讨论】:

【参考方案8】:

对于 1a 和 2:我会投票支持新的 Symfony 组件类 DOMCrawler (DomCrawler)。 此类允许类似于 CSS 选择器的查询。查看此演示文稿以获取真实示例:news-of-the-symfony2-world。

该组件设计为独立工作,可以在没有 Symfony 的情况下使用。

唯一的缺点是它只适用于 PHP 5.3 或更高版本。

【讨论】:

类似jquery的css查询说得好,因为w3c文档中缺少一些东西,但在jquery中作为额外功能存在。【参考方案9】:

顺便说一下,这通常称为屏幕抓取。我为此使用的库是Simple HTML Dom Parser。

【讨论】:

不完全正确 (en.wikipedia.org/wiki/Screen_scraping#Screen_scraping)。线索在“屏幕”中;在所描述的情况下,不涉及屏幕。虽然,不可否认,这个词最近遭受了很多误用。 我不是截屏,要解析的内容将在我的同意下由内容提供者授权。【参考方案10】:

我们之前已经创建了很多爬虫来满足我们的需求。归根结底,通常是简单的正则表达式做得最好。虽然上面列出的库因其创建的原因而很好,但如果您知道自己在寻找什么,正则表达式是一种更安全的方法,因为您还可以处理无效的 HTML/XHTML 结构,这将如果通过大多数解析器加载,则会失败。

【讨论】:

【参考方案11】:

我推荐PHP Simple HTML DOM Parser。

它确实有很好的功能,例如:

foreach($html->find('img') as $element)
       echo $element->src . '<br>';

【讨论】:

【参考方案12】:

这听起来像是对 W3C XPath 技术的一个很好的任务描述。很容易表达诸如“返回嵌套在&lt;foo&gt;&lt;bar&gt;&lt;baz&gt; elements 中的img 标记中的所有href 属性”之类的查询。不是 PHP 爱好者,我无法告诉您 XPath 可能以何种形式可用。如果您可以调用外部程序来处理 HTML 文件,您应该可以使用 XPath 的命令行版本。 如需快速介绍,请参阅http://en.wikipedia.org/wiki/XPath。

【讨论】:

【参考方案13】:

使用 DOM 而不是字符串解析的 SimpleHtmlDom 的第三方替代方案:phpQuery、Zend_Dom、QueryPath 和 FluentDom。

【讨论】:

如果你已经复制了我的 cmets,至少要正确链接它们 ;) 那应该是:建议的第三方替代 SimpleHtmlDom 实际使用 DOM 而不是字符串解析:phpQuery, @ 987654328@、QueryPath 和 FluentDom。 好的答案是一个很好的来源。 ***.com/questions/3606792/…【参考方案14】:

是的,您可以为此目的使用 simple_html_dom。然而,我在 simple_html_dom 上做了很多工作,特别是对于网络抓取,并且发现它太脆弱了。它可以完成基本工作,但无论如何我都不推荐它。

我从未将 curl 用于此目的,但我了解到,curl 可以更有效地完成这项工作,并且更可靠。

请查看此链接:scraping-websites-with-curl

【讨论】:

curl 可以 get 文件,但它不会为您解析 HTML。这是最难的部分。【参考方案15】:

QueryPath 很好,但是要小心“跟踪状态”,因为如果你没有意识到它的含义,这可能意味着你会浪费大量的调试时间来试图找出发生了什么并且为什么代码不起作用。

这意味着对结果集的每次调用都会修改对象中的结果集,它不像在 jquery 中那样可链接,其中每个链接都是一个新集,您只有一个集合,它是查询的结果,每个函数调用会修改该单个集合。

为了获得类似 jquery 的行为,您需要在执行过滤/修改类似操作之前进行分支,这意味着它将更接近地反映 jquery 中发生的情况。

$results = qp("div p");
$forename = $results->find("input[name='forename']");

$results 现在包含 input[name='forename'] 的结果集,而不是原始查询 "div p" 这让我大吃一惊,我发现 QueryPath 跟踪过滤器和发现的所有内容修改您的结果并将它们存储在对象中。你需要这样做

$forename = $results->branch()->find("input[name='forname']")

那么$results不会被修改,你可以一次又一次地重复使用结果集,也许有更多知识的人可以澄清一下,但从我发现的情况来看基本上是这样的。

【讨论】:

【参考方案16】:

Advanced Html Dom 是一个简单的 HTML DOM 替代品,提供相同的界面,但它是基于 DOM 的,这意味着不会发生任何相关的内存问题。

它还具有完整的 CSS 支持,包括 jQuery 扩展。

【讨论】:

我从 Advanced Html Dom 得到了很好的结果,我认为它应该在接受的答案中。对于任何依赖它的人来说,一个重要的事情是“这个项目的目标是成为 PHP 简单 html dom 库的基于 DOM 的替代品......如果你使用 file/str_get_html 那么你不需要改变任何东西。” archive.is/QtSuj#selection-933.34-933.100 是您可能需要对代码进行更改以适应某些不兼容性。我在项目的 github 问题中注意到了四个我已知的问题。 github.com/monkeysuffrage/advanced_html_dom/issues【参考方案17】:

对于HTML5,html5 库已被废弃多年。我能找到的唯一具有最近更新和维护记录的 HTML5 库是 html5-php,它在一周多前刚刚发布到 beta 1.0。

【讨论】:

【参考方案18】:

我创建了一个名为 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]$selection.add [x]$selection.addClass [x]$selection.after [x]$selection.append [x]$selection.attr [x]$selection.before [x]$selection.children [x]$selection.closest [x]$selection.contents [x]$selection.detach [x]$selection.each [x]$selection.eq [x]$selection.empty(2) [x]$selection.find [x]$selection.first [x]$selection.get [x]$selection.insertAfter [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 兼容库。包含的示例应该开箱即用,无需任何额外配置。或者,您可以将它与作曲家一起使用。

【讨论】:

看起来是适合这项工作的工具,但在 Worpress 的 PHP 5.6.23 中没有为我加载。有关如何正确包含它的任何其他说明?包括:define("BASE_PATH", dirname(FILE));定义(“LIBRARY_PATH”,BASE_PATH . DIRECTORY_SEPARATOR . 'lib/vendor');需要 LIBRARY_PATH 。目录分隔符。 '加载器.php'; Loader::init(array(LIBRARY_PATH, USER_PATH));在functions.php中【参考方案19】:

您可以尝试使用 HTML Tidy 之类的东西来清理任何“损坏”的 HTML 并将 HTML 转换为 XHTML,然后您可以使用 XML 解析器对其进行解析。

【讨论】:

【参考方案20】:

我编写了一个通用的 XML 解析器,可以轻松处理 GB 文件。它基于 XMLReader 并且非常易于使用:

$source = new XmlExtractor("path/to/tag", "/path/to/file.xml");
foreach ($source as $tag) 
    echo $tag->field1;
    echo $tag->field2->subfield1;

这里是 github 仓库:XmlExtractor

【讨论】:

【参考方案21】:

您可以尝试的另一个选项是QueryPath。它受到 jQuery 的启发,但在服务器上使用 PHP 并在 Drupal 中使用。

【讨论】:

【参考方案22】:

XML_HTMLSax 相当稳定——即使它不再维护。另一种选择是通过Html Tidy 将您的 HTML 传输到管道中,然后使用标准 XML 工具对其进行解析。

【讨论】:

【参考方案23】:

处理 HTML/XML DOM 的方法有很多种,其中大部分已经被提及。因此,我不会尝试自己列出这些。

我只想补充一点,我个人更喜欢使用 DOM 扩展以及为什么:

iit 充分利用了底层 C 代码的性能优势 它是面向对象的 PHP(并允许我对其进行子类化) 它的级别相当低(这让我可以将它用作更高级行为的非臃肿基础) 它提供对 DOM 的每个部分的访问(不像 SimpleXml,它忽略了一些鲜为人知的 XML 功能) 它用于 DOM 抓取的语法类似于原生 Javascript 中使用的语法。

虽然我想念 DOMDocument 使用 CSS 选择器的能力,但有一种相当简单方便的方法来添加此功能:继承 DOMDocument 并添加类似 JS 的 querySelectorAllquerySelector 方法你的子类。

为了解析选择器,我建议使用来自Symfony framework 的极简CssSelector component。该组件只是将 CSS 选择器转换为 XPath 选择器,然后可以将其输入到 DOMXpath 以检索相应的节点列表。

然后您可以使用这个(仍然非常低级别的)子类作为更高级别类的基础,例如。解析非常特定类型的 XML 或添加更多类似 jQuery 的行为。

下面的代码直接来自我的DOM-Query library,并使用了我描述的技术。

HTML解析:

namespace PowerTools;

use \Symfony\Component\CssSelector\CssSelector as CssSelector;

class DOM_Document extends \DOMDocument 
    public function __construct($data = false, $doctype = 'html', $encoding = 'UTF-8', $version = '1.0') 
        parent::__construct($version, $encoding);
        if ($doctype && $doctype === 'html') 
            @$this->loadHTML($data);
         else 
            @$this->loadXML($data);
        
    

    public function querySelectorAll($selector, $contextnode = null) 
        if (isset($this->doctype->name) && $this->doctype->name == 'html') 
            CssSelector::enableHtmlExtension();
         else 
            CssSelector::disableHtmlExtension();
        
        $xpath = new \DOMXpath($this);
        return $xpath->query(CssSelector::toXPath($selector, 'descendant::'), $contextnode);
    

    [...]

    public function loadHTMLFile($filename, $options = 0) 
        $this->loadHTML(file_get_contents($filename), $options);
    

    public function loadHTML($source, $options = 0) 
        if ($source && $source != '') 
            $data = trim($source);
            $html5 = new HTML5(array('targetDocument' => $this, 'disableHtmlNsInDom' => true));
            $data_start = mb_substr($data, 0, 10);
            if (strpos($data_start, '<!DOCTYPE ') === 0 || strpos($data_start, '<html>') === 0) 
                $html5->loadHTML($data);
             else 
                @$this->loadHTML('<!DOCTYPE html><html><head><meta charset="' . $encoding . '" /></head><body></body></html>');
                $t = $html5->loadHTMLFragment($data);
                $docbody = $this->getElementsByTagName('body')->item(0);
                while ($t->hasChildNodes()) 
                    $docbody->appendChild($t->firstChild);
                
            
        
    

    [...]

另请参阅 Symfony 的创建者 Fabien Potencier 的Parsing XML documents with CSS selectors,了解他决定为 Symfony 创建 CssSelector 组件以及如何使用它。

【讨论】:

【参考方案24】:

Symfony 框架具有可以解析 HTML 的包,您可以使用 CSS 样式选择 DOMs 而不是使用 XPath。

【讨论】:

【参考方案25】:

使用FluidXML,您可以使用XPathCSS 选择器 查询和迭代XML。

$doc = fluidxml('<html>...</html>');

$title = $doc->query('//head/title')[0]->nodeValue;

$doc->query('//body/p', 'div.active', '#bgId')
        ->each(function($i, $node) 
            // $node is a DOMNode.
            $tag   = $node->nodeName;
            $text  = $node->nodeValue;
            $class = $node->getAttribute('class');
        );

https://github.com/servo-php/fluidxml

【讨论】:

【参考方案26】:

三行来自 XML 的 JSON 和数组:

$xml = simplexml_load_string($xml_string);
$json = json_encode($xml);
$array = json_decode($json,TRUE);

哒哒!

【讨论】:

【参考方案27】:

不使用正则表达式解析 HTML 有几个原因。但是,如果您可以完全控制要生成的 HTML,那么您可以使用简单的正则表达式。

上面是一个通过正则表达式解析HTML的函数。请注意,此功能非常敏感,需要 HTML 遵守一定的规则,但在许多场景下都能很好地工作。如果您想要一个简单的解析器,并且不想安装库,请试一试:

function array_combine_($keys, $values) 
    $result = array();
    foreach ($keys as $i => $k) 
        $result[$k][] = $values[$i];
    
    array_walk($result, create_function('&$v', '$v = (count($v) == 1)? array_pop($v): $v;'));

    return $result;


function extract_data($str) 
    return (is_array($str))
        ? array_map('extract_data', $str)
        : ((!preg_match_all('#<([A-Za-z0-9_]*)[^>]*>(.*?)</\1>#s', $str, $matches))
            ? $str
            : array_map(('extract_data'), array_combine_($matches[1], $matches[2])));


print_r(extract_data(file_get_contents("http://www.google.com/")));

【讨论】:

【参考方案28】:

我创建了一个名为 HTML5DOMDocument 的库,可在https://github.com/ivopetkov/html5-dom-document-php免费获得

它也支持查询选择器,我认为这对您的情况非常有帮助。下面是一些示例代码:

$dom = new IvoPetkov\HTML5DOMDocument();
$dom->loadHTML('<!DOCTYPE html><html><body><h1>Hello</h1><div class="content">This is some text</div></body></html>');
echo $dom->querySelector('h1')->innerHTML;

【讨论】:

【参考方案29】:

解析xml的最佳方法:

$xml='http://www.example.com/rss.xml';
$rss = simplexml_load_string($xml);
$i = 0;
foreach ($rss->channel->item as $feedItem) 
  $i++;
  echo $title=$feedItem->title;
  echo '<br>';
  echo $link=$feedItem->link;
  echo '<br>';
  if($feedItem->description !='') 
    $des=$feedItem->description;
   else 
    $des='';
  
  echo $des;
  echo '<br>';
  if($i>5) break;

【讨论】:

【参考方案30】:

如果你熟悉 jQuery 选择器,你可以使用ScarletsQuery for PHP

<pre><?php
include "ScarletsQuery.php";

// Load the HTML content and parse it
$html = file_get_contents('https://www.lipsum.com');
$dom = Scarlets\Library\MarkupLanguage::parseText($html);

// Select meta tag on the HTML header
$description = $dom->selector('head meta[name="description"]')[0];

// Get 'content' attribute value from meta tag
print_r($description->attr('content'));

$description = $dom->selector('#Content p');

// Get element array
print_r($description->view);

这个库处理离线 html 的时间通常不到 1 秒。 它还接受无效的 HTML 或标签属性上缺少引号。

【讨论】:

以上是关于您如何在 PHP 中解析和处理 HTML/XML?的主要内容,如果未能解决你的问题,请参考以下文章

您如何在 PHP 中解析和处理 HTML/XML?

您如何在 PHP 中解析和处理 HTML/XML?

您如何在 PHP 中解析和处理 HTML/XML?

您如何在 PHP 中解析和处理 HTML/XML?

您如何在 PHP 中解析和处理 HTML/XML?

解析和处理 HTML/XML/纯文本页面 [重复]