PHP DOMDocument loadHTML non codifica correttamente UTF-8


195

Sto cercando di analizzare un po 'di HTML usando DOMDocument, ma quando lo faccio, improvvisamente perdo la mia codifica (almeno così mi sembra).

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

Il risultato di questo codice è che ottengo un sacco di caratteri che non sono giapponesi. Tuttavia, se lo faccio:

echo $profile;

viene visualizzato correttamente. Ho provato saveHTML e saveXML e nessuno dei due viene visualizzato correttamente. Sto usando PHP 5.3.

Quello che vedo:

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

Cosa dovrebbe essere mostrato:

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

EDIT: ho semplificato il codice fino a cinque righe in modo da poterlo testare tu stesso.

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

Ecco il codice HTML che viene restituito:

<div lang="ja"><p>イリノイ州シカゴã«ã¦ã€ã‚¢ã‚¤ãƒ«ãƒ©ãƒ³ãƒ‰ç³»ã®å®¶åº­ã«ã€</p></div>
<div lang="ja"><p>イリノイ州シカゴにて、アイルランド系の家庭に、</p></div>


Grazie. Ho controllato tutti quelli e niente mi ha aiutato. Non capisco ????, ma qualche altro strano testo. Proverò a incollarlo qui, ma non so come verrà visualizzato dal sito.
Leggermente A.

Prova a utilizzare utf8_encode
Webnet il

Ho provato senza successo. Restituisce gli stessi personaggi di prima.
Leggermente A.

Risposte:


516

DOMDocument::loadHTMLtratterà la tua stringa come in ISO-8859-1 a meno che tu non lo dica diversamente. Ciò comporta l'interpretazione errata delle stringhe UTF-8.

Se la tua stringa non contiene una dichiarazione di codifica XML, puoi anteporre una per far sì che la stringa venga trattata come UTF-8:

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

Se non riesci a sapere se la stringa conterrà già una tale dichiarazione, c'è una soluzione alternativa in SmartDOMDocument che dovrebbe aiutarti:

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

Questa non è una grande soluzione, ma poiché non tutti i personaggi possono essere rappresentati in ISO-8859-1 (come questi katana), è l'alternativa più sicura.


1
Sì, lo ha fatto. Grazie per l'aiuto. Ho provato saveHTML, saveXML, non pensavo che il problema potesse essersi verificato durante il caricamento.
Leggermente A.

4
La chiamata mb_convert_encoding ha funzionato per me, mentre anteporre la dichiarazione di codifica non ha funzionato. Probabilmente perché il documento aveva già una dichiarazione contrastante. Molte grazie - mi ha risparmiato un sacco di tempo inseguendo questo.
Peter Bagnall,

1
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $content);risolto per me in PHP7 (quindi è ancora un problema) - questo è un problema davvero fastidioso, perché ho definito utf8 nel documento HTML (con <meta charset="UTF-8" />) ma che non ha alcun effetto, sembra che abbia bisogno della parte <? xml, che è totalmente non intuitivo.
iquito

11
Sempre nel 2017 questa risposta è pertinente e ha funzionato anche per me. Avevo il mio database, multibyte, metatag html e codifica DOM tutti impostati su utf8 e avevo ancora una codifica errata sull'importazione del nodo da un DOC all'altro. php.net/manual/en/function.mb-convert-encoding.php era la soluzione.
Louis Loudog Trottier,

6
$dom->loadHTML(mb_convert_encoding($profile, 'HTML-ENTITIES', 'UTF-8'));funziona alla grande! Grazie,
vee

67

Il problema è con saveHTML()e saveXML(), entrambi non funzionano correttamente in Unix. Non salvano correttamente i caratteri UTF-8 quando utilizzati in Unix, ma funzionano in Windows.

La soluzione è molto semplice:

Se provi il valore predefinito, otterrai l'errore che hai descritto

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

Tutto quello che devi fare è salvare come segue:

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

Questa riga di codice consentirà di salvare correttamente i caratteri UTF-8. Utilizzare la stessa soluzione alternativa se si sta utilizzando saveXML().


Aggiornare

Come suggerito da " Jack M " nella sezione commenti qui sotto e verificato da " Pamela " e " Marco Aurélio Deleu ", nel tuo caso potrebbe funzionare la seguente variante:

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

Nota

  1. I caratteri inglesi non causano alcun problema quando si utilizza saveHTML()senza parametri (poiché i caratteri inglesi vengono salvati come caratteri a byte singolo in UTF-8)

  2. Il problema si verifica quando si hanno caratteri multi-byte (come cinese, russo, arabo, ebraico, ... ecc.)

Consiglio di leggere questo articolo: http://coding.smashingmagazine.com/2012/06/06/all-about-unicode-utf8-character-sets/ . Capirai come funziona UTF-8 e perché hai questo problema. Ci vorranno circa 30 minuti, ma è tempo ben speso.


5
Ho dovuto utf8_decode durante l'utilizzo di questa soluzione. Grazie!
Jack M.

9
Questo doveva diventare utf8_decode ($ dom-> saveHTML (dom-> documentElement)) per preservare i miei caratteri speciali. Altrimenti, sono diventati qualcos'altro. Basta menzionarlo nel caso in cui aiuti qualcun altro.
Jack M.

4
Grazie @MrJack. Ho anche dovuto fare lo stesso per visualizzarlo senza gli strani personaggi$str = utf8_decode($dom->saveHTML($dom->documentElement));
Pamela,

1
utf8_decode($dom->saveHTML($dom->documentElement));fatto perfettamente per me.
Marco Aurélio Deleu,

2
Mi hai salvato la vita con questo. Ho cercato questa risposta OVUNQUE! Grazie!
Paulo Hgo,

15

Assicurati che il file sorgente reale sia salvato come UTF-8 (potresti anche provare i caratteri BOM non consigliati con UTF-8 per essere sicuro).

Anche in caso di HTML, assicurati di aver dichiarato la codifica corretta utilizzando i metatag:

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

Se si tratta di un CMS (poiché hai taggato la tua domanda con Joomla), potrebbe essere necessario configurare le impostazioni appropriate per la codifica.


Capisco quello che stai dicendo, ma non ho problemi a visualizzare i personaggi. se faccio "echo $ profile;" funziona benissimo. è quando DomDocument viene a conoscenza che inizia a fallire.
Leggermente A.

2
La tua meta impedisce a saveHTML di codificare tutto quanto sopra ASCII in entità. La soluzione che stavo cercando :)
sod

2
Come nota a margine, il <meta charset="UTF-8">tag più recente non funziona con DOMDocument.
Taylan,

10

È possibile aggiungere un prefisso a una riga che impone la utf-8codifica, in questo modo:

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

E puoi quindi continuare con il codice che hai già, come:

$doc->saveXML()

10

Mi ci è voluto un po 'per capire, ma ecco la mia risposta.

Prima di usare DomDocument avrei usato file_get_contents per recuperare gli URL e poi elaborarli con le funzioni stringa. Forse non è il modo migliore ma veloce. Dopo essere stato convinto che Dom fosse altrettanto veloce, ho provato per la prima volta quanto segue:

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

Ciò ha fallito in modo spettacolare nel preservare la codifica UTF-8 nonostante i metatag, le impostazioni php e tutti gli altri rimedi offerti qui e altrove. Ecco cosa funziona:

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

ecc. Ora va tutto bene nel mondo. Spero che questo ti aiuti.


Volevo solo aggiungere alla mia risposta sopra che un altro modo di affrontare questo è con il seguente, suggerito anche altrove: if ($ dom-> loadHTML ('<? Xml encoding = "UTF-8">'. $ Str) = = falso). Dopo aver pubblicato la mia risposta ho trovato un'occasione in cui il mio primo suggerimento è fallito ma il secondo ha funzionato.
Sam,

Funziona per me anche senza i parametri in DomDocument('1.0', 'UTF-8'). Ma nel mio caso viene caricato solo HTML parziale.
JKB,

5

Devi fornire al DOMDocument una versione del tuo HTML con un'intestazione che abbia senso. Proprio come HTML5.

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

forse è una buona idea mantenere il tuo html il più valido possibile, quindi non ti preoccupare quando inizi la query ... in giro :-) e stai lontano da htmlentities!!!! Questo è un necessario spreco di risorse avanti e indietro. mantieni il tuo codice pazzo !!!!


5

Sto usando php 7.3.8 su un manjaro e stavo lavorando con contenuti persiani. Questo ha risolto il mio problema:

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

Questo stesso identico consiglio è stato dato da Sam anni prima in questa stessa pagina. Si prega di non pubblicare informazioni ridondanti.
Mickmackusa,

4

Le opere trovano per me:

$dom = new \DOMDocument;
$dom->loadHTML(utf8_decode($html));
...
return  utf8_encode( $dom->saveHTML());

2
Fai attenzione, utf8_decode potrebbe perdere informazioni (sostituito da un ?)
jwal

2

Usalo per il risultato corretto

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

Questa operazione

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

È un brutto modo, perché simboli speciali come & lt; , & gt; possono essere in $ profile e non verranno convertiti due volte dopo mb_convert_encoding. È il buco per XSS e HTML errato.


1

L'unica cosa che ha funzionato per me è stata la risposta accettata di

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

PERÒ

Ciò ha comportato nuovi problemi, di avere <?xml encoding="utf-8" ?>nell'output del documento.

La soluzione per me era quindi fare

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

Alcune soluzioni mi hanno detto che per rimuovere l' xmlintestazione, che dovevo eseguire

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

Questo non ha funzionato per me come per un documento parziale (ad esempio un documento con due <p>tag), solo uno dei <p>tag in cui è stato restituito.


0

Il problema è che quando si aggiunge un parametro alla funzione DOMDocument :: saveHTML (), si perde la codifica. In alcuni casi, dovrai evitare l'uso del parametro e utilizzare la vecchia funzione stringa per trovare quello che stai cercando.

Penso che la risposta precedente funzioni per te, ma dal momento che questa soluzione alternativa non ha funzionato per me, sto aggiungendo quella risposta per aiutare chi potrebbe essere nel mio caso.



0

Questo ha funzionato per me.

Nel file php.ini,

mbstring.encoding_transration = On

cambia in

mbstring.encoding_transration = Off
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.