PHP DOMDokumentasi loadHTML tidak mengkodekan UTF-8 dengan benar

194

Saya mencoba mem-parsing beberapa HTML menggunakan DOMDocument, tetapi ketika saya melakukannya, tiba-tiba saya kehilangan penyandian saya (setidaknya begitulah yang terlihat oleh saya).

$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);
}

Hasil dari kode ini adalah saya mendapatkan banyak karakter yang bukan bahasa Jepang. Namun, jika saya lakukan:

echo $profile;

ini ditampilkan dengan benar. Saya sudah mencoba saveHTML dan saveXML, dan keduanya tidak ditampilkan dengan benar. Saya menggunakan PHP 5.3.

Apa yang saya lihat:

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

Apa yang harus ditampilkan:

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

EDIT: Saya telah menyederhanakan kode menjadi lima baris sehingga Anda dapat mengujinya sendiri.

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

Berikut adalah html yang dikembalikan:

<div lang="ja"><p>イリノイ州シカゴã«ã¦ã€ã‚¢ã‚¤ãƒ«ãƒ©ãƒ³ãƒ‰ç³»ã®å®¶åº­ã«ã€</p></div>
<div lang="ja"><p>イリノイ州シカゴにて、アイルランド系の家庭に、</p></div>
Sedikit A.
sumber
Ini dapat membantu Anda. stackoverflow.com/questions/1580543/…
frustratedtech
Terima kasih. Saya memeriksa semua itu dan tidak ada yang membantu. Saya tidak mendapatkan ????, tetapi beberapa teks aneh lainnya. Saya akan mencoba menempelkannya di sini, tetapi tidak tahu bagaimana situs akan menampilkannya.
Slightly A.
Coba gunakan utf8_encode
Webnet
Mencoba tanpa hasil. Mengembalikan karakter yang sama seperti sebelumnya.
Slightly A.

Jawaban:

513

DOMDocument::loadHTMLakan memperlakukan string Anda sebagai ISO-8859-1 kecuali Anda memberi tahu sebaliknya. Ini menghasilkan string UTF-8 ditafsirkan secara salah.

Jika string Anda tidak mengandung deklarasi penyandian XML, Anda dapat menambahkannya untuk menyebabkan string diperlakukan sebagai UTF-8:

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

Jika Anda tidak dapat mengetahui apakah string tersebut sudah berisi pernyataan seperti itu, ada solusi di SmartDOMDocument yang akan membantu Anda:

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

Ini bukan solusi yang hebat, tetapi karena tidak semua karakter dapat diwakili dalam ISO-8859-1 (seperti katana ini), itu adalah alternatif paling aman.

cmbuckley
sumber
1
Ya, itu berhasil. Terima kasih untuk bantuannya. Saya mencoba saveHTML, saveXML, tidak berpikir bahwa masalahnya mungkin telah muncul selama pemuatan.
Slightly A.
4
Panggilan mb_convert_encoding bekerja untuk saya, sedangkan mendahului pernyataan encoding tidak. Kemungkinan karena dokumen tersebut sudah memiliki deklarasi yang bertentangan. Terima kasih banyak - menyelamatkan saya banyak waktu mengejar ini.
Peter Bagnall
1
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $content);memperbaikinya untuk saya di PHP7 (jadi masih masalah) - ini adalah masalah yang sangat menjengkelkan, karena saya mendefinisikan utf8 dalam dokumen HTML (with <meta charset="UTF-8" />) tapi itu tidak berpengaruh, sepertinya memerlukan bagian <? xml, yang sama sekali tidak intuitif.
iquito
11
Masih pada tahun 2017 jawaban ini relevan dan bekerja untuk saya juga. Saya memiliki basis data, multibyte, html meta tag, dan DOM encoding saya set ke utf8 dan masih memiliki encoding buruk pada mengimpor node dari satu DOC ke yang lain. php.net/manual/en/function.mb-convert-encoding.php adalah perbaikannya.
Louis Loudog Trottier
6
$dom->loadHTML(mb_convert_encoding($profile, 'HTML-ENTITIES', 'UTF-8'));bekerja hebat! Terima kasih,
vee
66

Masalahnya adalah dengan saveHTML()dan saveXML(), keduanya tidak berfungsi dengan benar di Unix. Mereka tidak menyimpan karakter UTF-8 dengan benar ketika digunakan di Unix, tetapi mereka bekerja di Windows.

Solusinya sangat sederhana:

Jika Anda mencoba default, Anda akan mendapatkan kesalahan yang Anda jelaskan

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

Yang harus Anda lakukan adalah menyimpan sebagai berikut:

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

Baris kode ini akan membuat karakter UTF-8 Anda disimpan dengan benar. Gunakan solusi yang sama jika Anda menggunakan saveXML().


Memperbarui

Seperti yang disarankan oleh " Jack M " di bagian komentar di bawah, dan diverifikasi oleh " Pamela " dan " Marco Aurélio Deleu ", variasi berikut mungkin berfungsi dalam kasus Anda:

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

Catatan

  1. Karakter bahasa Inggris tidak menyebabkan masalah ketika Anda menggunakan saveHTML()tanpa parameter (karena karakter bahasa Inggris disimpan sebagai karakter byte tunggal di UTF-8)

  2. Masalahnya terjadi ketika Anda memiliki karakter multi-byte (seperti Cina, Rusia, Arab, Ibrani, ... dll.)

Saya sarankan membaca artikel ini: http://coding.smashingmagazine.com/2012/06/06/all-about-unicode-utf8-character-sets/ . Anda akan memahami cara kerja UTF-8 dan mengapa Anda memiliki masalah ini. Ini akan memakan waktu sekitar 30 menit, tetapi menghabiskan waktu dengan baik.

Greeso
sumber
5
Saya harus utf8_decode saat menggunakan solusi ini. Terima kasih!
Jack M.
9
Ini harus menjadi utf8_decode ($ dom-> saveHTML (dom-> documentElement)) untuk mempertahankan karakter khusus saya. Kalau tidak, mereka hanya menjadi sesuatu yang lain. Sebut saja kalau-kalau itu membantu orang lain.
Jack M.
4
Terima kasih @ MrJack. Saya juga harus melakukan hal yang sama untuk membuatnya ditampilkan tanpa karakter aneh$str = utf8_decode($dom->saveHTML($dom->documentElement));
Pamela
1
utf8_decode($dom->saveHTML($dom->documentElement));melakukannya dengan sempurna untuk saya.
Marco Aurélio Deleu
2
Anda menyelamatkan hidup saya dengan ini. Saya mencari jawaban ini DI MANA SAJA! Terima kasih!
Paulo Hgo
15

Pastikan file sumber asli disimpan sebagai UTF-8 (Anda bahkan mungkin ingin mencoba BOM Chars yang tidak direkomendasikan dengan UTF-8 untuk memastikan).

Juga dalam hal HTML, pastikan Anda telah menyatakan pengkodean yang benar menggunakan metatag:

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

Jika itu adalah CMS (karena Anda telah menandai pertanyaan Anda dengan Joomla), Anda mungkin perlu mengonfigurasi pengaturan yang sesuai untuk penyandian.

Hossein
sumber
Saya mengerti apa yang Anda katakan, tetapi saya tidak memiliki masalah dalam menampilkan karakter. jika saya melakukan "echo $ profile;" ini bekerja dengan baik. itu ketika DomDocument dikuasai bahwa itu mulai gagal.
Slightly A.
2
Meta Anda mencegah saveHTML dari menyandikan semua yang di atas ASCII menjadi entitas. Solusi yang saya cari :)
sod
2
Sebagai catatan tambahan, <meta charset="UTF-8">tag yang lebih baru tidak berfungsi dengan DOMDocument.
Taylan
10

Anda bisa mengawali utf-8pengkodean garis penegakan , seperti ini:

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

Dan Anda kemudian dapat melanjutkan dengan kode yang sudah Anda miliki, seperti:

$doc->saveXML()
Ivan
sumber
10

Saya perlu beberapa saat untuk mencari tahu tapi inilah jawaban saya.

Sebelum menggunakan DomDocument saya akan menggunakan file_get_contents untuk mengambil url dan kemudian memprosesnya dengan fungsi string. Mungkin bukan cara terbaik tapi cepat. Setelah diyakinkan Dom sama cepatnya, saya pertama kali mencoba yang berikut:

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

Ini gagal secara spektakuler dalam mempertahankan pengkodean UTF-8 meskipun ada meta tag, pengaturan php dan semua solusi yang ditawarkan di sini dan di tempat lain. Inilah yang berhasil:

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

dll. Sekarang semuanya benar dengan dunia. Semoga ini membantu.

Sam
sumber
Hanya ingin menambahkan jawaban saya di atas bahwa cara lain untuk mengatasi ini adalah dengan yang berikut, yang disarankan di tempat lain juga: if ($ dom-> loadHTML ('<? Xml encoding = "UTF-8">'. $ Str) = = false). Setelah memposting jawaban saya, saya menemukan kesempatan di mana saran pertama saya gagal tetapi yang kedua berhasil.
Sam
Bekerja untuk saya bahkan tanpa params di DomDocument('1.0', 'UTF-8'). Tetapi dalam kasus saya hanya sebagian html yang dimuat.
JKB
5

Anda harus memberi makan DOMDocument versi HTML Anda dengan header yang masuk akal. Sama seperti HTML5.

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

mungkin ide yang bagus untuk menjaga agar html Anda tetap valid, sehingga Anda tidak akan mendapat masalah ketika Anda akan memulai permintaan ... sekitar :-) dan menjauhlah dari htmlentities!!!! Itu bolak-balik sumber daya yang diperlukan. pertahankan kode Anda tetap gila !!!!

Lazaros Kosmidis
sumber
5

Saya menggunakan php 7.3.8 di manjaro dan saya bekerja dengan konten Persia. Ini menyelesaikan masalah saya:

$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;
sajed zarrinpour
sumber
Nasihat yang sama persis ini diberikan oleh Sam bertahun-tahun sebelumnya di halaman yang sama. Tolong jangan posting informasi yang berlebihan.
mickmackusa
4

Bekerja untuk saya:

$dom = new \DOMDocument;
$dom->loadHTML(utf8_decode($html));
...
return  utf8_encode( $dom->saveHTML());
mO
sumber
2
Hati-hati, utf8_decode dapat kehilangan informasi (diganti dengan a ?)
jwal
2

Gunakan untuk hasil yang benar

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

Operasi ini

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

Ini cara yang buruk, karena simbol khusus seperti & lt; , & gt; bisa dalam $ profil, dan mereka tidak akan mengonversi dua kali setelah mb_convert_encoding. Ini adalah lubang untuk XSS dan HTML salah.

Alexander Goncharov
sumber
1

Satu-satunya hal yang berhasil bagi saya adalah jawaban yang diterima

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

NAMUN

Ini membawa masalah baru, memiliki <?xml encoding="utf-8" ?>dalam keluaran dokumen.

Solusi bagi saya adalah kemudian melakukannya

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

Beberapa solusi mengatakan kepada saya bahwa untuk menghapus xmlheader, saya harus melakukan

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

Bagi saya ini tidak berfungsi sebagai dokumen parsial (mis. Dokumen dengan dua <p>tag), hanya satu dari <p>tag yang dikembalikan.

Luke Madhanga
sumber
0

Masalahnya adalah ketika Anda menambahkan parameter ke fungsi DOMDocument :: saveHTML (), Anda kehilangan enkode. Dalam beberapa kasus, Anda harus menghindari penggunaan parameter dan menggunakan fungsi string lama untuk menemukan apa yang Anda cari.

Saya pikir jawaban sebelumnya bekerja untuk Anda, tetapi karena solusi ini tidak berhasil untuk saya, saya menambahkan jawaban itu untuk membantu ppl yang mungkin ada dalam kasus saya.

copndz
sumber
0

Dapat juga menyandikan seperti di bawah ini .... dikumpulkan dari 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();
Anbarasi Selvaraj
sumber