PHPでワードクラウドをつくる

(2/3)

サンプル・プログラムの解説:日本語形態素解析Webサービス

0021: /**
0022:  * Yahoo! JAPAN Webサービス アプリケーションID
0023:  *    アプリケーションIDは、
0024:  *    http://api.search.yahoo.co.jp/webservices/register_application
0025:  *    にて登録してください。
0026:  * @global string $ApplicationID
0027: */
0028: $ApplicationID = 'xxxxxxxxxxxxxxxxxxxxxxxxx';

WebAPI「日本語形態素解析 Web サービス」(Yahoo!JAPAN)を利用するため、アプリケーション ID を取得する必要がある。http://api.search.yahoo.co.jp/webservices/register_application から無料で取得できる。
取得した ID は変数 $ApplicationID に格納すること。

0186: /**
0187:  * 「Yahoo!JAPAN 日本語形態素解析Webサービス」を用いてテキストを解析する
0188:  * @param string $sentence 解析するテキスト
0189:  * @param string $filter   解析する品詞番号(1 - 13)
0190:  * @param array  $items 解析結果を格納する配列
0191:  * @return 
0192: */
0193: function getParse($sentence$filter, &$items) {
0194:     global $ApplicationID;
0195: 
0196: //WebAPIにパラメータをPOST渡しする
0197:     $url = 'http://api.jlp.yahoo.co.jp/MAService/V1/parse';
0198:     $sentence = urlencode($sentence);
0199:     $post = array(
0200:         'appid'       => $ApplicationID,
0201:         'results'     => 'ma,uniq',
0202: //      'uniq_filter' => '9|10',
0203:         'filter'      => $filter,
0204:         'sentence'    => $sentence
0205:     );

「URL で与えられたコンテンツから単語を切り出し、出現頻度を数える」処理は、「PHP で形態素解析を行う」で作成したユーザー関数 getParse をほぼそのままの形で使う。
space
ただし今回、検索対象とするのは名詞だけにすることを考えているので、filter を指定できるように、引数に $filter を追加している。

サンプル・プログラムの解説:ワードクラウドの作成

0267: /**
0268:  * ワードクラウドを作成する
0269:  * @param array  $items 情報を格納した配列
0270:  * @return string ワードクラウド(HTML)
0271: */
0272: function getWordCloud($items) {
0273:     $max_fsize = 250;            //最大サイズ(%)
0274:     $outstr = '';
0275:     $n = 0;
0276: 
0277:     //出現頻度の最大値・最小値を調べる
0278:     $count_max = 0;
0279:     $count_min = 0;
0280:     foreach ($items as $surface=>$val) {
0281:         if (mb_strlen($surface) > 1) {       //1文字は無視する
0282:             if ($val['count'] > $count_max)      $count_max = $val['count'];
0283:             if ($val['count'] < $count_min)      $count_min = $val['count'];
0284:         }
0285:     }
0286:     $k = 30 / ($count_max - $count_min);
0287: 
0288:     foreach ($items as $surface=>$val) {
0289:         if (mb_strlen($surface) > 1) {       //1文字は無視する
0290:             $n++;
0291:             $count = $val['count'];
0292:             $fsize = $k * $val['count'] * $val['count'] + 80;
0293:             if ($fsize > $max_fsize)    $fsize = $max_fsize;
0294:             $link = get_link($surface);
0295:             if ($n > 1)     $outstr .= ",\n";
0296: $outstr .=<<< EOT
0297: <span style="font-size: {$fsize}%;">
0298: <a href={$link}>{$surface}</a>
0299: </span>
0300: 
0301: EOT;
0302:         }
0303:     }
0304:     return $outstr;
0305: }

ワードクラウドを作成する処理は、「PHP で『kizasi.jp』を利用する」で使った表示ルーチン"putTitle" からだいぶ変更した。ここでは、表示文字サイズは、計算式を使って算出することにした。
space
単語の最多出現頻度を C1、最小出現頻度を C2、ある単語の出現頻度を N、表示する文字フォントサイズ(%)を S とすると、
S = 30 ÷ (C1 - C2) * N * N + 80
で計算することにした。
これは、ある単語の出現回数 N が増えると、回数の二乗に比例して文字サイズが大きくなるというものである。当初、単純比例で大きくしようとしたのだが、それでは頻度による大きさの違いがそれほど目立たなかったので、二乗に比例するようにした。
また、あまりにも大きくなりすぎるのも困るので、変数 $max_fsize で制限を設けるようにした。
space

0256: /**
0257:  * ワードクラウド表示時における個々の単語のリンク先URLを取得する
0258:  * @param string $surface 見出し語
0259: */
0260: function get_link($surface) {
0261:     $url  = 'http://www.google.com/search?hl=ja&lr=lang_ja&q=';    //Google検索
0262:     $site = 'pahoo.org';     //絞り込みsite
0263: 
0264:     return $url . urlencode($surface) . '+site%3A' . $site;
0265: }

ただ文字サイズを変更して並べるだけでは面白くないので、個々の単語にリンクを張ることにした。
space
ここでは、その単語を Google 検索に投げ、その際に site で絞り込み検索を指定するようにした。$site には自サイトの URL を入れておけばいいだろう。
space

サンプル・プログラムの解説:コンテンツの取り出し

0162: /**
0163:  * コンテンツの必要な部分を読み込む
0164:  * @param string $url コンテンツのURL
0165:  * @return string 読み込んだコンテンツ/FALSE=失敗
0166: */
0167: function my_get_contents($url) {
0168:     global $InternalEncoding$InEncoding;
0169: 
0170:     $instr = @file_get_contents($url);
0171:     $instr = mb_convert_encoding($instr$InternalEncoding$InEncoding);
0172:     if ($instr == FALSE || $instr == '')   return FALSE;
0173: 
0174:     //本文のみ読み込む(ぱふぅ家のホームページの場合)
0175:     preg_match('/\<\!\-\- 本文 \-\-\>(.*)\<\!\-\- フッタ \-\-\>/msu', $instr$arr);
0176:     $ss = isset($arr[1]) ? $arr[1] : $instr;
0177:     $ss = strip_tags($ss);           //タグを除く
0178:     $ss = html_entity_decode($ss);   //特殊文字を通常文字に変換
0179: 
0180:     $sps = array("\n", "\r", "\t");
0181:     $outstr = str_replace($sps, '', $ss);   //改行、タブを除く
0182: 
0183:     return $outstr;
0184: }

ユーザー関数 getParse に渡すコンテンツを URL から取り出す必要がある。
これには少し工夫が必要だ。
space
まず、URL の全体を getParse に渡すのは非合理的だ。ヘッダやフッタなど、コンテンツとして分析する必要がない部分も含まれてしまう。
たとえば、「ぱふぅ家のホームページ」の場合、以下のコメントで囲まれた部分に本文が入っている。
<!-- 本文 -->
  本文
<!-- フッタ -->
これ以外の部分はヘッダやメニューであるため、コンテンツには含めたくない。
space
そこで用意したのがユーザー関数 my_get_contents である。
space
まず、関数  file_get_contents  を使ってコンテンツ全体を変数 $instr に取り込む。
次に、 preg_match  を使って、解析したい本文だけを切り出す。
最後に、関数  strip_tags 、 strip_tags 、 strip_tags  を使い、HTML や PHP のタグ、特殊文字、改行・タブなどを消去しておく。
space
ユーザー関数 my_get_contents については、解析したサイトの状況に合わせて変更してほしい。
なお、「ぱふぅ家のホームページ」以外については、ヘッダやフッタを含めて解析するように変更した。
この項つづく
header