7

我正在使用Goutte从 UTF-8 站点上抓取,该站点内部使用 Guzzle。该站点声明了一个 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 中四处搜寻,看看是否有任何东西看起来会强制使用字符集,但无济于事。有任何想法吗?

4

3 回答 3

16

问题实际上在于 symfony/browser-kit 和 symfony/domcrawler。browserkitClient 不会检查 HTML 元标记来确定字符集,内容类型标题。当响应主体移交给 domcrawler 时,它被视为默认字符集 ISO-8859-1。在检查元标记后,应该恢复决定并重建 DomDocument,但这从未发生过。

$crawler->text()简单的解决方法是包装utf8_decode()

$text = utf8_decode($crawler->text());

如果输入是 UTF-8,则此方法有效。我想对于其他编码,类似的东西可以用iconv()左右来实现。但是,您必须记住每次调用时都这样做text()

一种更通用的方法是让 Domcrawler 相信它处理的是 UTF-8。为此,我提出了一个 Guzzle 插件,它覆盖(或添加)内容类型响应标头中的字符集。您可以在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();
于 2013-09-13T19:10:32.250 回答
11

我似乎在这里遇到了两个错误,其中一个是由彼得的回答确定的。另一种是我单独使用 Symfony Crawler 类来探索 HTML 片段的方式。

我正在这样做(解析表格行的 HTML):

$subCrawler = new Crawler($rowHtml);

然而,通过构造函数添加 HTML 似乎并没有提供一种可以指定字符集的方法,我假设 ISO-8859-1 再次成为默认值。

简单地使用addHtmlContent就可以了;第二个参数指定字符集,如果不指定则默认为UTF-8。

$subCrawler = new Crawler();
$subCrawler->addHtmlContent($rowHtml);
于 2013-09-14T13:39:28.103 回答
2

Crawler尝试从<meta charset标签中检测字符集,但它经常丢失,然后Crawler默认使用字符集(ISO-8859-1) - 这是该线程中描述的问题的根源。

当我们Crawler通过构造函数传递内容时,我们错过Content-Type了通常包含字符集的标题。

以下是我们如何处理它:

$crawler = new Crawler();
$crawler->addContent(
    $response->getBody()->getContents(), 
    $response->getHeaderLine('Content-Type')
);

使用此解决方案,我们使用来自服务器响应的正确字符集,并且不将我们的解决方案绑定到任何单个字符集,当然之后我们不需要解码来自Crawler(使用utf8_decode()或以其他方式)接收到的每一行。

于 2017-11-21T11:25:31.237 回答