DOMDocument->loadHTML()处理中文的一点问题
授权方式:署名,非商业用途,保持一致,转载时请务必以超链接(http://www.fwolf.com/blog/post/314)的形式标明文章原始出处和作者信息及本声明。DOM是php比较新的xml和html处理类,可以像javascript那样方便的操作DOM树,网上更多的是介绍它处理XML的情况,今天我来介绍一个用它处理html时的中文问题,php版本为5.1.6,所有php代码均为utf8编码。
我要处理的html是使用curl从网页上读取过来的,一个是百度的首页,gb2312字符集,一个是有道的首页,utf8字符集,两者的html头部分分别如下:
<html><head><title>百度一下,你就知道 </title><meta http-equiv=Content-Type content="text/html;charset=gb2312">
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="/pack23501M/index.css" type="text/css"/>
<script type="text/javascript" src="/pack23501M/all.js"></script>
<title>有道</title>
可以看出百度的代码非常不规范,而有道就好多了,这虽然是体外话,其实还是有些关系的,后天会提到。
以上两段html代码,用同样的方式处理结果却不同,比如下面简单的处理(输出网页的title):
$dom = new DOMDocument();
@$dom->loadHTML($html);
echo $dom->getElementsByTagName('title')->item(0)->nodeValue;
...
$html = $dom->saveHTML();
有道的输出结果是正常的,百度却是乱码:
ç¾åº¦ä¸ä¸ï¼ä½ å°±ç¥é
由于php文档的loadHTML上说了,DOM内部处理全部都是utf8的,所以除了传入内容要utf8化之外,传入的内容中最好还有声明字符集的html代码:
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
注意,这就是DOM处理html和xml最大的不同了,xml一般要求在第一行就显示的声明字符集,而html则灵活得多,可声明可不声明。不过不管输出的内容是正常还是乱码,dom内的nodeValue和最终的输出结果都是一致的,说明dom工作正常,问题就在输入数据上。
于是,针对百度的gb2312网页内容,增加了两项处理,第一项是使用mb_convert_encoding把网页内容由gb2312编码转换为utf8编码,第二项是把html中的:
<meta http-equiv=Content-Type content="text/html;charset=gb2312">
替换成了utf8的:
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
这样按说应该是可以了,但百度的处理结果仍然是乱码,百思而不得其解,偶然当中发现如果$html的值是这样的话输出是正常的:
$html = mb_convert_encoding('<title>测试test</title>', 'gb2312', 'utf-8');
$html = '<meta http-equiv="Content-Type" content="text/html;charset=gb2312">' . $this->html;
这说明,DOM正确识别了html代码中的Content-Type描述,即使html是gb2312编码的,DOM也能夠自动转换为正确的代码。
现在的情况是这样的:
- DOM工作正常
- html已经转换为utf8编码
- Content-Type描述也已经调整
怎么还是会出问题呢?先看看下面的Content-Type描述代码:
$meta = '<meta http-equiv="Content-Type" content="text/html; charset=utf-8">';
看清楚喽,如果用$meta直接替换百度html代码中的那句meta,不会生效,仍然乱码;可如果把$meta添加到整个html代码前面,也就是<html>前面,输出就正常了,神奇吧。
于是我就推测,之前百度代码处理乱码的原因,可能是在它的html代码中,meta前面有个含有中文的<title>,DOM在解析到<title>的时候,遇到了非ascii字符,而这时没有解析到<meta>,DOM不知道整个html代码是什么字符集,也就无法正确判断<title>的编码,于是糊里糊涂的进行了错误的字符集转换。
为了证实我的猜测,试着这样处理一下:只修改<meta>,把定义位置放在<title>前面,把缺少的引号加上,但是字符集声明仍然为gb2312,html代码也不进行iconv转换,就像下面这样(注意为gb2312编码):
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=gb2312">
<title>百度一下,你就知道 </title>
执行,输出正常,而且是正常的gb2312编码,没有乱码。所以我的猜测是正确的,关于Content-Type的meta声明一定要放在<title>前面才行。另外上例中如果把nodeValue输出,是utf8编码的,也就是DOM的内部使用编码,说明DOM输入和输出的时候都会进行字符集转换(根据html代码中的字符集声明)。
最后,总结一下,curl读过来的网页数据,全部iconv为utf8编码,然后把声明Content-Type的<meta>替换到紧跟在<head>的位置上,再用DOM处理就不会出现乱码了。

Save to Browser Favorites
Ask
backflip
blinklist
BlogBookmark
Bloglines
BlogMarks
Blogsvine
BUMPzee!
CiteULike
co.mments
Connotea
del.icio.us
DotNetKicks
Digg
diigo
dropjack.com
dzone
Facebook
Fark
Faves
Feed Me Links
Friendsite
folkd.com
Furl
Google
Hugg
Jeqq
Kaboodle
linkaGoGo
LinksMarker
Ma.gnolia
Mister Wong
Mixx
MySpace
MyWeb
Netvouz
Newsvine
PlugIM
popcurrent
Propeller
Reddit
Rojo
Segnalo
Shoutwire
Simpy
sk*rt
Slashdot
Sphere
Sphinn
Spurl.net
Squidoo
StumbleUpon
Technorati
ThisNext
Webride
Windows Live
Yahoo!
Email This to a Friend
If you like this then please subscribe to the
April 15th, 2007 at 14:30:28
百度不识别UTF8编码,有的时候直接现实乱码.
[Reply]