PHPでXPathを使用する

文字化けするコード

こんな感じのコードでPHPのDOMXPathを試していたけども、いくつかのサイトで文字化けが発生する。

<?php

class Index_Controllers_Index extends Sabel_Controller_Page
{
  public function index()
  {
    $url = "http://b.hatena.ne.jp/entry/http://www.sabel.jp/";
    $shr = new Sabel_Http_Request($url);
    $response = $shr->request();

    $dom = new DOMDocument();
    @$dom->loadHTML($response->getContents());

    $xpath = new DOMXpath($dom);

    dump($xpath->query("//title")->item(0)->textContent);
  }
}

例えばはてブなんかが文字化けする。

% php sabel.php ""

================================================
string(108) "&#227;&#65533;&#175;&#227;&#65533;&#166;&#227;&#65533;&#170;&#227;&#65533;&#65533;&#227;&#65533;&#65533;&#227;&#65533;&#175;&#227;&#65533;&#65533;&#227;&#65533;&#188;&#227;&#65533;&#175; - Sabel PHP&#227;&#65533;&#65533;&#227;&#65533;&#172;&#227;&#65533;&#188;&#227;&#65533;&#160;&#227;&#65533;&#175;&#227;&#65533;&#188;&#227;&#65533;&#175;"
================================================

dumpした結果をそのまま貼りつけたんだけど、はてなに投稿時に変換された><

文字化けしないコード

これをちゃんと文字化けせずに使うには、DOMDocumentに渡す前にエンコーディングをHTML-ENTITIESに変えてやれば良い。

<?php

class Index_Controllers_Index extends Sabel_Controller_Page
{
  public function index()
  {
    $url = "http://b.hatena.ne.jp/entry/http://www.sabel.jp/";
    $shr = new Sabel_Http_Request($url);
    $response = $shr->request();

    $contents = $response->getContents();
    $contents = mb_convert_encoding($contents, "HTML-ENTITIES", "auto");

    $dom = new DOMDocument();
    @$dom->loadHTML($contents);

    $xpath = new DOMXpath($dom);

    dump($xpath->query("//title")->item(0)->textContent);
  }
}

結果

% php sabel.php ""

================================================
string(60) "はてなブックマーク - Sabel PHPフレームワーク"
================================================

mb_convert_encodingの第3引数にautoを渡してやれば殆どのサイトで大丈夫だと思う。

文字化けしないコード その2

もしも上記のコードでもダメな場合は、自力で文字コードを判断しないといけない。

<?php

class Index_Controllers_Index extends Sabel_Controller_Page
{
  public function index()
  {
    $url = "http://b.hatena.ne.jp/entry/http://www.sabel.jp/";
    $shr = new Sabel_Http_Request($url);
    $response = $shr->request();

    $contents = $response->getContents();
    $charset  = "Shift_JIS";
    if (preg_match('/charset=([^"]+)/', $contents, $matches)) {
      $charset = $matches[1];
    }
    $contents = mb_convert_encoding($contents, "HTML-ENTITIES", $charset);

    $dom = new DOMDocument();
    @$dom->loadHTML($contents);

    $xpath = new DOMXpath($dom);

    dump($xpath->query("//title")->item(0)->textContent);
  }
}

まとめ

PHPのDOMはちゃんとHTML-ENTITIESに変えてやれば文字化けしないはず。
何故文字化けするのかどうかは不明。


Sabelフレームワークを使ってテストしてるので、上記コードはSabel上でしか動かないはず。