个人认为, 对于 web 前端程序员和跟 html 和 CSS 打交道的人来说, jQuery 是有史以来最伟大的发明. jQuery 的出现使 Web 程序员的开发效率突飞猛进, 不亚于工业革命给人类生产力带来的提升.
但问题在在于, 只有前端程序员可以利用 jQuery 的强力, 他们可以用它分析 HTML, 根据 CCS 类, HTML 属性, CSS 规则等各种选择器来查询, 获取, 操作 HTML 里的任何一个元素. 而作为后端 (服务端) 程序员来说, 他们同样需要分析 HTML 内容, 从 HTML 中提取符合要求的 HTML 片段, 获取某个符合条件的属性值等.
遇到这种情况, 后端程序员通常的做法就是用正则表达式, 或用 xml 解析器. 这些做法非常的笨拙, 不方便, 效率低下. 所以, 对于在服务器端解析 HTML, 每个后端程序员都极力避免.
我是一个 PHP 程序员, 最近就遇到了这样的一个任务, 需要在服务器端解析 HTML, 将里面的标题名称和链接提取出来. 最初我想开发一个小程序逐行分析 HTML, 捕捉关键字, 或用正则表达式. 但简单分析了一下, 这样做实在不可取. 因为我也是个 Web 程序员, 经常使用 jQuery 解析 HTML 页面上的内容. 如果这个任务放到浏览器端执行, 太简单了, 只需要一句代码: jQuery('.title').each(...);, 如何能在服务器端也能像 jQuery 那样进行 HTML DOM 查询呢?
实际上, 在服务器端有不少具有 jQuery 功能的 PHP 程序库. 在网上稍微做了点功夫, 就搜到了 10 几个声称都能解析 HTML 的 PHP 工具. 但经过试验, 大部分都多少有这样或那样的缺陷, 而且都有一个通病, 就是中文乱码问题. 最终, 我选用了一个叫做 phpQuery http://code.google.com/p/phpquery/ 的工具包.
实际上, 使用 phpQuery http://code.google.com/p/phpquery/ 这个 PHP 程序库也是很不情愿的, 因为这个程序已经很多年没人维护更新了. 但比起其它几个类似功能程序库, 例如 Zend_Dom http://framework.zend.com/manual/en/zend.dom.html ,QueryPath http://querypath.org/ ,SimpleHtmlDom, 它算是好的.
phpQuery http://code.google.com/p/phpquery/ 的接口很丰富, 但很简单. 一个基本的用法是这样的:
- $list = phpQuery::newDocumentFileHTML('http://www.webhek.com')->find('h2.title a');
- foreach($list as $e) {
- $title = $e->nodeValue;
- $url = $e->getAttribute('href');
- }
上面的 find()方法返回的对象是 PHP 官方扩展库中的 DOM http://php.net/manual/en/book.dom.php 对象, 也就是说, phpQuery http://code.google.com/p/phpquery/ 是一个基于 PHP 原生的 DOM http://php.net/manual/en/book.dom.php 对象的 HTML/xml 解析器, 这样做的好处是, 效率很高. 相反, 像 SimpleHtmlDom 这样也是分析 HTML/xml 的程序库, 但没有基于 PHP 原生 DOM http://php.net/manual/en/book.dom.php 对象, 当分析大数据量时, 很容易产生性能问题, 所以不推荐使用.
之前说了, 所有的这样类似 jQuery 的能分析 HTML DOM 的 PHP 程序库都一个相同的通病: 遇到中文会有乱码. 我在使用 phpQuery http://code.google.com/p/phpquery/ 的过程中也遇到了这个问题.
首先 PHP 中的中文本身就是个问题, 而 PHP 的 DOM http://php.net/manual/en/book.dom.php 对象处理中文的方式也是有争议的. 官方文档是说, 这个 DOM 扩展包使用的是 UTF-8 编码, 当遇到 ISO-8859-1 编码的文本时, 使用 utf8_encode() 和 utf8_decode() 编码和解码, 遇到其它编码时, 使用 Iconv 函数进行转码. 但现实情况比这要复杂的多. 网上有很多意见认为在遇到 DOM 乱码时, 在 HTML 代码里的 < title > 标记前加入 < meta charset="utf-8">就行了. 但这种方法有时候也不灵.
我在解决 phpQuery http://code.google.com/p/phpquery/ 的中文乱码问题也是反复尝试才最后搞定的, 没有任何理论依据. 就像是有个程序员的笑话: 这段代码不好用, 我不知道为什么. 这段代码好用, 我也不知道为什么.:(
首先我是在台式机上开发测试的, 是 Window7, 这种环境下会出现两种情况, 一种情况是 HTML 的字符集是 GBK/gb2312, 一种情况是字符集是 UTF-8. 奇怪的是, 两个同样是 gb2312 字符集的不同页面, 用 phpQuery http://code.google.com/p/phpquery/ 解析后, 一个会有乱码, 一个没有乱码. 同样, 两个同样是 UTF-8 字符集的不同页面, 也会出现这种情况. 所有, 对我来说, 没有规律可言. 我只能说, 这两种方法能解决 phpQuery http://code.google.com/p/phpquery/ 使用过程中出现的乱码, 但何时使用哪种? 我不知道, 你只能两个都试一下, 会有一个好用.
所以, 有乱码出现时, 首先使用第一种方案:
- // 仍然使用上面的代码例子:
- $list = phpQuery::newDocumentFileHTML('http://www.webhek.com')->find('h2.title a');
- foreach($list as $e) {
- $title = $e->nodeValue;
- // 进行 GBK 转码
- $title = iconv("UTF-8","GBK", $title);
- }
如果不行, 使用第二种方案:
- // 仍然使用上面的代码例子:
- // 指定 GBK 字符集参数
- $list = phpQuery::newDocumentFileHTML('http://www.webhek.com','GBK')->find('h2.title a');
- foreach($list as $e) {
- $title = $e->nodeValue;
- }
第一种方案中要使用 iconv 函数进行转码, 第二种方案中不需要 iconv 转码, 但需要在 newDocumentFileHTML 方法上提供 "GBK 字符集".
还有一点很重要, 在使用 iconv 函数转码是, 一定要使用 GBK, 而不是使用 gb2312, 如果使用 gb2312,iconv 函数会很容易发生非法字符的报错, 使得转码失败.
我以为有了这两种方案护航后, 乱码问题再不会出现. 可是, 你要知道, 做程序员很容易的心脏病的. 当我把这些代码部署到 Linux 服务器上时, 乱码依旧. 抓狂.
没办法, 程序员的生活就是这样. 经过调试, 发现, 在 Linux 服务器上, 采用第二种方案的部分网页仍然正常, 但使用第一种方案时, 需要去掉 iconv 函数转码.
下辈子一定不要做程序员.
来源: http://www.webhek.com/post/php-jquery.html