phpQuery乱码的终极解决方案

phpQuery是一款JQuery的PHP实现,用来解析网页元素DOM非常的方便,头疼的是他总是有乱码问题。其实也不能全怪phpQuery。
因为phpQuery分析网页元素时候进行网页编码探测使用的是正则表达式进行页面meta标签的charset匹配。但是总是有那么多奇形怪状的网页,所以也难免会出错。
比如这个页面 他就没有meta标签,phpQuery在处理这个页面的时候(function loadMarkupHTML),就会遵循如下流程

[text]if(正则表达式匹配meta标签失败,即documentCharset变量空){
if(定义了requestedCharset,就是那个newDocumentFileHTML后面的charset参数){
charset = requestedCharset
}
if(charset还不能被定义,即requestedCharset为空){
charset = phpQuery::$defaultCharset
}
if(documentCharset变量空){
根据HTTP 1.1的要求 设置默认documentCharset为ISO-8859-1
需要在后面补足标准的meta标记
}
}
if (requestedCharset && documentCharset && requestedCharset !== documentCharset){
如果存在mb_detect_encoding的话,进行编码转换
possibleCharsets = array(documentCharset, requestedCharset, 'AUTO')
docEncoding = mb_detect_encoding(markup, implode(', ', possibleCharsets));
if(检测不出docEncoding){
已documentCharset为准
}
if(docEncoding这个检测编码和requestedCharset不一样){
使用mb_convert_encoding进行编码转换
}

}
其余略[/text]

这里面有个问题,那就是mb_detect_encoding检测的编码不一定是准确的,大部分情况下,在ISO-8859-1优先的情况下(documentCharset排在前面),他都认为该文档是ISO-8859-1,从而出现乱码。
所以,如果你这里定义了phpQuery::$defaultCharset,就可以避免这种情况的发生,这样的话 采集淘宝网首页 只要指定phpQuery::$defaultCharset=GBK,就不会乱码。
第二个问题是一个BUG,在phpQuery的大概273行(phpQuery 0.9.5 (r386; one file release)

[php]$markup = $this->charsetFixHTML($markup);[/php]

这个BUG会导致中文wordpress博客,比如我这个博客,虽然有规范的meta标签 但是会被错误的charsetFixHTML。破坏了DOM结构,导致解析乱码。该BUG到Revision 393依旧没修复。 这个不注释的话,采集我的博客就会乱码.

所以 这里有我的一个修补版本,有兴趣的可以下了试试。[download id="32" format="2"]
由于PHPQuery是精确采集,所以你之前基本知道了网页的编码,那么只要指定了phpQuery::$defaultCharset,不管是有没有meta的页面或者meta不规范的页面,基本上都不会乱码。

规范的HTML页面演示

[php]
<?php
//规范meta演示
header("Content-type: text/html; charset=utf-8");
set_time_limit(0);
include 'phpQuery-onefile-ipatched.php';
//phpQuery::$defaultCharset = 'euc-jp';
phpQuery::newDocumentFileHTML('http://page14.auctions.yahoo.co.jp/jp/auction/s237346475');
echo pq('title')->text();
echo pq("strong[property='auction:Price'])")->text();
?>[/php]

不规范的页面演示

[php]
<?php
//不规范meta演示
header("Content-type: text/html; charset=utf-8");
set_time_limit(0);
include 'phpQuery-onefile-ipatched.php';
phpQuery::$defaultCharset = 'euc-jp';
phpQuery::newDocumentFileHTML('http://storeuser11.auctions.yahoo.co.jp/jp/user/makototakahashi0316?alocale=0jp&mode=0&u=makototakahashi0316');
$trs = pq('#list01')->find('table tr:not(:first)');
foreach($trs as $tr){
$line = pq($tr)->find('td:first a');
$title = $line->text();
if($title){
$url = $line->attr('href');
echo $title.'---->'.$url.'<br />';
}

}

?>
[/php]


[php]<?php
//规范meta演示
header("Content-type: text/html; charset=utf-8");
set_time_limit(0);
include 'phpQuery-onefile-ipatched.php';
//phpQuery::$defaultCharset = 'euc-jp';
phpQuery::newDocumentFileHTML('http://ihipop.info');
echo pq("title")->text();
//不规范meta演示
phpQuery::$defaultCharset = 'GBK';
phpQuery::newDocumentFileHTML('http://taobao.com');
echo pq("title")->text();
?>[/php]

注意 1.phpQuery的输出全部是UTF-8编码的.
2.phpQuery主页http://code.google.com/p/phpquery/

phpQuery在每处理一个网页就会产生一个DOMDocumentWrapper 对象,而每个DOMDocumentWrapper 对象会被保存在静态成员$documents中(phpQuery::createDocumentWrapper中),这个变量是一个数组,每解析一个网页数组元素就增加一个。如果不想让自己的内存被额沾满,每次解析完一个网页,把phpQuery::$documents置空
[php]phpQuery::$documents = array();[/php]或者清空不必要的ID,节省内存。同理,你也可以有选择的保留一些ID 见http://code.google.com/p/phpquery/wiki/MultiDocumentSupport

Author Info :
  • From:phpQuery乱码的终极解决方案
  • URL:https://blog.ihipop.com/2011/08/2647.html
  • Please Reserve This Link,Thanks!
  • 《phpQuery乱码的终极解决方案》上有7条评论

    1. http://www.fhzww.net 用你的版本采这个站的title是乱码

      $callback=array('demo2_cb1',array('http://www.fhzww.net'));
      $curl->add('http://www.fhzww.net'),$callback);

      $curl->go();

      function demo2_cb1($r,$id){
      global $db,$curl;
      //print_r($r['info']);exit;
      if($r['info']['http_code']==200){
      phpQuery::$defaultCharset = 'utf-8';
      $html=phpQuery::newDocumentHTML($r['content'],'utf-8');

      $list=$html['title'];

      echo $list->text();exit;

      }
      phpQuery::unloadDocuments();
      $curl->status();
      }

      ?>

    发表回复

    您的电子邮箱地址不会被公开。 必填项已用*标注