DOMDocument#loadHTML()の出力がおかしい
PHPで、DOMDocument#loadHTML()を使用すると、HTMLのテキストからDomのツリーを作成することが出来ます。
とあるファイルを、loadHTML()したのですが、なぜか正しくツリーが作られないことがありました。
しかも、まったく作られない訳ではなく途中で途切れてしまう。
中身を調べてみると、”㈱”(かっこかぶ:機種依存文字)や、”鎌”の文字が出てきたところで解析が打ち切られています。”かっこかぶ”は明らかにヤバそうですが、”鎌”はダメ文字なので、loadHTMLが文字コードを勘違いしている雰囲気です。
余談:「ダメ文字」とは?
ダメ文字とは、2byte目が0x5cになっている文字で、主なものには以下の文字があります。
詳しくは、wikipediaでも見てください。
ーソЫⅨ噂浬欺圭構蚕十申曾箪貼能表暴予禄兔喀媾彌拿杤歃濬畚秉綵臀藹觸軆鐔饅鷭偆砡纊犾 |
ーソЫⅨ噂浬欺圭構蚕十申曾箪貼能表暴予禄兔喀媾彌拿杤歃濬畚秉綵臀藹觸軆鐔饅鷭偆砡纊犾
原因を調べる
loadHTML()に渡したhtmlテキストのheadを見ると、以下の定義がされてた。
<meta http-equiv="Content-Type" content="text/html; charset=shift_jis"> |
<meta http-equiv="Content-Type" content="text/html; charset=shift_jis">
SJISに㈱は無い(windowsの機種依存文字だから)ので、loadHTMLがこけるのも仕方が無い状況でした。
charsetを修正してみるがうまくいかない…
元となるhtmlテキストを変更すれば簡単なのですが、今回、元ネタは変更できない状況だったので、loadHTML()をコールする直前でcharsetを強制変換させてみた。
$doc = new DOMDocument();
@$doc->loadHTML( $html ); |
$doc = new DOMDocument();
@$doc->loadHTML( $html );
↓
$doc = new DOMDocument();
$html = str_replace("charset=shift_jis", "charset=Windows-31J", $html );
@$doc->loadHTML( $html ); |
$doc = new DOMDocument();
$html = str_replace("charset=shift_jis", "charset=Windows-31J", $html );
@$doc->loadHTML( $html );
これでいけるはず… なのにうまくいかない。
というか全体が文字化けしているのでcharsetを正しく解釈してくれていないみたい。
CP932の指定だと、なぜかうまく動いた
仕方が無いのでダメ元で、CP932を指定してみる。
$doc = new DOMDocument();
$html = str_replace("charset=shift_jis", "charset=CP932", $html );
@$doc->loadHTML( $html ); |
$doc = new DOMDocument();
$html = str_replace("charset=shift_jis", "charset=CP932", $html );
@$doc->loadHTML( $html );
これで、なぜかうまく動作しました。
IANAだと、charsetにCP932は無いはずなのに、なぜだろう…と思ったけど使い捨てのプログラムなので深く考えないことにする。
別解:元データの文字コードを変更してしまう手もあり?
あと、元データの文字コード自体をutf8に変更するという方法でもOKでした。
$doc = new DOMDocument();
$html = mb_convert_encoding($html, "UTF-8", "SJIS-win" );
$html = str_replace("charset=shift_jis", "charset=utf-8", $html );
@$doc->loadHTML( $html ); |
$doc = new DOMDocument();
$html = mb_convert_encoding($html, "UTF-8", "SJIS-win" );
$html = str_replace("charset=shift_jis", "charset=utf-8", $html );
@$doc->loadHTML( $html );
遅そうだったので今回は不採用でしたが、CP932は絶対に指定したくないという人は、この方法も良いかもしれません。
関連記事
コメントを残す