2011年5月30日

Yahoo!知恵袋

Yahoo!知恵袋Web APIを使用したQ&Aサイトの作成パート2

  • このエントリーをはてなブックマークに追加

本記事は、OAuth1.0の仕様に則った内容になっています。2012年11月7日にYConnectが公開され、OAuth2.0のフローで知恵袋Web APIを利用できるようになりました。より簡潔なプログラムにすることが可能です。
詳しくはYConnectのドキュメントをご覧ください。

こんにちは、R&D統括本部の望月です。
前回、「Yahoo!知恵袋Web APIを使用したQ&Aサイトの作成パート1」では、新着質問リストの表示から質問の詳細情報の表示までをご説明させていただきました。

今回は「Yahoo!知恵袋Web APIを使用したQ&Aサイトの作成パート2」と題しまして、取得した質問に回答を投稿できる機能の追加についてご説明します。

使用するAPIは、「知恵袋 - 回答投稿・編集」です。
このAPIを使用することで、Yahoo!知恵袋に回答が投稿できるようになります。

作成するページ

作成するページは下記の1ページです。
回答投稿画面

使用するAPI

使用するAPIは1つです。
知恵袋 - 回答投稿・編集

準備

回答投稿・編集APIではOAuthを使用しますので、下記の2つを取得する必要があります。
   1. OAuth Consumer Key
   2. Consumer Secret
まずはその取得方法を簡単に説明いたします。

Yahoo!デベロッパーネットワークの「新しいアプリケーションを開発」ページを開いてください。
このページから入力フォームの説明に沿って進んでいただければ問題なく取得できますが、少し注意が必要な項目を2か所だけ画像を交えて説明します。

注意点1. "2.開発するアプリケーションの選択"
"認証を必要とするAPIを使ったアプリケーション" を選択してください。



注意点2. "3.アプリケーションの基本情報"
"OAuthで利用するスコープ"の項目で、"回答投稿関連の機能"を"利用する"としてください。



説明に沿って入力を行い、登録が完了しますと下記の画面が表示されます。



ここに記述されている"OAuth Consumer Key"と"Consumer Secret"を使用してください。
また、登録完了後は「アプリケーションの管理」 画面からも確認できます。

これで準備は完了となります。お疲れさまでした。
では、さっそく本編に入って行きましょう。

サンプル

前回と同様にPHPを使用して作成しています。
下記の環境で動作確認しています。

構成環境:Apache-2.2.14 + PHP-5.3.1

1. OAuth PHP SDKを設置

Yahoo! JAPANのOAuth対応のAPIを簡単に実装するため、OAuth PHP SDK を使用します。
アーカイブファイルを展開したら下記のような構造になるように設置してください。
|-- index.php    ←パート1で作成したファイル
|-- detail.php   ←パート1で作成したファイル
`-- lib
    |-- CookieSessionStore.php
    |-- NoCookieSessionStore.php
    |-- OAuth.php
    |-- OAuthClient.php
    |-- YahooAbstractApi.php
    |-- YahooAuthentication.php
    |-- YahooException.php
    |-- YahooHeaderParser.php
    |-- YahooLogger.php
    |-- YahooOAuth.inc
    |-- YahooSession.php
    |-- YahooSessionStore.php
    `-- YahooUtil.php

2. 回答投稿画面の作成

下記のソースコードをanswer.phpというファイル名で、detail.phpと同じディレクトリに設置してください。

<?php

/**
 * OAuth SDK を読み込みます。
 */
require('./lib/YahooOAuth.inc');
require('./lib/YahooAbstractApi.php');

/**
 * Consumer Key、Consumer Secret
 * 事前準備で取得したOAuth Consumer Key及びConsumer Secretを指定してください。
 */
define('CONSUMER_KEY', '<あなたのConsumer Key>');
define('CONSUMER_SECRET', '<あなたのConsumer Secret>');

/**
 * 知恵袋回答投稿APIクラス
 */
class PostAnswerApi extends YahooAbstractApi
{
    const REQUEST_URL = 'http://chiebukuro.yahooapis.jp/Chiebukuro/V1/postAnswer';

    /**
     * execute HTTP POST request
     *
     * @param String $entrypoint
     * @param Array  $parameters
     */
    protected function httpPost( $entrypoint, $parameters = array() )
    {
        $contentType = "application/x-www-form-urlencoded";

        $this->result = $this->client->post($entrypoint, $contentType, $parameters);
    }

    /**
     * レスポンスを取得
     */
    public function getResponse($parameters)
    {
        $this->httpPost(self::REQUEST_URL, $parameters);
        $response = $this->getResponseBody();
        return simplexml_load_string($response);
    }
}

// エラーメッセージ用
$err_msg = '';

// 質問ID
if (array_key_exists('question_id', $_REQUEST)) {
    $question_id = preg_replace('/[^0-9]/', '', $_REQUEST['question_id']);
}

if (isset($question_id) && $question_id != '') {

    // 回答本文を一時的にcookieへコピーします。
    $cookie_key = 'chie_ans_text_' . $question_id;

    if (array_key_exists('ans_text', $_POST)) {
        setcookie($cookie_key, $_POST['ans_text'], time()+60*30);
        $ans_text = $_POST['ans_text'];
    } elseif (isset($_COOKIE[$cookie_key])) {
        $ans_text = $_COOKIE[$cookie_key];
    }

} else {
    $err_msg .= "指定された質問は存在しません。\n";
}

// このプログラムのURLを取得し、コールバックURLにセットします
$callback = sprintf('http://%s%s?question_id=%s', $_SERVER['SERVER_NAME'], $_SERVER['SCRIPT_NAME'], $question_id);

// セッションを取得します
$session = YahooSession::requireSession(CONSUMER_KEY, CONSUMER_SECRET, $callback);

if (is_object($session)) {

    // Yahoo! JAPAN IDに対して一意なEnd Userの識別子
    $guid = $session->getGuid();

    // 不正投稿回避用KEY作成
    if (isset($question_id)) {
        $secret_key = md5($guid . $question_id . CONSUMER_KEY);
    }

    // 回答投稿
    if (isset($ans_text) && $err_msg == '' && array_key_exists('key', $_REQUEST)) {

        // 不正投稿回避用KEYの比較
        if ($secret_key === $_REQUEST['key']) {
            $api = new PostAnswerApi($session);

            $response = $api->getResponse(array(
                'content'       => $ans_text,
                'question_id'   => $question_id));

            // エラーメッセージ
            if (isset($response->Message)) {
                $err_msg .= $response->Message . "\n";
            }
        } else {
            $err_msg .= "回答が投稿できませんでした。\n";
        }
    }

} else {
    $err_msg .= "現在回答が投稿できません。\n";
}

// cookie内の回答本文を削除します。
if (isset($cookie_key)) {
    setcookie($cookie_key, "", time() - 3600);
}

?>
<html>
<meta http-equiv=content-type content="text/html; charset=utf-8" />
<head>
<title>地震に関する質問<title>
<style type="text/css">
<!--
dt,dd {
    padding:5px;
}
#ans_text {
    height:100px;
    width:400px;
}
-->
</style>
</head>
<body>
<a href="./index.php">&lt;&lt;&nbsp;地震に関する質問一覧</a><br />
<a href="./detail.php?question_id=<?php echo $question_id; ?>">&lt;&nbsp;質問詳細ページに戻る</a> 

<?php if ($err_msg != '') : ?>
<div id="content_error"> 
    <dl> 
        <dt>エラー</dt> 
        <dd><?php echo nl2br($err_msg); ?></dd> 
    </dl> 
</div> 
<?php endif; ?> 
<?php if (isset($response) && $err_msg == '') : ?> 
<div id="content_result"> 
    <p>あなたの回答が投稿されました。</p> 
    <a href="<?php echo $response->Result->QuestionUrl; ?>">知恵袋の質問詳細ページ</a> 
</div> 
<?php else : ?>
<div id="content_input"> 
    <form method="POST" action="./answer.php"> 
    <dl> 
        <dt>回答本文</dt> 
        <dd><textarea name="ans_text" id="ans_text"><?php if(isset($ans_text)) echo htmlspecialchars($ans_text); ?></textarea></dd> 
    </dl> 
    <input type="hidden" name="question_id" value="<?php echo $question_id; ?>"> 
<?php   if (isset($secret_key)) : ?>
    <input type="hidden" name="key" value="<?php echo $secret_key; ?>"> 
<?php   endif; ?>
    <input type="submit" value="この内容で投稿する"> 
    </form> 
</div> 
<?php endif; ?>

<hr> 
<!-- Begin Yahoo! JAPAN Web Services Attribution Snippet --> 
<a href="http://developer.yahoo.co.jp/about"> 
<img src="https://s.yimg.jp/images/yjdn/yjdn_attbtn2_105_17.gif" width="105" height="17" title="Webサービス by Yahoo! JAPAN" alt="Webサービス by Yahoo! JAPAN" border="0" style="margin:15px 15px 15px 15px"></a> 
<!-- End Yahoo! JAPAN Web Services Attribution Snippet --> 
</body> 
</html> 

このファイルでは回答入力画面の表示と、回答投稿Web APIを使用して回答の投稿を行っています。
PostAnswerApiではOAuth PHP SDKに含まれる抽象クラス(YahooAbstractApi)を継承し、回答投稿Web APIにアクセスして結果を取得するための実装をしています。

SDKを使用したOAuth関連の処理については、「PHPでYahoo!のOAuth対応APIにアクセスしてみよう!」で説明されていますので、こちらでは割愛させていただきます。

上記のソースコードについて2点補足します。

・cookieの使用について
セッション情報を取得するときに未ログイン状態の場合はログイン画面、アプリケーションでのデータ利用に同意していない場合は同意画面にリダイレクトされます。
セッション情報を取得する前に回答本文をいったんcookieに保存していますが、回答本文を保持するためだけの役割ですので、取得後はcookieを削除しています。

・不正投稿回避用KEYについて
回答内容を受け取ってそれをそのまま投稿してしまうと、他サイトから悪用されてユーザーの意図しない投稿がされてしまう恐れがあります。
md5を使用してKEYの一致を確認することで、不正な投稿について簡易チェックを行っています。

さて、これで回答投稿の機能の追加が完了しました。
しかし、このままではせっかく作った画面が独りぼっちで寂しそうです。
回答投稿画面へのリンクを追加してあげましょう。

3. 回答投稿画面へのリンクを追加

前回作成したdetail.phpに回答投稿画面へのリンクを追加します。
追加後は下記のようになります。
<a href="./answer.php?question_id=<?php echo $response->Result->QuestionId; ?>">この質問に回答する</a><br /><br />
<a href="<?php echo $response->Result->PcQuestionUrl; ?>">知恵袋の質問詳細ページ</a>

以上で質問の詳細画面から回答投稿画面へ遷移できるようになりました。

まとめ

パート1、パート2の2回に分けてご説明してまいりましたQ&Aサイトの作成ですが、いかがでしたでしょうか。
ぜひ皆様の自由な発想で、疑問や悩みを解決するのに役立つアプリケーションを開発してみてください。
これからもYahoo!知恵袋をよろしくお願いいたします。

Yahoo! JAPANでは情報技術を駆使して人々や社会の課題を一緒に解決していける方を募集しています。詳しくは採用情報をご覧ください。

  • このエントリーをはてなブックマークに追加