使用DOMXpath提取LD-Json后出现UTF-8二次编码问题求助
双重UTF-8编码问题:DOMDocument导致的字符乱码根源
这个问题我之前踩过坑!你遇到的其实是双重UTF-8编码问题,核心原因是DOMDocument::loadHTML的默认编码行为在搞鬼:
为什么编码会变?
当你用curl获取到的页面是标准UTF-8编码(比如“ół”的十六进制是C3 B3 C5 82),但DOMDocument::loadHTML默认会把输入内容当作**ISO-8859-1(Latin-1)**来解析。
这就导致了:
- UTF-8的每个字节被错误识别成一个独立的Latin-1字符
- 当你通过
nodeValue提取内容时,这些Latin-1字符又被重新编码成UTF-8 - 最终就出现了双重编码的结果:
C3 83 C2 B3 C3 85 C2 82(相当于把原来的UTF-8字节再转了一次UTF-8)
解决方法:明确告诉DOMDocument使用UTF-8编码
你只需要在加载HTML前,明确指定编码规则,就能解决这个问题。这里有两种可靠的方案:
方案1:使用libxml设置内部编码
直接告诉libxml库用UTF-8解析内容,代码修改如下:
$handle = curl_init(); curl_setopt($handle, CURLOPT_URL, $url); curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); curl_setopt($handle, CURLOPT_FOLLOWLOCATION, true); $page = curl_exec($handle); curl_close($handle); // 关键步骤:设置libxml内部编码为UTF-8 libxml_set_internal_encoding('UTF-8'); $dom = new DOMDocument(); // 用LIBXML_NOERROR替代@符号,更优雅地抑制解析警告 $dom->loadHTML($page, LIBXML_NOERROR | LIBXML_NOWARNING); $xpath = new DOMXpath($dom); $jsonScripts = $xpath->query('//script[@type="application/ld+json"]'); foreach ($jsonScripts as $jScript) { $json = $jScript->nodeValue; // 现在$json是正确的UTF-8编码,json_decode可以正常处理 $data = json_decode($json, true); // 后续业务逻辑... }
方案2:手动添加UTF-8 Meta标签
如果方案1在你的PHP版本中不生效(比如某些旧版libxml),可以在页面内容开头插入一个UTF-8的meta标签,强制DOMDocument识别编码:
$handle = curl_init(); curl_setopt($handle, CURLOPT_URL, $url); curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); curl_setopt($handle, CURLOPT_FOLLOWLOCATION, true); $page = curl_exec($handle); curl_close($handle); // 在页面开头插入UTF-8编码声明 $page = '<meta http-equiv="Content-Type" content="text/html; charset=utf-8">' . $page; $dom = new DOMDocument(); $dom->loadHTML($page, LIBXML_NOERROR | LIBXML_NOWARNING); $xpath = new DOMXpath($dom); $jsonScripts = $xpath->query('//script[@type="application/ld+json"]'); foreach ($jsonScripts as $jScript) { $json = $jScript->nodeValue; $data = json_decode($json, true); // 后续业务逻辑... }
验证效果
修改后,再次提取“ół”字符,它的十六进制应该会回到正确的C3 B3 C5 82,json_decode也能正常解析LD-JSON内容了。
内容的提问来源于stack exchange,提问作者Pepe
相关产品推荐
相关产品推荐

