红得发紫的jQuery框架是专门用于页面Javascript程序设计的,它通过一种优雅的方式让我们轻松自如地操作页面的所有元素而无须担心浏览器版本以及兼容性等问题。受到jQuery的启发,一种试图让Web开发者在PHP中直接采用jQuery方式操纵和生成HTML/XML元素的 QueryPath计划开始了,库的发开者是Matt Butcher 。
QueryPath可以很方便地读入和生成HTML/XML,使用jQuery类似的语法和函数遍历文档对象,支持远程URL文件的读取和分析。支持标准的CSS3 Selector和XPath,这意味着我们可以使PHP像jQuery一样,随心所欲地玩转任何HTML!当然PHP5本身就带了不少DOM解析库,QueryPath本身也是在这些库上的二次加工,省下了我们不少的工作量。
QueryPath官方网站:http://querypath.org/
QueryPath使用一个qp()工厂函数,为各种需求生成不同的类,一个最简单的例子如下:
- require ‘QueryPath/QueryPath.php’;
- $html = qp(‘a.html’);
- $html->find(‘title’)->text(‘hello world’);
- $html->find(‘.myInput’)->attr(‘value’, ‘hello world’);
- $html->find(‘body’)->css(‘background-color’, ‘red’);
如果你对jQuery熟悉的话,几乎可以没有任何学习成本就能很快上手。我立刻就使用QueryPath来写了几个测试例程,分析和遍历了一个常去的网站的内容结构,抓取了和分析了不少有用的资料。感觉对于那些做网站采集程序的家伙们来说,QueryPath是不是又要让他们更加如鱼得水(抑或是无恶不作)了,LOL。
使用过程中发现QueryPath库尚处于基本的开发状态,不少问题。但对于程序员来说,遇到问题读一下库的源代码,基本上就能搞定。下面列出两个常用问题的解决,与大家分享。
1.当QueryPath查询到一个节点并操作完成后,它本身并不回到根节点,而是停留在节点上,不知道这样描述正确否,这样导致的结果是下一步的查询将很可能找不到数据。解决的方法是用top()函数回到根节点。如上面的例子可以直接这样用,用top回到根:
- $html->top()->find(‘.myInput’)->attr(‘value’, ‘hello world’);
支持的方法有top(), prev(), next(), child(),不用解释,很好理解。
2.无法读取远程url,或不能识别非"html"的扩展名的html文件。QueryPath直接分析文件和url的扩展名,不为"html"的直接当做xml处理,寒一个。解决的办法是用url的传递参数在最后面伪装一个".html"的参数,告诉QueryPath这是HTML文件。解决方法:
- $qp(‘http://www.acwind.net/index.php?=.html’);//伪装一个后缀名,搞定。
希望开发者能在将来的版本中改进,能自动识别文件类型那该多好。
详解:
PHP 也曾在 Web 开发领域造成轰动。由于易于开发和以 Web 为中心的模型,PHP 使 Web 站点从小小的主页变成像 Yahoo! 这样强大的站点。但是,通过 PHP 来使用这三种技术 — 尤其是 XML — 有时候会比较复杂。在本文中,了解 QueryPath,这是一个 PHP 库,它在设计时考虑了两个目标:
- 简单性,使 HTML、XML 和 HTTP 变得容易使用
- 健壮性,为使用这些技术提供丰富的工具
本文探索如何构建 QueryPath 对象、遍历 XML 和 HTML、操纵 XML 和 HTML 以及使用 QueryPath 访问 Web 服务(使用 Twitter 作为示例服务)。
对于 QueryPath 的典型使用,有四个最重要的概念:
- QueryPath 对象与一个 XML 或 HTML 文档相关联。
- QueryPath 可以查询文档,识别文档中的一组匹配项。
- QueryPath 可以操纵文档。可以添加新的部分,修改已有的部分,删除不想要的部分。
- QueryPath 方法可以链接在一起,在一个简洁的序列中执行很多操作。只需几行代码,就可以装载、解析、查询、修改和写入文档。
清单 1 中的代码展示了所有这些要点。
- <?php
- require ‘QueryPath/QueryPath.php’;
- qp(‘sample.html’)->find(‘title’)->text(‘Hello World’)->writeHTML();
- ?>
以上例子需要一个库,即 QueryPath/QueryPath.php
。除非还要装载 QueryPath 扩展,否者只需包括这个库就可以使用 QueryPath。
![]() |
|
例子中接下来一行代码是一个 QueryPath 链,它做以下事情。
- 创建一个新的
QueryPath
对象,该对象指向 sample.html 文档。当qp()
运行时,它将创建一个新的QueryPath
对象,后者随即装载和解析文档。 - 使用
find()
方法,它使用 CSS 3 选择器title
搜索整个文档,寻找所有<title/>
元素。在一个有效的 HTML 文档中,该搜索只能在文档的头部找到一个匹配的
<title/>
元素。 - 标题的文本值被设为
Hello World
。当执行到这里时,标题的子节点将被 CDATA(字符数据)字符串Hello World
替换。任何已有的内容将被破坏。 - 使用
writeHTML()
方法将整个文档写到标准输出中。
以上例子实际上还可以缩短一点,因为 qp()
工厂函数带有一个 CSS 选择器作为可选的第二个参数。清单 2 显示了缩短后的版本。
|
假设 sample.html
是一个最基本的 HTML 文档,以上代码(清单 1 或清单 2)的结果看上去将如清单 3 所示。加粗的行包含我们设置的标题。
|
这些简单的例子展示了 QueryPath 可以执行的一些常见的任务。接下来几个小节探索一些方法。然后,您将把这些构建块装配起来,创建一个简单的 Web 服务客户机。
QueryPath 库中最常用的函数是 qp()
工厂函数。实际上,它执行创建新的 QueryPath 对象的任务。它被用于传统的构造函数。
如果您熟悉面向对象设计模式,那么可能会意识到 qp()
是工厂模式的一个变种。 QueryPath 不是用构造器方法定义一个工厂类,而是使用一个函数。这种方法除了可以节省键盘输入外(在链接方法时比较重要),还可以使 QueryPath 更贴近 jQuery,减少 jQuery 熟悉者的学习曲线。
一个 QueryPath
对象与一个 XML 或 HTML 文档相关联。当构造 QueryPath
对象时,文档被绑定到该对象。qp()
函数带有 3 个参数,这 3 个参数都是可选的:
- 一个文档
- 可以是一个文件名或 URL、一个 XML 或 HTML 字符串、一个 DOMDocument 或 DOMElement、一个 SimpleXMLElement 或者一个 DOMElement 数组。如果不为该参数提供任何值,QueryPath 将创建一个空白的 XML 文档,供后面进行操纵。
- 一个 CSS3 选择器
- 如果提供了该参数,在装载文档时,QueryPath 将使用给定的选择器查询那个文档。
- 一个关联的选项数组
- 为这个特定 QueryPath 实例提供一种传递一组复杂配置参数的方法。API 参考详细列出了这里可以传递的选项。
![]() |
|
qp()
支持将很多类型的数据作为第一个参数,从而方便构建 QueryPath 对象。QueryPath 可以以一个文件名或 URL 开始,然后装载一个文档。如果传递的是一个 XML 或 HTML 字符串,QueryPath 将解析该内容。当然,它可以接受另外两种常用的 XML 文档的对象表示:DOM 和 SimpleXML。清单 4 展示 qp()
函数如何解析包含 XML 的字符串。
清单 4. 从 XML 字符串构建 QueryPath 对象
|
当清单 4 中的代码运行时, $qp
将引用一个 QueryPath
对象,该对象在内部指向 XML 解析后的表示。前面的例子传入的是一个文件名。如果 PHP 被配置为允许 HTTP/HTTPS 流包装器(在大多数 PHP V5 发行版中是标准配置),那么甚至可以装载远程 HTTP URL,如下所示。
|
这样便可以使用 QueryPath 访问 Web 服务。(可以使用第 3 个参数 qp()
传递流上下文,以便对连接设置进行调整)。当创建新文档时,有一个添加样板 HTML 的快捷方式,如下所示。
清单 6. 使用 QueryPath::HTML_STUB
常量
|
QueryPath::HTML_STUB
常量定义一个基本的 HTML 文档,如下所示。
|
以这个框架文档为基础,可以更快地生成 HTML。
至此,您知道了如何创建新的指向文档的 QueryPath 对象,并且看到了一个简单的 CSS 选择器。下一小节讨论如何使用 QueryPath 遍历文档。
打开文档后,需要在文档中查找感兴趣的内容。QueryPath 的设计使得这一任务变得很容易。为了简化遍历需求,QueryPath 提供了一些用于遍历的方法。大多数方法使用 CSS3 选择器查找所需的节点。
图 1 总结了常用的遍历函数。下面一一描述每个函数。虽然还有一些遍历函数没有提到,但这里覆盖了大多数常见的需求。
方法 | 描述 | 是否带 CSS 选择器 |
---|---|---|
find() |
选择与选择器匹配的任何元素(在当前选择的节点下) | 是 |
xpath() |
选择与给定 XPath 查询匹配的元素 | 否(使用 XPath 查询) |
top() |
选择文档元素(根元素) | 否 |
parents() |
选择任何祖先元素 | 是 |
parent() |
选择直接父元素 | 是 |
siblings() |
选择所有同胞(sibling)元素(包括之前和之后的元素) | 是 |
next() |
选择后一个同胞元素 | 是 |
nextAll() |
选择当前元素之后的所有同胞元素 | 是 |
prev() |
选择前一个同胞元素 | 是 |
prevAll() |
选择当前元素之前的所有同胞元素 | 是 |
children() |
选择当前元素的直接子元素 | 是 |
deepest() |
选择当前元素下最深的节点 | 否 |
QueryPath 中的很多方法可以以查询作为参数,进一步指定应该选择什么项。如表 1 中第三列所示,几乎所有这些方法都带有一个作为可选参数的 CSS3 选择器。(xpath()
函数则带有一个 XPath 查询,而不是 CSS3 选择器)。只有 top()
和 deepest()
不使用查询作为参数。
可以通过另一个简单的例子了解如何进行遍历。假设有一个像下面这样的 XML 文档。
|
<root/>
元素有 4 个子元素:其中有 3 个名为 <child/>
,还有一个名为 <ignore/>
。可以用一个 QueryPath 查询选择 <root/>
的所有 4 个子元素。
|
children()
方法将选择 <root/>
元素的所有直接子元素。最后一行打印 QueryPath
对象中匹配项的数量,最终打印的结果为 4
。
假设只需选择 3 个 <child/>
元素,而不需要选择 <ignore/>
元素。 清单 10 显示了如何实现这一点。
|
最后的 print
语句将打印 QueryPath 当前选择的项的数量。它将返回 3
。在内部,QueryPath 跟踪这 3 个元素。它们被存储为当前上下文。如果执行进一步的查询,那么查询将从这 3 个元素开始。如果试图附加数据,那么数据将被附加到这 3 个元素后。
![]() |
|
CSS 选择器是 CSS 语句的一部分,用于选择将应用某种样式的元素。CSS 选择器还可以在样式表上下文之外使用。QueryPath 使用选择器作为查询语言,并支持 CSS3 选择器 标准 中描述的特性集。
CSS 选择器在 QueryPath 中扮演很重要的角色。您已经看到,有 10 个函数使用 CSS 选择器作为参数。到目前为止使用的选择器是简单的标记名查询。CSS3 选择器要比前面的例子强大得多。对 CSS3 选择器的详细描述超出了本文的范围,但表 2 提供了一些常见的选择器模式的例子。
选择器模式 | 描述 | 示例匹配项 |
---|---|---|
p |
找到标记名为 <p/> 的元素 |
<p> |
.container |
找到 class 属性被设为 container 的元素 |
<div class="container"/> |
#menu |
找到 id 属性被设为 menu 的元素。基于 ID 的搜索以这种方式进行 |
<div id="menu"/> |
[type="inline"] |
找到 type 属性的值为 inline 的元素 |
<code type="inline"/> |
tr > th |
找到直接父元素为 <tr> 的 <th> 元素 |
<tr><th/></tr> |
table td |
找到祖先(例如父亲或祖父)中有 <table> 元素的 <td> 元素 |
<table><tr><td/></tr></table> |
li:first |
获取第一个名为 <li/> 的元素。支持的伪类包括 :last 、 :even 和 :odd |
<li/> |
RDF|seq |
找到 <RDF:seq> 元素。 QueryPath 包括用于 XML 名称空间的 CSS3 选择器。名称空间支持延伸到属性和元素 |
<RDF:seq> |
这些常见的选择器模式可以加以组合,形成复杂的选择器,例如
|
。 这个选择器将搜索 class 为 content
的任何 <div/>
。在 div
中,它将搜索所有无序列表(<ul>
),返回每个列表的第一个列表项(<li>
)。
您了解了遍历文档的两个方面:QueryPath 提供的方法和 CSS3 选择器支持。第三个方面是迭代选择的项。
QueryPath 对象是可遍历的(traversable) 。在 PHP 中,这意味着对象可以当做迭代器。标准的 PHP 循环结构可以遍历 QueryPath 对象选择的元素。还记得吗,清单 10 中的例子是一个简单的查询,它从一个 XML 文档中检索 3 个元素。接下来的例子将以这个例子为基础。
如果要单独处理每个项,应该怎么办?很容易,因为 QueryPath 可以用作迭代器。清单 11 显示了一个例子。
|
当 foreach
循环迭代时,它将每个匹配项赋给 $child
变量。但是,$child
不是真正的元素,它是指向当前元素的一个 QueryPath
对象。您可以任意使用所有常见的 QueryPath 方法。
为了使 API 与 jQuery 的 API 类似, QueryPath 提供一些可同时作为 accessor 和 mutator — 或 getter 和 setter 的方法。取决于参数,同一个方法可以检索(access)数据,或者更改(mutate)数据。 attr()
函数就是一个例子。
qp()->attr('name')
检索 name
属性的值。 qp()->attr('name', 'value')
将 name
属性的值设为 value
。还有一些方法,包括 text()
、html()
和 xml()
,作为 accessor 和 mutator 同时执行两种任务。
由于每个迭代的项包装在一个 QueryPath 对象中,所以可以通过 $child
任意使用所有标准的 QueryPath 方法。上面的例子使用了 attr()
函数,这是一个元素中的属性的 accessor 和 mutator。
attr()
方法检索名为 id
的属性的值。下面显示以上代码的输出。
|
您已经了解了如何使用 QueryPath 方法、CSS3 选择器和迭代技术遍历文档。下一节探索如何用 QueryPath 修改文档。
除了使用 QueryPath 搜索文档外,还可以使用它添加、修改和移除文档中的数据。在清单 1 中可以大致了解 QueryPath 的功能。为了方便,下面再重复一遍。
|
在这个例子中,text()
函数用于修改 <title/>
元素的内容。QueryPath 提供了十几个用于更改文档的方法。图 2 展示一些常用的修改方法如何工作。这些方法都是添加或替换数据。绿色的标记表示当前被选中的元素。
每个方法以字符串数据(通常是以 HTML 或 XML 片段的形式)作为参数,并将数据插入到文档中。随后立即可以访问和进一步操纵新插入的数据。
实际
评论