<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Fwolf's Blog &#187; ContentType - Fwolf's Blog</title>
	<atom:link href="http://www.fwolf.com/blog/post/tag/contenttype/feed" rel="self" type="application/rss+xml" />
	<link>http://www.fwolf.com/blog</link>
	<description>随心·随意·随缘·努力～</description>
	<lastBuildDate>Wed, 07 Jul 2010 07:07:52 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>DOMDocument-&gt;loadHTML()处理中文的一点问题</title>
		<link>http://www.fwolf.com/blog/post/314</link>
		<comments>http://www.fwolf.com/blog/post/314#comments</comments>
		<pubDate>Sun, 08 Apr 2007 08:46:39 +0000</pubDate>
		<dc:creator>Fwolf</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[charset]]></category>
		<category><![CDATA[ContentType]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[encoding]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[meta]]></category>

		<guid isPermaLink="false">http://www.fwolf.com/blog/post/314</guid>
		<description><![CDATA[DOM是php比较新的xml和html处理类，可以像javascript那样方便的操作DOM树，网上更多的是介绍它处理XML的情况，今天我来介绍一个用它处理html时的中文问题，php版本为5.1.6，所有php代码均为utf8编码。 我要处理的html是使用curl从网页上读取过来的，一个是百度的首页，gb2312字符集，一个是有道的首页，utf8字符集，两者的html头部分分别如下： &#60;html&#62;&#60;head&#62;&#60;title&#62;百度一下，你就知道 &#60;/title&#62;&#60;meta http-equiv=Content-Type content="text/html;charset=gb2312"&#62; &#60;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&#62; &#60;html xmlns="http://www.w3.org/1999/xhtml"&#62; &#60;head&#62; &#60;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&#62; &#60;link rel="stylesheet" href="/pack23501M/index.css" type="text/css"/&#62; &#60;script type="text/javascript" src="/pack23501M/all.js"&#62;&#60;/script&#62; &#60;title&#62;有道&#60;/title&#62; 可以看出百度的代码非常不规范，而有道就好多了，这虽然是题外话，其实还是有些关系的，后面会提到。 以上两段html代码，用同样的方式处理结果却不同，比如下面简单的处理（输出网页的title）： $dom = new DOMDocument(); @$dom-&#62;loadHTML($html); echo $dom-&#62;getElementsByTagName('title')-&#62;item(0)-&#62;nodeValue; ... $html = $dom-&#62;saveHTML(); 有道的输出结果是正常的，百度却是乱码： ç¾åº¦ä¸ä¸ï¼ä½ å°±ç¥é 由于php文档的loadHTML上说了，DOM内部处理全部都是utf8的，所以除了传入内容要utf8化之外，传入的内容中最好还有声明字符集的html代码： &#60;meta http-equiv="Content-Type" content="text/html; charset=utf-8"/&#62; 注意，这就是DOM处理html和xml最大的不同了，xml一般要求在第一行就显示的声明字符集，而html则灵活得多，可声明可不声明。不过不管输出的内容是正常还是乱码，dom内的nodeValue和最终的输出结果都是一致的，说明dom工作正常，问题就在输入数据上。 于是，针对百度的gb2312网页内容，增加了两项处理，第一项是使用mb_convert_encoding把网页内容由gb2312编码转换为utf8编码，第二项是把html中的： &#60;meta http-equiv=Content-Type content="text/html;charset=gb2312"&#62; [...]]]></description>
			<content:encoded><![CDATA[<p>DOM是php比较新的xml和html处理类，可以像javascript那样方便的操作DOM树，网上更多的是介绍它处理XML的情况，今天我来介绍一个用它处理html时的中文问题，php版本为5.1.6，所有php代码均为utf8编码。</p>

<p>我要处理的html是使用curl从网页上读取过来的，一个是百度的首页，gb2312字符集，一个是有道的首页，utf8字符集，两者的html头部分分别如下：</p>

<pre><code>&lt;html&gt;&lt;head&gt;&lt;title&gt;百度一下，你就知道   &lt;/title&gt;&lt;meta http-equiv=Content-Type content="text/html;charset=gb2312"&gt;

&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml"&gt;
&lt;head&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&gt;
&lt;link rel="stylesheet" href="/pack23501M/index.css" type="text/css"/&gt;
&lt;script type="text/javascript" src="/pack23501M/all.js"&gt;&lt;/script&gt;
&lt;title&gt;有道&lt;/title&gt;
</code></pre>

<p>可以看出百度的代码非常不规范，而有道就好多了，这虽然是题外话，其实还是有些关系的，后面会提到。</p>

<p>以上两段html代码，用同样的方式处理结果却不同，比如下面简单的处理（输出网页的title）：</p>

<pre><code>$dom = new DOMDocument();
@$dom-&gt;loadHTML($html);
echo $dom-&gt;getElementsByTagName('title')-&gt;item(0)-&gt;nodeValue;
...
$html = $dom-&gt;saveHTML();
</code></pre>

<p>有道的输出结果是正常的，百度却是乱码：</p>

<pre><code>ç¾åº¦ä¸ä¸ï¼ä½ å°±ç¥é
</code></pre>

<p>由于<a href="http://us3.php.net/manual/en/function.dom-domdocument-loadhtml.php">php文档的loadHTML</a>上说了，DOM内部处理全部都是utf8的，所以除了传入内容要utf8化之外，传入的内容中最好还有声明字符集的html代码：</p>

<pre><code>&lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"/&gt;
</code></pre>

<p>注意，这就是DOM处理html和xml最大的不同了，xml一般要求在第一行就显示的声明字符集，而html则灵活得多，可声明可不声明。不过不管输出的内容是正常还是乱码，dom内的nodeValue和最终的输出结果都是一致的，说明dom工作正常，问题就在输入数据上。</p>

<p>于是，针对百度的gb2312网页内容，增加了两项处理，第一项是使用<code>mb_convert_encoding</code>把网页内容由gb2312编码转换为utf8编码，第二项是把html中的：</p>

<pre><code>&lt;meta http-equiv=Content-Type content="text/html;charset=gb2312"&gt;
</code></pre>

<p>替换成了utf8的：</p>

<pre><code>&lt;meta http-equiv="Content-Type" content="text/html;charset=utf-8"&gt;
</code></pre>

<p>这样按说应该是可以了，但百度的处理结果仍然是乱码，百思而不得其解，偶然当中发现如果$html的值是这样的话输出是正常的：</p>

<pre><code>$html = mb_convert_encoding('&lt;title&gt;测试test&lt;/title&gt;', 'gb2312', 'utf-8');
$html = '&lt;meta http-equiv="Content-Type" content="text/html;charset=gb2312"&gt;' . $this-&gt;html;
</code></pre>

<p>这说明，DOM正确识别了html代码中的Content-Type描述，即使html是gb2312编码的，DOM也能夠自动转换为正确的代码。</p>

<p>现在的情况是这样的：</p>

<ul>
<li>DOM工作正常</li>
<li>html已经转换为utf8编码</li>
<li>Content-Type描述也已经调整</li>
</ul>

<p>怎么还是会出问题呢？先看看下面的Content-Type描述代码：</p>

<pre><code>$meta = '&lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"&gt;';
</code></pre>

<p>看清楚喽，如果用<code>$meta</code>直接替换百度html代码中的那句meta，不会生效，仍然乱码；可如果把<code>$meta</code>添加到整个html代码前面，也就是<code>&lt;html&gt;</code>前面，输出就正常了，神奇吧。</p>

<p>于是我就推测，之前百度代码处理乱码的原因，可能是在它的html代码中，meta前面有个含有中文的<code>&lt;title&gt;</code>，DOM在解析到<code>&lt;title&gt;</code>的时候，遇到了非ascii字符，而这时没有解析到<code>&lt;meta&gt;</code>，DOM不知道整个html代码是什么字符集，也就无法正确判断<code>&lt;title&gt;</code>的编码，于是糊里糊涂的进行了错误的字符集转换。</p>

<p>为了证实我的猜测，试着这样处理一下：只修改<code>&lt;meta&gt;</code>，把定义位置放在<code>&lt;title&gt;</code>前面，把缺少的引号加上，但是字符集声明仍然为gb2312，html代码也不进行iconv转换，就像下面这样（注意为gb2312编码）：</p>

<pre><code>&lt;html&gt;
&lt;head&gt;
&lt;meta http-equiv="Content-Type" content="text/html;charset=gb2312"&gt;
&lt;title&gt;百度一下，你就知道   &lt;/title&gt;
</code></pre>

<p>执行，输出正常，而且是正常的gb2312编码，没有乱码。所以我的猜测是正确的，关于Content-Type的meta声明一定要放在<code>&lt;title&gt;</code>前面才行。另外上例中如果把nodeValue输出，是utf8编码的，也就是DOM的内部使用编码，说明DOM输入和输出的时候都会进行字符集转换（根据html代码中的字符集声明）。</p>

<p>最后，总结一下，curl读过来的网页数据，全部iconv为utf8编码，然后把声明Content-Type的<code>&lt;meta&gt;</code>替换到紧跟在<code>&lt;head&gt;</code>的位置上，再用DOM处理就不会出现乱码了。</p>

	Tags: <a href="http://www.fwolf.com/blog/post/tag/charset" title="charset" rel="tag">charset</a>, <a href="http://www.fwolf.com/blog/post/tag/contenttype" title="ContentType" rel="tag">ContentType</a>, <a href="http://www.fwolf.com/blog/post/tag/dom" title="DOM" rel="tag">DOM</a>, <a href="http://www.fwolf.com/blog/post/tag/encoding" title="encoding" rel="tag">encoding</a>, <a href="http://www.fwolf.com/blog/post/tag/html" title="html" rel="tag">html</a>, <a href="http://www.fwolf.com/blog/post/tag/meta" title="meta" rel="tag">meta</a>, <a href="http://www.fwolf.com/blog/post/tag/php" title="PHP" rel="tag">PHP</a><br />

	<h4>Related posts</h4>
	<ul class="st-related-posts">
	<li><a href="http://www.fwolf.com/blog/post/300" title="针对$_SERVER['PHP_SELF']的跨站脚本攻击（XSS） (2007-03-18)">针对$_SERVER['PHP_SELF']的跨站脚本攻击（XSS）</a> (3)</li>
	<li><a href="http://www.fwolf.com/blog/post/443" title="配置安全的共享web服务器（抛砖引玉） (2009-06-09)">配置安全的共享web服务器（抛砖引玉）</a> (4)</li>
	<li><a href="http://www.fwolf.com/blog/post/400" title="选择Eclipse PHP Development Tools(PDT)作为PHP开发工具 (2008-05-03)">选择Eclipse PHP Development Tools(PDT)作为PHP开发工具</a> (8)</li>
	<li><a href="http://www.fwolf.com/blog/post/176" title="让phpmailer支持中文名称的附件 (2006-05-23)">让phpmailer支持中文名称的附件</a> (5)</li>
	<li><a href="http://www.fwolf.com/blog/post/155" title="终于能够通过phpmailer使用gmail账号发送邮件了 (2006-04-14)">终于能够通过phpmailer使用gmail账号发送邮件了</a> (50)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://www.fwolf.com/blog/post/314/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>
