PHP版的jQuery
个人认为,对于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的工具包。
实际上,使用phpQuery这个PHP程序库也是很不情愿的,因为这个程序已经很多年没人维护更新了。但比起其它几个类似功能程序库,例如Zend_Dom、QueryPath、SimpleHtmlDom,它算是好的。
phpQuery的接口很丰富,但很简单。一个基本的用法是这样的:
$list = phpQuery::newDocumentFileHTML('https://www.webhek.com')->find('h2.title a'); foreach($list as $e) { $title = $e->nodeValue; $url = $e->getAttribute('href'); }
上面的find()
方法返回的对象是PHP官方扩展库中的DOM对象,也就是说,phpQuery是一个基于PHP原生的DOM对象的HTML/XML解析器,这样做的好处是,效率很高。相反,像SimpleHtmlDom这样也是分析HTML/XML的程序库,但没有基于PHP原生DOM对象,当分析大数据量时,很容易产生性能问题,所以不推荐使用。
之前说了,所有的这样类似jQuery的能分析HTML DOM的PHP程序库都一个相同的通病:遇到中文会有乱码。我在使用phpQuery的过程中也遇到了这个问题。
首先PHP中的中文本身就是个问题,而PHP的DOM对象处理中文的方式也是有争议的。官方文档是说,这个DOM扩展包使用的是UTF-8编码,当遇到 ISO-8859-1 编码的文本时,使用 utf8_encode() 和 utf8_decode() 编码和解码,遇到其它编码时,使用Iconv函数进行转码。但现实情况比这要复杂的多。网上有很多意见认为在遇到DOM乱码时,在HTML代码里的<title>
标记前加入<meta charset="utf-8">
就行了。但这种方法有时候也不灵。
我在解决phpQuery的中文乱码问题也是反复尝试才最后搞定的,没有任何理论依据。就像是有个程序员的笑话:这段代码不好用,我不知道为什么。这段代码好用,我也不知道为什么。:(
首先我是在台式机上开发测试的,是Window7,这种环境下会出现两种情况,一种情况是HTML的字符集是GBK/gb2312,一种情况是字符集是UTF-8。奇怪的是,两个同样是gb2312字符集的不同页面,用phpQuery解析后,一个会有乱码,一个没有乱码。同样,两个同样是UTF-8字符集的不同页面,也会出现这种情况。所有,对我来说,没有规律可言。我只能说,这两种方法能解决phpQuery使用过程中出现的乱码,但何时使用哪种?我不知道,你只能两个都试一下,会有一个好用。
所以,有乱码出现时,首先使用第一种方案:
//仍然使用上面的代码例子: $list = phpQuery::newDocumentFileHTML('https://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('https://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函数转码。
下辈子一定不要做程序员。
乱码问题开始也很折磨我,后来我发现,在把获取的内容进行处理前,先进行utf8处理,然后把页面里的meta name=charset这个标签过滤掉再处理就正常了,有时候你内容转码成utf8,但是内容里还带了meta name=charset这个标签,他好像还是会傻傻的按这个标签去转码,多此一举。你可以试试看,反正我现在一上来就暴力的去掉这个标签,再也没有遇到乱码问题 🙂
叼!