Symfoware

Symfowareについての考察blog

PHP Simple HTML DOM Parserを使用して、CLEditorで作成したデータを解析 その2

PHP Simple HTML DOM Parserを使用して、HTMLデータの解析を行おうとしています。
PHP Simple HTML DOM Parserを使用して、CLEditorで作成したデータを解析

だいぶ頭の中が煮詰まってきたので、PHP Simple HTML DOM Parserの使い方を
改めてまとめてみます。



必要なファイルはsimple_html_dom.php



http://sourceforge.net/projects/simplehtmldom/files/simplehtmldom/
ここから最新版をダウンロードして解凍すると、いくつかファイルや
フォルダが出てきますが、最低限必要なのはsimple_html_dom.phpのみ。

これを、実行するphpファイルと同じ階層にコピーして、

include('simple_html_dom.php');


としてやると、もうPHP Simple HTML DOM Parserが使える状態になります。






基本的な使い方




$html = file_get_html('http://www.google.com/');



で、webサイトのhtmlの解析結果を得ることが出来ます。

得られた解析結果に対し、find([取得したいタグ])とすることで、Arrayが帰ってきます。
そのArrayに見つかったタグのデータがあるので、それを使用してデータの取得を行います。

例えば、
http://symfo.web.fc2.com/
このページのタイトルを取得したい場合、


<?php
include('simple_html_dom.php');

$html = file_get_html('http://symfo.web.fc2.com/');

foreach($html->find('title') as $item) {
    echo 'tag:' . $item->tag . "\n";
    echo 'outertext:' . $item->outertext . "\n";
    echo 'innertext:' . $item->innertext . "\n";
    echo 'plaintext:' . $item->plaintext . "\n";
}

?>




実行結果は、以下のようになります。

tag:title
outertext:<title>Symfoware Blog まとめサイト</title>
innertext:Symfoware Blog まとめサイト
plaintext:Symfoware Blog まとめサイト



innertext、またはplaintextを参照することで、「title」タグの内容を取得出来ます。



似たような例として、aタグで囲まれているリンクの文字列を取得するには、

<?php
include('simple_html_dom.php');

$html = file_get_html('http://symfo.web.fc2.com/');

foreach($html->find('a') as $item) {
    echo 'plaintext:' . $item->plaintext . "\n";
}

?>



実行結果は、以下のとおり。

plaintext:Symfoware blog
plaintext:Symfowareのコマンドまとめ
plaintext:言語別処理速度一覧
plaintext:掲載した記事の一覧
plaintext:カテゴリ別の記事一覧
...(略)...





応用として、リンク先のURL(hrefで指定されている値)を取得するには、

<?php
include('simple_html_dom.php');

$html = file_get_html('http://symfo.web.fc2.com/');

foreach($html->find('a') as $item) {
    echo 'href:' . $item->href . "\n";
}

?>





実行結果は以下のとおり。

href:http://symfoware.blog68.fc2.com/
href:blog/cmd.html
href:blog/speed_test.html
href:blog/sitemap.html
...(略)...





また、[タグ=値]という検索条件を追加することも可能です。
試しに、リンク先に「blog/cmd.html」と指定しているリンクの名称を取得してみます。


<?php
include('simple_html_dom.php');

$html = file_get_html('http://symfo.web.fc2.com/');

foreach($html->find('a[href=blog/cmd.html]') as $item) {
    echo 'text:' . $item->plaintext . "\n";
}

?>




実行結果は以下のとおり。

text:Symfowareのコマンドまとめ




その他、jQueryと同様にタグのidやclassに指定されいる名称での検索も可能です。








CLEditorで作成したデータ解析



上記を踏まえ、CLEditorで作成したデータ解析を行ってみます。

まず、以下のパターンで黄色くハイライトされている領域の文字を取得。

19_001_20111213232623.png


htmlデータはこうなります。

あい<span class="Apple-style-span" style="background-color: rgb(255, 255, 0);">うえおかき</span>くけこ




普通にプログラムを書くとすると、spanタグで抜けばOK

<?php
include('simple_html_dom.php');

$html = str_get_html('あい<span class="Apple-style-span" style="background-color: rgb(255, 255, 0);">うえおかき</span>くけこ');

foreach($html->find('span') as $item) {
    echo 'text:' . $item->plaintext . "\n";
}

?>




実行結果

text:うえおかき





しかし今回は、まず「あい」が来て、次にハイライトされた「うえおかき」が来て、
最後に通常の状態で「くけこ」が表示されているという情報が知りたいので、
この方法はちょっと使えそうにありません。

テキスト部分を全件取得するには、検索条件に「text」を指定してやります。


<?php
include('simple_html_dom.php');

$html = str_get_html('あい<span class="Apple-style-span" style="background-color: rgb(255, 255, 0);">うえおかき</span>くけこ');

foreach($html->find('text') as $item) {
    echo 'text:' . $item->plaintext . "\n";
}
?>




実行してみると、タグが取り除かれ、テキスト部分のみ分割されて表示されます。

text:あい
text:うえおかき
text:くけこ




まず、spanタグでくくられている部分とそれ以外に分割することができました。
このテキストはハイライトされているか否かの判定は、このテキストの親(parent)に
styleがあるかを調べれば良さそうです。


<?php
include('simple_html_dom.php');

$html = str_get_html('あい<span class="Apple-style-span" style="background-color: rgb(255, 255, 0);">うえおかき</span>くけこ');

foreach($html->find('text') as $item) {
    if ($item->parent->style) {
        echo $item->parent->style."\n";
    }
    echo 'text:' . $item->plaintext . "\n";
}
?>




実行結果

text:あい
background-color: rgb(255, 255, 0);
text:うえおかき
text:くけこ



これで、「うえおかき」にはbackground-colorに黄色が指定されていることが分かりそうです。





ハイライト途中の文字が装飾されている場合



こんなパターンを考えてみます。

19_002_20111213232623.png



生成されるhtmlはこんな感じになります。(見やすいように改行を入れてます。)

あい
<span class="Apple-style-span" style="background-color: rgb(255, 255, 0);">
うえ
<font class="Apple-style-span" color="#ff0000">お</font>
かき
</span>
くけこ





プログラムを書いて、実行してみます。

<?php
include('simple_html_dom.php');

$src = 'あい<span class="Apple-style-span" style="background-color: rgb(255, 255, 0);">うえ';
$src .= '<font class="Apple-style-span" color="#ff0000">お</font>かき</span>くけこ';

$html = str_get_html($src);

foreach($html->find('text') as $item) {
    if ($item->parent->style) {
        echo $item->parent->style;
    }
    echo 'text:' . $item->plaintext . "\n";
}
?>




実行結果

text:あい
background-color: rgb(255, 255, 0);text:うえ
text:お
background-color: rgb(255, 255, 0);text:かき
text:くけこ



「うえ」と「かき」はハイライトされていることが分かりそうですが、
赤いフォント色に変更した「お」がハイライト対象から漏れそうです。



試行錯誤した結果がこれ。


<?php
include('simple_html_dom.php');

$src = 'あい<span class="Apple-style-span" style="background-color: rgb(255, 255, 0);">うえ';
$src .= '<font class="Apple-style-span" color="#ff0000">お</font>かき</span>くけこ';

$html = str_get_html($src);

foreach($html->find('text, span') as $item) {
    
    if (isHighLightChild($item)) {
        continue;
    }
    
    if ($item->style) {
        echo 'highlight-';
    }
    echo 'text:' . $item->plaintext . "\n";
    
}

exit();


function isHighLightChild($item) {
    //親がなければ、false
    if (!$item->parent) {
        return false;
    }
    //親がstyleを持てば(とりあえず)ハイライト部分の子
    if ($item->parent->style) {
        return true;
    }
    
    //親にさらに親がいればそちらの結果に依存
    if ($item->parent->parent) {
        return isHighLightChild($item->parent);
    }
    
    //親の親がいなければfalse
    return false;
}

?>




検索条件にtextとspanを指定します。
親を再帰的にたどって、styleタグを探し、存在していたらspan内のtextとみなして
解析をキャンセルしています。
というのも、span内の文字はtextとspan両方の検索条件に合致するので、
解析をキャンセルする処理を入れていないと、

text:あい
text:うえおかき
text:うえ
text:お
text:かき
text:くけこ



こんな感じで、二回処理されます。


親がstyleを持っていたら出力をキャンセルすれば、

text:あい
highlight-text:うえおかき
text:くけこ



こんな感じで、ハイライトされる範囲が取得できました。




簡単に得られた解析結果から画像を出力するプログラムを書いてみます。


<?php

include('simple_html_dom.php');

#テキストの描画に使用するフォントファイル
define("FONT", './ipam.ttc');

$im = imagecreatefrompng("gray.png");

#使用する色を定義
$black = imagecolorallocate($im, 0x00, 0x00, 0x00);

#4pxはマージン
$text_x = 4;
$text_y = 30;

$src = 'あい<span class="Apple-style-span" style="background-color: rgb(255, 255, 0);">うえ';
$src .= '<font class="Apple-style-span" color="#ff0000">お</font>かき</span>くけこ';

$html = str_get_html($src);

foreach($html->find('text, span') as $item) {
    
    if (isHighLightChild($item)) {
        continue;
    }
    
    if ($item->style) {
        fillHighlight($im, $item, $text_x, $text_y);
        //echo 'highlight-';
        //echo $item->innertext;
    }
    
    imagettftext($im, 20, 0, $text_x, $text_y, $black, FONT, $item->plaintext);
    //echo 'text:' . $item->plaintext . "\n";
    $box = imageftbbox(20, 0, FONT, $item->plaintext);
    $text_x += $box[2] - $box[0];
}

imagepng( $im, "out.png");
#メモリ開放
imagedestroy($im);

exit();

function fillHighlight($im, $item, $text_x, $text_y) {
    
    $color = imagecolorallocate($im, 0xFF, 0xFF, 0x00);
    
    //表示領域の文字列を取得
    $text = $item->plaintext;
    $box = imageftbbox(20, 0, FONT, $text);
    
    //背景画像を出力
    #右下のX座標と左下のX座標の差が幅になる
    $back_width = $box[2] - $box[0];
    #左下のY座標と左上のY座標の差が高さになる
    $back_height = $box[1] - $box[7];

    $rect_x = $text_x + $box[6];
    $rect_y = $text_y + $box[7];
    imagefilledrectangle($im, $rect_x, $rect_y, $rect_x + $back_width, $rect_y + $back_height , $color);
}


function isHighLightChild($item) {
    //親がなければ、false
    if (!$item->parent) {
        return false;
    }
    //親がstyleを持てば(とりあえず)ハイライト部分の子
    if ($item->parent->style) {
        return true;
    }
    
    //親にさらに親がいればそちらの結果に依存
    if ($item->parent->parent) {
        return isHighLightChild($item->parent);
    }
    
    //親の親がいなければfalse
    return false;
}





まだフォントの色は反映されていませんが、狙いどおりの動作になっているようです。
19_003_20111213232623.png








フォントに色を付ける



ハイライトした上に書き込む文字の色を判別したいので、一旦今までの考えを捨てて
spanタグでの解析結果は文字列の出力に使用せず、ハイライトを付ける範囲の
計算のみに使用することにしました。

テストプログラムはこんな感じ。


<?php
include('simple_html_dom.php');

$src = 'あい<span class="Apple-style-span" style="background-color: rgb(255, 255, 0);">うえ';
$src .= '<font class="Apple-style-span" color="#ff0000">お</font>かき</span>くけこ';

$html = str_get_html($src);

foreach($html->find('text, span') as $item) {
    
    if ($item->style) {
        echo 'highlight-text:'.$item->plaintext."\n";
    } else {
        echo getFontColor($item)."-";
        echo 'text:' . $item->plaintext . "\n";
    }
    
}

exit();


function getFontColor($item) {
    //親がなければ、標準色
    if (!$item->parent) {
        return 'default';
    }
    //親がcolorを持てば(とりあえず)赤
    if ($item->parent->color) {
        return 'red';
    }
    
    //親にさらに親がいればそちらの結果に依存
    if ($item->parent->parent) {
        return getFontColor($item->parent);
    }
    
    //親の親がいなければ標準色
    return 'default';
}

?>





解析結果はこちら。

default-text:あい
highlight-text:うえおかき
default-text:うえ
red-text:お
default-text:かき
default-text:くけこ



これで、
・「あい」を標準の色で出力
・「うえおかき」の範囲をハイライト
・「うえ」を標準の色で出力
・「お」を赤で出力
・「かき」を標準の色で出力
・「くけこ」を標準の色で出力
という情報が取り出せました。


画像を出力するプログラムに組み込んでみます。




<?php

include('simple_html_dom.php');

#テキストの描画に使用するフォントファイル
define("FONT", './ipam.ttc');

$im = imagecreatefrompng("gray.png");

#使用する色を定義
$black = imagecolorallocate($im, 0x00, 0x00, 0x00);
$red = imagecolorallocate($im, 0xFF, 0x00, 0x00);

#4pxはマージン
$text_x = 4;
$text_y = 30;

$src = 'あい<span class="Apple-style-span" style="background-color: rgb(255, 255, 0);">うえ';
$src .= '<font class="Apple-style-span" color="#ff0000">お</font>かき</span>くけこ';

$html = str_get_html($src);

foreach($html->find('text, span') as $item) {
    
    if ($item->style) {
        fillHighlight($im, $item, $text_x, $text_y);
        
    } else {
        $color = $black;
        
        if (getFontColor($item) == 'red') {
            $color = $red;
        }
        imagettftext($im, 20, 0, $text_x, $text_y, $color, FONT, $item->plaintext);
        $box = imageftbbox(20, 0, FONT, $item->plaintext);
        $text_x += $box[2] - $box[0];
    }    
}

imagepng( $im, "out.png");
#メモリ開放
imagedestroy($im);

exit();

function fillHighlight($im, $item, $text_x, $text_y) {
    
    $color = imagecolorallocate($im, 0xFF, 0xFF, 0x00);
    
    //表示領域の文字列を取得
    $text = $item->plaintext;
    $box = imageftbbox(20, 0, FONT, $text);
    
    //背景画像を出力
    #右下のX座標と左下のX座標の差が幅になる
    $back_width = $box[2] - $box[0];
    #左下のY座標と左上のY座標の差が高さになる
    $back_height = $box[1] - $box[7];

    $rect_x = $text_x + $box[6];
    $rect_y = $text_y + $box[7];
    imagefilledrectangle($im, $rect_x, $rect_y, $rect_x + $back_width, $rect_y + $back_height , $color);
}



function getFontColor($item) {
    //親がなければ、標準色
    if (!$item->parent) {
        return 'default';
    }
    //親がcolorを持てば(とりあえず)赤
    if ($item->parent->color) {
        return 'red';
    }
    
    //親にさらに親がいればそちらの結果に依存
    if ($item->parent->parent) {
        return getFontColor($item->parent);
    }
    
    //親の親がいなければ標準色
    return 'default';
}

?>





細かいことは置いておくとして、とりあえず見た目は揃いました。

19_004_20111213232622.png








関連記事

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2011/12/13(火) 23:29:51|
  2. 備忘録
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集
<<PHP Simple HTML DOM Parserを使用して、CLEditorで作成したデータを解析 その3 | ホーム | PHP Simple HTML DOM Parserを使用して、CLEditorで作成したデータを解析>>

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバック URL
https://symfoware.blog.fc2.com/tb.php/866-07616330
この記事にトラックバックする(FC2ブログユーザー)