Может ли Goutte / Guzzle быть переведен в режим UTF-8?

Я соскабливаю с сайта UTF-8, используя Goutte , который внутренне использует 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"; 

Теперь тестовая страница будет размещена на веб-сервере:

 <?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, чтобы увидеть, есть ли что-то похожее на то, что это заставит набор символов, но безрезультатно. Есть идеи?

Related of "Может ли Goutte / Guzzle быть переведен в режим UTF-8?"

Проблема на самом деле связана с symfony / browser-kit и symfony / domcrawler. Client browserkit не проверяет метатеги HTML, чтобы определить только кодировку, только заголовок содержимого. Когда тело ответа передается domcrawler, оно рассматривается как кодировка по умолчанию ISO-8859-1 . Изучив метатеги, решение должно быть возвращено, а DomDocument перестроен, но этого никогда не произойдет.

$crawler->text() является обертка $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(); 

Кажется, у меня были две ошибки, одна из которых была идентифицирована в ответ Питера. Другой способ, которым я отдельно использую класс Symfony Crawler для изучения фрагментов HTML.

Я делал это (для разбора HTML для строки таблицы):

 $subCrawler = new Crawler($rowHtml); 

Тем не менее, добавление HTML через конструктор не указывает способ, которым может быть задан набор символов, и я полагаю, что ISO-8859-1 снова является значением по умолчанию.

Просто используя addHtmlContent это правильно; второй параметр указывает набор символов, и по умолчанию он имеет значение UTF-8, если он не указан.

 $subCrawler = new Crawler(); $subCrawler->addHtmlContent($rowHtml); 

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() или как-то иначе).