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


当前回答

有很多方法:

一般来说:

本机XML扩展:它们与PHP捆绑在一起,通常比所有第三方库都快,并为我提供了对标记所需的所有控制。DOM:DOM能够解析和修改真实世界(损坏的)HTML,并且可以执行XPath查询。它基于libxml。XMLReader:XMLReader与DOM一样,基于libxml。XMLReader扩展是一个XML拉式解析器。读取器充当文档流上前进的光标,并在途中的每个节点处停止XML解析器:此扩展允许您创建XML解析器,然后为不同的XML事件定义处理程序。每个XML解析器也有一些可以调整的参数。它实现了SAX风格的XML推送解析器。简单XML:SimpleXML扩展提供了一个非常简单且易于使用的工具集,用于将XML转换为可以使用普通属性选择器和数组迭代器处理的对象。

第三方库[libxml-based]:

FluentDom-Repo:FluentDom为PHP中的DOMDocument提供了一个类似jQuery的fluent XML接口。它可以加载JSON、CSV、JsonML、RabbitFish等格式。可以通过Composer安装。HtmlPageDom:是一个PHP库,使用它可以方便地操纵HTML文档。它需要Symfony2组件的DomCrawler来遍历DOM树,并通过添加操纵HTML文档的DOM树的方法来扩展它。ZendDOM:Zend_Dom提供了处理Dom文档和结构的工具。目前,他们提供了Zend_Dom_Query,它为使用XPath和CSS选择器查询Dom文档提供了统一的接口。QueryPath:QueryPath是一个用于处理XML和HTML的PHP库。它不仅设计用于本地文件,还设计用于web服务和数据库资源。它实现了大部分jQuery接口(包括CSS样式选择器),但它针对服务器端使用进行了大量调整。可以通过Composer安装。fDOM文档:fDOMDocument扩展了标准DOM,以在所有错误情况下使用异常,而不是PHP警告或通知。为了方便和简化DOM的使用,他们还添加了各种自定义方法和快捷方式。Sabre/XML:ssabre/XML是一个库,它包装并扩展XMLReader和XMLWriter类,以创建一个简单的“XML到对象/数组”映射系统和设计模式。编写和读取XML是一次性的,因此速度很快,对大型XML文件的内存要求很低。FluidXML:FluidXML是一个PHP库,用于使用简洁流畅的API处理XML。它利用XPath和流畅的编程模式,使其变得有趣和有效。

第三方库[不基于libxml]:

PHP简单HTML DOM解析器:一个用PHP5+编写的HTML DOM解析器允许您以非常简单的方式操作HTML,它需要PHP 5+。还支持无效的HTML。它在单行中从HTML中提取内容。代码库很糟糕,而且工作速度很慢。PHPHtmlParser:HPHtmlParser是一个简单、灵活的Html解析器,允许您使用任何CSS选择器(如jQuery)选择标记。目标是帮助开发需要快速、简单的方式来抓取HTML的工具,无论它是否有效。它速度慢,占用了太多CPU电源。Ganon(推荐):通用标记器和HTML/XML/RSS DOM解析器。它具有操纵元素及其属性的能力。它支持无效的HTML和UTF8。它可以对元素执行类似CSS3的高级查询(如jQuery——支持名称空间)。HTML美化器(如HTML Tidy)。精简CSS和Javascript。它对属性进行排序、更改字符大小写、正确缩进等。可扩展。操作分为较小的功能,便于覆盖和快速且易于使用。

Web服务:

如果您不想编写PHP,也可以使用Web服务。ScraperWiki的外部界面允许您以您希望在web或自己的应用程序中使用的形式提取数据。您还可以提取有关任何刮刀状态的信息。

我已经分享了所有的资源,你可以根据自己的口味、用途等进行选择。

其他回答

对于HTML5,html5lib已经被放弃多年了。我能找到的唯一一个最近更新和维护记录的HTML5库是一周多前刚刚发布到beta 1.0的HTML5 php。

使用FluidXML,您可以使用XPath和CSS选择器查询和迭代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

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

我创建了一个名为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;

处理HTML/XML DOM的方法有很多,其中大多数已经提到。因此,我不会亲自列出这些。

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

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

虽然我怀念为DOMDocument使用CSS选择器的能力,但有一种非常简单和方便的方法可以添加此功能:将DOMDocument子类化,并将类似于querySelectorAll和querySelector的JS方法添加到子类中。

为了解析选择器,我建议使用Symfony框架中的非常简约的CsSelector组件。该组件只是将CSS选择器转换为XPath选择器,然后可以将其输入到DOMX路径中以检索相应的Nodelist。

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

下面的代码直接来自我的DOM查询库,并使用了我描述的技术。

对于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关于为Symfony创建CsSelector组件的决定以及如何使用它的CSS选择器解析XML文档。