wa72/htmlpagedom

基于jQuery的DOM操作扩展,用于Symfony的Crawler

v3.0.2 2023-12-05 20:36 UTC

README

tests Latest Version Downloads from Packagist

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

当你不仅需要从HTML文件中提取信息(DomCrawler所做的),还需要修改HTML页面时,它非常有用。它可以作为一个模板引擎:加载HTML模板文件,在页面标题、div#contentul#menu 等元素上设置新的HTML内容,并打印出修改后的页面。

Wa72\HtmlPageDom 由两个主要类组成

  • HtmlPageCrawler 通过添加如 setInnerHtml($htmltext)before()append()wrap()addClass()css() 等受jQuery启发的、HTML特定的DOM 操作 函数来扩展 Symfony\Components\DomCrawler。它就像PHP中的jQuery:简单地使用CSS选择器选择HTML页面的元素,并更改它们的属性和内容。

    HtmlPageCrawler API文档

  • HtmlPage 代表一个完整的HTML文档,并提供如 getTitle()setTitle($title)setMeta('description', $description)getBody() 等便利函数。内部,它使用 HtmlPageCrawler 类来过滤和操作DOM元素。自1.2版本以来,它还提供了压缩(minify())和美化打印(indent())HTML页面的方法。

    HtmlPage API文档

要求与兼容性

版本3.x

版本2.x

2.x和3.0.x版本之间的API没有区别。唯一的区别是与不同版本的Symfony的兼容性。

安装

  • 使用 composer: composer require wa72/htmlpagedom

  • 使用其他 PSR-4 兼容的自动加载器:将此项目克隆到包含库的位置,并将自动加载器指向在此项目的 "src" 目录中查找 "\Wa72\HtmlPageDom" 命名空间

使用方法

HtmlPageCrawler 是DOMNodes的包装器。可以使用 new 或静态函数 HtmlPageCrawler::create() 创建 HtmlPageCrawler 对象,该函数接受一个HTML字符串或一个DOMNode(或一个DOMNode数组、一个DOMNodeList,甚至是另一个 Crawler 对象)作为参数。

之后,可以通过调用 filter()(相当于jQuery中的find())从添加的DOM树中选择节点,并使用以下jQuery-like操作函数更改选定的元素

  • addClass()hasClass()removeClass()toggleClass()
  • after()before()
  • append()appendTo()
  • makeClone()(相当于jQuery中的 clone()
  • css()(别名 getStyle() / setStyle()
  • html()(获取内部HTML内容)和 setInnerHtml($html)
  • attr()(别名 getAttribute() / setAttribute())、removeAttr()
  • insertAfter()insertBefore()
  • makeEmpty()(与 jQuery 中的 empty() 等价)
  • prepend()prependTo()
  • remove()
  • replaceAll()replaceWith()
  • text()getCombinedText()(获取爬虫中所有节点的文本内容)以及 setText($text)
  • wrap()unwrap()wrapInner()unwrapInner()wrapAll()

要获取修改后的 DOM 作为 HTML 代码,请使用 html()(返回爬虫对象中第一个节点的 innerHTML)或 saveHTML()(返回列表中所有元素的组合“外部”HTML 代码)。

请参阅生成的 HtmlPageCrawler API 文档中的完整方法文档。

示例

use \Wa72\HtmlPageDom\HtmlPageCrawler;

// create an object from a fragment of HTML code as you would do with jQuery's $() function
$c = HtmlPageCrawler::create('<div id="content"><h1>Title</h1></div>');

// the above is the same as calling:
$c = new HtmlPageCrawler('<div id="content"><h1>Title</h1></div>');

// filter for h1 elements and wrap them with an HTML structure
$c->filter('h1')->wrap('<div class="innercontent">');

// return the modified HTML
echo $c->saveHTML();
// or simply:
echo $c; // implicit __toString() calls saveHTML()
// will output: <div id="content"><div class="innercontent"><h1>Title</h1></div></div>

高级示例:从 HTML 表格中删除第三列

use \Wa72\HtmlPageDom\HtmlPageCrawler;
$html = <<<END
<table>
    <tr>
        <td>abc</td>
        <td>adsf</td>
        <td>to be removed</td>
    </tr>
    <tr>
        <td>abc</td>
        <td>adsf</td>
        <td>to be removed</td>
    </tr>
    <tr>
        <td>abc</td>
        <td>adsf</td>
        <td>to be removed</td>
    </tr>
</table>    
END;  

$c = HtmlPageCrawler::create($html);
$tr = $c->filter('table > tr > td')
    ->reduce(
        function ($c, $j) {
            if (($j+1) % 3 == 0) {
                return true;
            }
            return false;
        }
    );
$tr->remove();
echo $c->saveHTML();

HtmlPage 类的使用示例

use \Wa72\HtmlPageDom\HtmlPage;

// create a new HtmlPage object with an empty HTML skeleton
$page = new HtmlPage();

// or create a HtmlPage object from an existing page
$page = new HtmlPage(file_get_contents('http://www.heise.de'));

// get or set page title
echo $page->getTitle();
$page->setTitle('New page title');
echo $page->getTitle();


// add HTML content
$page->filter('body')->setInnerHtml('<div id="#content"><h1>This is the headline</h1><p class="text">This is a paragraph</p></div>');

// select elements by css selector
$h1 = $page->filter('#content h1');
$p = $page->filter('p.text');

// change attributes and content of an element
$h1->addClass('headline')->css('margin-top', '10px')->setInnerHtml('This is the <em>new</em> headline');

$p->removeClass('text')->append('<br>There is more than one line in this paragraph');

// add a new paragraph to div#content
$page->filter('#content')->append('<p>This is a new paragraph.</p>');

// add a class and some attribute to all paragraphs
$page->filter('p')->addClass('newclass')->setAttribute('data-foo', 'bar');


// get HTML content of an element
echo $page->filter('#content')->saveHTML();

// output the whole HTML page
echo $page->save();
// or simply:
echo $page;

// output formatted HTML code
echo $page->indent()->save();

// output compressed (minified) HTML code
echo $page->minify()->save();

请参阅生成的 HtmlPage API 文档

限制

  • HtmlPageDom 建立在 PHP 的 DOM 函数之上,并使用 DOMDocument 类的 loadHTML() 和 saveHTML() 方法。这就是为什么它的输出总是 HTML,而不是 XHTML。

  • PHP 使用的 HTML 解析器是为 HTML4 构建的。它会在 HtmlPageDom 忽略的 HTML5 特定元素上抛出错误,因此 HtmlPageDom 在一些限制下可用于 HTML5。

  • HtmlPageDom 未经测试,除 UTF-8 之外的其他字符编码。

历史

当我发现使用 jQuery 修改 HTML 文档非常容易时,我在寻找一个提供类似功能的 PHP 库。

搜索 Google 时,我发现了 SimpleHtmlDom 和后来的 Ganon,但两者都证明非常慢。尽管如此,我还是在我的项目中使用了这两个库。

当 Symfony2 出现并带有它的 DomCrawler 和 CssSelector 组件时,我想:遍历 DOM 树和通过 CSS 选择器选择元素的函数已经存在了,只有操作函数缺失。让我们来实现它们!因此,HtmlPageDom 项目应运而生。

结果证明,建立在 PHP 的 DOM 函数之上是一个好选择:与 SimpleHtmlDom 和 Ganon 相比,HtmlPageDom 非常快。在我的一个项目中,我有一个 PHP 脚本,它接受一个包含数百个文章元素的巨大 HTML 页面,并将它们提取到单独的 HTML 文件中(稍后通过 AJAX 需求加载回原始 HTML 页面)。使用 SimpleHtmlDom,该脚本需要 3 分钟(是的,分钟!)才能运行(并且我需要将 PHP 的内存限制提高到超过 500MB)。使用 Ganon 作为 HTML 解析和操作引擎,它甚至需要更长的时间,大约 5 分钟。切换到 HtmlPageDom 后,执行相同处理任务的相同脚本只需要大约一秒钟(所有都是在同一台服务器上)。HtmlPageDom 真的很快。

© 2012-2023 Christoph Singer。许可协议为 MIT 许可。