PHP DOMDocument loadHTML неправильно кодирует UTF-8

195

Я пытаюсь разобрать некоторый HTML с помощью DOMDocument, но когда я это делаю, я внезапно теряю свою кодировку (по крайней мере, так мне кажется).

$profile = "<div><p>various japanese characters</p></div>";
$dom = new DOMDocument();
$dom->loadHTML($profile); 

$divs = $dom->getElementsByTagName('div');

foreach ($divs as $div) {
    echo $dom->saveHTML($div);
}

Результатом этого кода является то, что я получаю набор символов, которые не являются японскими. Однако, если я сделаю:

echo $profile;

он отображается правильно. Я пробовал saveHTML и saveXML, и ни один из них не отображается правильно. Я использую PHP 5.3.

Что я вижу:

ã¤ãªãã¤å·ã·ã«ã´ã«ã¦ãã¢ã¤ã«ã©ã³ãç³»ã®å®¶åº­ã«ã9人åå¼ã®5çªç®ã¨ãã¦çã¾ãããå½¼ãå«ãã¦4人ã俳åªã«ãªã£ããç¶è¦ªã¯æ¨æã®ã»ã¼ã«ã¹ãã³ã§ãæ¯è¦ªã¯éµä¾¿å±ã®å®¢å®¤ä¿ã ã£ããé«æ ¡æ代ã¯ã­ã£ãã£ã®ã¢ã«ãã¤ãã«å¤ãã¿ãæè²è³éãåããªããã«ããªãã¯ç³»ã®é«æ ¡ã¸é²å­¦ã

Что должно быть показано:

イリノイ州シカゴにて、アイルランド系の家庭に、9人兄弟の5番目として生まれる。彼を含めて4人が俳優になった。父親は木材のセールスマンで、母親は郵便局の客室係だった。高校時代はキャディのアルバイトに勤しみ、教育資金を受けながらカトリック系の高校へ進学

РЕДАКТИРОВАТЬ: я упростил код до пяти строк, чтобы вы могли проверить его самостоятельно.

$profile = "<div lang=ja><p>イリノイ州シカゴにて、アイルランド系の家庭に、</p></div>";
$dom = new DOMDocument();
$dom->loadHTML($profile);
echo $dom->saveHTML();
echo $profile;

Вот HTML, который возвращается:

<div lang="ja"><p>イリノイ州シカゴã«ã¦ã€ã‚¢ã‚¤ãƒ«ãƒ©ãƒ³ãƒ‰ç³»ã®å®¶åº­ã«ã€</p></div>
<div lang="ja"><p>イリノイ州シカゴにて、アイルランド系の家庭に、</p></div>
Слегка А.
источник
Это может помочь вам. stackoverflow.com/questions/1580543/…
frustratedtech
Спасибо. Я проверил все это и ничего не помогло. Я не получаю ????, но какой-то другой странный текст. Я постараюсь вставить это здесь, но не знаю, как сайт будет отображать это.
Слегка А.
Попробуйте использовать utf8_encode
Webnet
Пробовал безуспешно. Возвращены те же символы, что и раньше.
Слегка А.

Ответы:

517

DOMDocument::loadHTMLбудет относиться к вашей строке как к ISO-8859-1, если вы не укажете иначе. Это приводит к тому, что строки UTF-8 интерпретируются неправильно.

Если ваша строка не содержит объявления кодировки XML, вы можете добавить его, чтобы строка была обработана как UTF-8:

$profile = '<p>イリノイ州シカゴにて、アイルランド系の家庭に、9</p>';
$dom = new DOMDocument();
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $profile);
echo $dom->saveHTML();

Если вы не можете знать, будет ли строка уже содержать такое объявление, в SmartDOMDocument есть обходной путь, который должен вам помочь:

$profile = '<p>イリノイ州シカゴにて、アイルランド系の家庭に、9</p>';
$dom = new DOMDocument();
$dom->loadHTML(mb_convert_encoding($profile, 'HTML-ENTITIES', 'UTF-8'));
echo $dom->saveHTML();

Это не лучший обходной путь, но, поскольку не все символы могут быть представлены в ISO-8859-1 (как эти катаны), это самая безопасная альтернатива.

cmbuckley
источник
1
Да, это сделал. Спасибо за помощь. Я пробовал saveHTML, saveXML, но не думал, что проблема могла возникнуть во время загрузки.
Слегка А.
4
Вызов mb_convert_encoding сработал для меня, в то время как предварительное объявление кодировки не сработало. Скорее всего, потому что документ уже имел противоречивую декларацию. Большое спасибо - сэкономил мне много времени в погоне за этим.
Питер Багналл
1
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $content);исправил это для меня в PHP7 (так что это все еще проблема) - это действительно раздражающая проблема, потому что я определил utf8 в документе HTML (с <meta charset="UTF-8" />), но это не имеет никакого эффекта, похоже, нужна часть <? xml, которая совершенно не интуитивно.
Икито
11
Еще в 2017 году этот ответ актуален и работал на меня тоже. У меня была база данных, многобайтовая мета-метка html и кодировка DOM, все они были настроены на utf8, но при импорте узла из одного DOC в другой все еще была плохая кодировка php.net/manual/en/function.mb-convert-encoding.php было исправлением.
Луи Лоудог Троттье
6
$dom->loadHTML(mb_convert_encoding($profile, 'HTML-ENTITIES', 'UTF-8'));прекрасно работает! Спасибо,
ви
67

Проблема с, saveHTML()и saveXML()оба они не работают правильно в Unix. Они не сохраняют символы UTF-8 правильно при использовании в Unix, но работают в Windows.

Обходной путь очень прост:

Если вы попробуете по умолчанию, вы получите ошибку, которую вы описали

$str = $dom->saveHTML(); // saves incorrectly

Все, что вам нужно сделать, это сохранить следующим образом:

$str = $dom->saveHTML($dom->documentElement); // saves correctly

Эта строка кода поможет вам правильно сохранить символы UTF-8. Используйте тот же обходной путь, если вы используете saveXML().


Обновить

Как предложено « Джеком М » в разделе комментариев ниже, и проверено « Памелой» » и « Марко Аурелио Делё », в вашем случае может подойти следующая вариация:

$str = utf8_decode($dom->saveHTML($dom->documentElement));

Заметка

  1. Английские символы не вызывают проблем при использовании saveHTML()без параметров (поскольку английские символы сохраняются как однобайтовые символы в UTF-8)

  2. Проблема возникает, когда у вас есть многобайтовые символы (например, китайский, русский, арабский, иврит и т. Д.)

Я рекомендую прочитать эту статью: http://coding.smashingmagazine.com/2012/06/06/all-about-unicode-utf8-character-sets/ . Вы поймете, как работает UTF-8 и почему у вас такая проблема. Это займет у вас около 30 минут, но это хорошо проведенное время.

Greeso
источник
5
Я должен был utf8_decode при использовании этого решения. Спасибо!
Джек М.
9
Это должно было стать utf8_decode ($ dom-> saveHTML (dom-> documentElement)), чтобы сохранить мои специальные символы. Иначе они просто стали чем-то другим. Просто упомяну это на случай, если это поможет кому-то еще.
Джек М.
4
Спасибо @MrJack. Мне также пришлось сделать то же самое, чтобы оно отображалось без странных символов$str = utf8_decode($dom->saveHTML($dom->documentElement));
Памела
1
utf8_decode($dom->saveHTML($dom->documentElement));сделал это идеально для меня.
Марко Аурелио Делё
2
Вы спасли мою жизнь с этим. Я искал этот ответ ВЕЗДЕ! Спасибо!
Пауло Хго,
15

Убедитесь, что реальный исходный файл сохранен как UTF-8 (Вы можете даже попробовать не рекомендуемые символы спецификации с UTF-8, чтобы убедиться).

Также в случае HTML, убедитесь, что вы объявили правильную кодировку, используя metaтеги:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

Если это CMS (как вы отметили свой вопрос в Joomla), вам может потребоваться настроить соответствующие параметры для кодировки.

Хоссейн
источник
Я понимаю, что вы говорите, но у меня нет проблем с отображением персонажей. если я сделаю "echo $ profile;" все работает отлично это когда DomDocument завладевает этим, он начинает терпеть неудачу.
Слегка А.
2
Ваша мета не позволяет saveHTML кодировать все, что находится выше ASCII, в сущности. Решение, которое я искал :)
sod
2
Как примечание, новый <meta charset="UTF-8">тег не работает с DOMDocument.
Тайлан
10

Вы можете добавить префикс для utf-8кодирования строки следующим образом:

@$doc->loadHTML('<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $profile);

И затем вы можете продолжить с кодом, который у вас уже есть, например:

$doc->saveXML()
Иван
источник
10

Мне потребовалось некоторое время, чтобы понять, но вот мой ответ.

Перед использованием DomDocument я использовал бы file_get_contents для получения URL-адресов, а затем обрабатывал их строковыми функциями. Возможно, не лучшим способом, но быстрым. Убедившись, что Дом так же быстр, я сначала попробовал следующее:

$dom = new DomDocument('1.0', 'UTF-8');
if ($dom->loadHTMLFile($url) == false) { // read the url
    // error message
}
else {
    // process
}

Это не помогло сохранить кодировку UTF-8, несмотря на правильные метатеги, настройки php и все остальные средства защиты, предлагаемые здесь и в других местах. Вот что работает:

$dom = new DomDocument('1.0', 'UTF-8');
$str = file_get_contents($url);
if ($dom->loadHTML(mb_convert_encoding($str, 'HTML-ENTITIES', 'UTF-8')) == false) {
}

и т.д. Теперь все в порядке с миром. Надеюсь это поможет.

Сэм
источник
Просто хотел добавить к моему ответу выше, что другой способ решения этой проблемы заключается в следующем, предложенном также в другом месте: if ($ dom-> loadHTML ('<? Xml encoding = "UTF-8">'. $ Str) = = ложь) После публикации моего ответа я нашел случай, когда мое первое предложение провалилось, но второе сработало.
Сэм
У меня работает даже без параметров DomDocument('1.0', 'UTF-8'). Но в моем случае загружается только частичный html.
JKB
5

Вы должны предоставить DOMDocument версию вашего HTML-кода с заголовком, который имеет смысл. Так же, как HTML5.

$profile ='<?xml version="1.0" encoding="'.$_encoding.'"?>'. $html;

Может быть, это хорошая идея, чтобы сохранить ваш HTML настолько действительным, насколько это возможно, чтобы вы не столкнулись с проблемами при запуске запроса ... вокруг :-) и держитесь подальше от htmlentities!!!! Это необходимая трата ресурсов назад и вперед. держи свой код безумным !!!!

Лазарос Космидис
источник
5

Я использую php 7.3.8 на manjaro, и я работал с персидским контентом. Это решило мою проблему:

$html = 'hi</b><p>سلام<div>の家庭に、9 ☆';
$doc = new DOMDocument('1.0', 'UTF-8');
$doc->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
print $doc->saveHTML($doc->documentElement) . PHP_EOL . PHP_EOL;
Сайед Зарринпур
источник
Точно такой же совет дал Сэм несколько лет назад на этой же странице. Пожалуйста, не размещайте избыточную информацию.
mickmackusa
4

Работает финде для меня:

$dom = new \DOMDocument;
$dom->loadHTML(utf8_decode($html));
...
return  utf8_encode( $dom->saveHTML());
главное метеорологическое управление
источник
2
Будьте осторожны, utf8_decode может потерять информацию (заменено на a ?)
jwal
2

Используйте это для правильного результата

$dom = new DOMDocument();
$dom->loadHTML('<meta http-equiv="Content-Type" content="text/html; charset=utf-8">' . $profile);
echo $dom->saveHTML();
echo $profile;

Эта операция

mb_convert_encoding($profile, 'HTML-ENTITIES', 'UTF-8');

Это плохо, потому что специальные символы, такие как & lt; & gt; может быть в профиле $, и они не будут преобразованы дважды после mb_convert_encoding. Это дыра для XSS и неправильного HTML.

Александр Гончаров
источник
1

Единственное, что сработало для меня, это принятый ответ

$profile = '<p>イリノイ州シカゴにて、アイルランド系の家庭に、9</p>';
$dom = new DOMDocument();
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $profile);
echo $dom->saveHTML();

ТЕМ НЕ МЕНИЕ

Это вызвало новые проблемы, имеющие <?xml encoding="utf-8" ?> с выводом документа.

Решение для меня было тогда сделать

foreach ($doc->childNodes as $xx) {
    if ($xx instanceof \DOMProcessingInstruction) {
        $xx->parentNode->removeChild($xx);
    }
}

Некоторые решения говорили мне, что для удаления xmlзаголовка, что я должен был выполнить

$dom->saveXML($dom->documentElement);

Это не сработало для меня, как для частичного документа (например, документа с двумя <p>тегами), только один из <p>тегов был возвращен.

Люк Мадханга
источник
0

Можно также кодировать, как показано ниже .... собрано из https://davidwalsh.name/domdocument-utf8-problem

$profile = '<p>イリノイ州シカゴにて、アイルランド系の家庭に、9</p>';
$dom = new DOMDocument();
$dom->loadHTML(mb_convert_encoding($profile, 'HTML-ENTITIES', 'UTF-8'));
echo $dom->saveHTML();
Анбараси Сельварадж
источник
0

Это сработало для меня.

В php.ini файле измените следующее свойство.

Перед:

mbstring.encoding_transration = On

После:

mbstring.encoding_transration = Off
Питер
источник
0

Проблема в том, что когда вы добавляете параметр в DOMDocument::saveHTML()функцию, вы теряете кодировку. В некоторых случаях вам нужно избегать использования параметра и использовать старую строковую функцию, чтобы найти то, что вы ищете.

Я думаю, что предыдущий ответ работает для вас, но так как этот обходной путь не работает для меня, я добавляю этот ответ, чтобы помочь людям, которые могут быть в моем случае.

copndz
источник