ブログの記事本文を抽出するスクリプトをつくってみた

ブログ検索において、RSSは必ずしも記事全文を配信していないので、クローラーが記事のURLにアクセスし記事の本文を取得するケースが多いようです。

上記の記事ではどちらも本文を抽出してくる、とあっさり書かれていますが100%に近い精度を実現するとなるとそう簡単ではないはず。

ちょっと調べてみたら以下のような取り組みが論文として読めました。英語圏の文献は、検索語が悪かったのかいまいち。「blog entry extract body text etc…」

  • NRI 技術創発 ブログ記事の自動分類により消費者意識の側面を捉える試み(PDF
  • なんでもRSS! HTML文書からのRSS Feed 自動生成 南野朋之 奥村学:人工知能学会研究会資料 SIG-SWO-A501-03(PDF

なんでもRSS!はブログから記事本文を取得しているわけではなくて、普通のウェブページからRSSに必要な要素として記事を取得する方法が書かれています。NRIのレポートではそれと同様の手法で「エントリ切り出し機構の成功確率は推定9割である」と述べられていました。

gooやYahoo!ではどんな風に実装されているんだろうと気になり、自分でもつくってみました。今回できたスクリプトでは28件中23件、8割の成功確率でした。NRIのレポートでも推定、とあるように人間が目で見て正解か確かめていくのはしんどいのでちょっとサンプル数は少なめ。

ソースは以下。PHP。

/**
 *  ブログから記事本文を取得したい
 *
 *  @author     Nob Funaki
 *  @license    This file is entirely BSD licensed.
 *
 *
 *  @param      string  HTML全文
 *  @return     string  ブログの記事本文(HTMLなし)
 */
function getBlogEntryBody($buf)
{
    $_threshold_len = 100; // 本文の必要最小文字数(strlenで)
    $_threshold_per = 0.1; // 本文中のHTML要素の割合
    $buf = substr($buf, strpos($buf, '</head>'));
    $res = '';
    $max = 0;
    $match = preg_split("'(<td[^>]*?>)|(</td>)|(<div[^>]*?>)|(</div>)'i", $buf, -1, PREG_SPLIT_NO_EMPTY);
    foreach ($match as $val) {
        $c = preg_match_all("'<[^>]*?>'", $val, $m);
        $val = trim(strip_tags($val));
        $len = strlen($val);
        if ($len > $_threshold_len) {
            $per = $c / $len;
            if ($max < $len && $per < $_threshold_per) {
                $max = $len;
                $res = $val;
            }
        }
    }
    return $res;
}

tdかdivで囲まれた文字列で、文章と比べてHTMLのタグがあまり多くないもののうち、一番文字数が多いのが本文だろう、というアルゴリズム。

実行は以下のように。

$buf = mb_convert_encoding(file_get_contents($url), 'UTF-8', 'auto');
echo getBlogEntryBody($buf);

実験に使ったのははてなブックマークの今日の人気エントリー50件。あえてはてブにしたのは記事本文がある程度長くないと精度が落ちるだろうなぁ、と思ったので。ブログではないブックマークも多いので、今回はRSSのAutoDiscoveryがあるもの28件をブログとして実験に使いました。

実行速度にはそれなりに気を使いましたが数百万のブログをクロールするとなると正規表現の部分は要チューニングかも。

うまく取得できなかった5件の大まかな特徴は、

  • サイドバーの一部と誤認
  • moreのついている記事(トップページでは全文を表示しないで記事個別ページに飛ばすタイプ)
  • 記事の中にdivを使った区切り線があった
  • はてなダイアリーのページ内リンクはどうしよう?(1ページに複数記事がある)

今回の実験対象にはありませんでしたが、Yahoo!のブログや楽天のブログもHTMLがあまり整っていないので難あり。

サイドバーやコメントに文字数が多いと誤認識するのが一番問題。これはHTMLの構造を解析する必要がありそう。ここから始まる精度との戦いはヘビーでしょう。。とはいえ、こういった基礎研究的な技術はきっとみんなの役に立つ!と思いつつ、自分も必要になるのでアップデートしていくつもりです。もしもっとうまい方法があればぜひ教えてください。

This entry was posted in つくる. Bookmark the permalink. Both comments and trackbacks are currently closed.

3 Comments

  1. Posted 2006/06/08 at 11:24 pm | Permalink

    gooではRSSのdescriptionの内容がブログのHTMLの
    どこに入ってるかを探してるだけらしいです。
    RSSを集めてるからできる方法ですね。

  2. Posted 2006/06/09 at 6:30 am | Permalink

    ブロック毎に「文章の重み」を計算してみるとかはどうですかね?
    「文章の重み」を計算する方法として、”専門用語(キーワード)自動抽出システム”( http://gensen.dl.itc.u-tokyo.ac.jp/ )を元にした”GetSen”( http://www.ryo.com/ryo/2005/06/01/39/ )というのをやってみたことがあります

  3. Posted 2006/06/09 at 8:41 pm | Permalink

    コメントありがとうございますー。

    > Ko-Jiさん
    RSSのdescriptionを利用できると確かに精度が上がる気がします。そういった方式の抽出方法もつくってもいいかも。

    > Ryosukeさん
    はてブの上位にくるような文章だと適用できるかもしれないですね。計算コストとの折り合いがつくかどうか。

5 Trackbacks

  • By Ceekz Logs on 2006/06/18 at 9:03 pm

    ブログの本文抽出にチャレンジ

    zuzara.com を読んでいると、ブログの本文抽出にチャレンジしているのを見つけました。…

  • By 何か開発するブログ on 2008/01/12 at 7:04 pm

    「はてブ速報 – 注目ニュース流し読み」公開しました…

    はてブ速報 – 注目ニュース流し読み

    小ネタ。はてなブックマークの「注目のニュース」には記事本文が付いていないので不便だなぁと思っていたので作ってみました。RSS配信にも対…

  • By ラリノサウラ開発者ブログ on 2008/01/23 at 3:00 am

    「最近投稿された画像」を表示する機能を改善…

    先日追加した「最近投稿された画像」ですが、画像を表示できるブログがあまりに少なくて個人的にがっかりしたので、がんばって本文中の画像を取得するようにしてみました。
    zuzara : …..

  • […] PHPでブログの記事本文を抽出するにあたっては、zuzara : ブログの記事本文を抽出するスクリプトをつくってみた を参考にさせていただきました。そちらのコメントにあった「 RSSのdescriptionを利用しては?」というご意見をもとに改造したものです。 […]

  • […] ブログの記事本文を抽出するスクリプトをつくってみた. update: 2011/04/15 | […]

Page optimized by WP Minify WordPress Plugin