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('http://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('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/php-jquery/