我正在使用内部使用Guzzle的Goutte来搜索UTF-8网站。该网站声明了UTF-8的元标记,因此:
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
但是,内容类型标题是:
Content-Type: text/html
而不是:
Content-Type: text/html; charset=utf-8
因此,当我刮擦时,Goutte没有发现它是UTF-8,并且不正确地抓取数据。远程站点不在我的控制之下,所以我无法解决问题!这是一组用于复制问题的脚本。首先,刮刀:
<?php
require_once realpath(__DIR__ . '/..') . '/vendor/goutte/goutte.phar';
$url = 'http://crawler-tests.local/utf-8.php';
use Goutte\Client;
$client = new Client();
$crawler = $client->request('get', $url);
$text = $crawler->text();
echo 'Whole page: ' . $text . "\n";
现在要放置在Web服务器上的测试页面:
<?php
// Correct
#header('Content-Type: text/html; charset=utf-8');
// Incorrect
header('Content-Type: text/html');
?>
<!DOCTYPE html>
<html>
<head>
<title>UTF-8 test</title>
<meta charset="utf-8" />
</head>
<body>
<p>When the Content-Header header is incomplete, the pound sign breaks:
£15,216</p>
</body>
</html>
以下是Goutte测试的输出:
整页:UTF-8测试 当Content-Header标头不完整时,英镑符号会中断:£15,216
正如您在上一个脚本中的注释中所看到的,正确声明标题中的字符集可以修复问题。我在Goutte周围寻找,看看是否有任何东西看起来会迫使字符集,但无济于事。有什么想法吗?
答案 0 :(得分:15)
问题实际上是symfony / browser-kit和symfony / domcrawler。 browserkit的Client
does not examine the HTML meta tags仅用于确定charset,content-type标头。当响应正文移交给domcrawler时,treated为the default charset ISO-8859-1。在检查了元标记后,应该恢复决策并重建DomDocument,但这种情况从未发生过。
简单的解决方法是使用$crawler->text()
包裹utf8_decode()
:
$text = utf8_decode($crawler->text());
如果输入为UTF-8,则此方法有效。我认为对于其他编码,iconv()
左右可以实现类似的功能。但是,每次拨打text()
时都必须记住这一点。
更通用的方法是让Domcrawler相信它处理UTF-8。为此,我想出了一个Guzzle插件,它在内容类型响应头中覆盖(或添加)charset。您可以在https://gist.github.com/pschultz/6554265找到它。用法是这样的:
<?php
use Goutte\Client;
$plugin = new ForceCharsetPlugin();
$plugin->setForcedCharset('utf-8');
$client = new Client();
$client->getClient()->addSubscriber($plugin);
$crawler = $client->request('get', $url);
echo $crawler->text();
答案 1 :(得分:11)
我似乎在这里遇到了两个错误,其中一个是彼得的回答。另一种是我分别使用Symfony Crawler类来探索HTML片段的方式。
我这样做(解析表格行的HTML):
$subCrawler = new Crawler($rowHtml);
然而,通过构造函数添加HTML似乎没有提供可以指定字符集的方法,我认为ISO-8859-1再次是默认值。
只需使用addHtmlContent
就可以了;第二个参数指定字符集,如果未指定,则默认为UTF-8。
$subCrawler = new Crawler();
$subCrawler->addHtmlContent($rowHtml);
答案 2 :(得分:2)
Crawler
尝试检测来自<meta charset
标记的字符集但经常丢失,然后Crawler
默认使用字符集(ISO-8859-1) - 它是问题的根源在这个帖子中描述。
当我们通过构造函数将内容传递给Crawler
时,我们会错过通常包含charset的Content-Type
标头。
以下是我们如何处理它:
$crawler = new Crawler();
$crawler->addContent(
$response->getBody()->getContents(),
$response->getHeaderLine('Content-Type')
);
使用此解决方案,我们使用来自服务器响应的正确字符集,并且不会将我们的解决方案绑定到任何单个字符集,当然之后我们不需要解码来自Crawler
的每一条接收线路(使用utf8_decode()
或其他方式)。