動画データを取得する その6

Javaニコニコ動画をゴニョゴニョする作戦 第1.9弾。
夏バテやら夏休みやらモチベーションがさがったりで中々進まなかったわけで。
今日は時間が出来たのでゆっくりソースかいてみた。

  • 今までやってきたこと
    • とりあえずニコニコ動画APIJavaで接続して取得したデータを解析しよう
    • Httpで繋いで結果のXMLは取得できた
    • パースも出来た
    • 途中で海外タグ対応などでXML仕様がちょっと変更(←いまこの辺)
  • 今回やったこと
    • XML新仕様に対応しちゃおうぜ!
    • Javaっぽく結果をBeanで返却しようぜ!*1


というわけでさっそくソースを晒していこう

package analyzer;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import bean.TagBean;
import bean.ThumbInfoBean;

import com.sun.org.apache.xerces.internal.parsers.DOMParser;

import exception.NiconicoException;

public class XmlPather {
  public ThumbInfoBean analyzer(InputStream is) throws NiconicoException,
                              SAXException,
                              IOException
  {
    try {

      DOMParser parser = new DOMParser();
      parser.parse(new InputSource(is)); 

      Document document = parser.getDocument();

      if (document.hasChildNodes()) {

        ThumbInfoBean thumbInfo = new ThumbInfoBean();
        thumbInfo.setResponseStatus(getStatus(document));
        if(getStatus(document).equals("ok")){
          thumbInfo.setVideoId(getNodeValue(document,"video_id"));
          thumbInfo.setTitle(getNodeValue(document,"title"));
          thumbInfo.setDescription(getNodeValue(document,"description"));
          thumbInfo.setThumbnailUrl(getNodeValue(document,"thumbnail_url"));
          thumbInfo.setFirstRetrive(getNodeValue(document,"first_retrieve"));
          thumbInfo.setLength(getNodeValue(document,"length"));
          thumbInfo.setViewCounter(getNodeValue(document,"view_counter"));
          thumbInfo.setCommentNum(getNodeValue(document,"comment_num"));
          thumbInfo.setMylistCounter(getNodeValue(document,"mylist_counter"));
          thumbInfo.setLastResBody(getNodeValue(document,"last_res_body"));
          thumbInfo.setWatchUrl(getNodeValue(document,"watch_url"));
          thumbInfo.setThumbType(getNodeValue(document,"thumb_type"));
          thumbInfo.setTagArray(getTagArray(document));
        }else{
          thumbInfo.setCode(getNodeValue(document,"code"));
          thumbInfo.setDescription(getNodeValue(document,"description"));
        }

        return thumbInfo;

      } else {
        throw new NiconicoException("childNode not found");
      } 
    } catch (SAXException e) {
      throw e;
    } catch (IOException e) { 
      throw e;
    }

  }

  /**
   * レスポンスステータスを取得
   * @param document
   * @return
   */
  private String getStatus(Document document){
    String status = "";
    NodeList nodeList = document.getElementsByTagName("nicovideo_thumb_response");
    Node node = nodeList.item(0);
    NamedNodeMap hoge = node.getAttributes();
    status = hoge.getNamedItem("status").getNodeValue();
    return status;
  }

  /**
   * 各Nodeの内容を取得(ノードが一意のもの tag以外)
   * @param document
   * @param tagName
   * @return
   */
  private String getNodeValue(Document document,String tagName){
    String value = "";
    NodeList nodeList = document.getElementsByTagName(tagName);
    Node node = nodeList.item(0);
    value = node.getTextContent();
    return value;
  }

  /**
   * 各Nodeの内容を取得(同一名のノードが複数あるもの tag)
   * @param document
   * @return
   */
  private ArrayList<TagBean> getTagArray(Document document){

    ArrayList<TagBean> resultList = new ArrayList<TagBean>();

    NodeList tagsList = document.getElementsByTagName("tags");
    for(int i=0;i<tagsList.getLength();i++){
      Node tags = tagsList.item(i);
      //domain取得
      String domain = tags.getAttributes().getNamedItem("domain").getNodeValue();

      //子ノードを取得
      NodeList childList = tags.getChildNodes();

      for (int j=0;j<childList.getLength();j++){
        TagBean tagBean = new TagBean();
        if(childList.item(j).getNodeType() == Node.ELEMENT_NODE){
          tagBean.setDomain(domain);
          tagBean.setTag(childList.item(j).getTextContent());
          if(childList.item(j).getAttributes().getNamedItem("lock") != null){
            tagBean.setLockStatus(childList.item(j).getAttributes().getNamedItem("lock").getNodeValue());
          }
          resultList.add(tagBean);
        }
      }
    }
    return resultList;
  }
}

XML新仕様に関しては前回のエントリ(こちら)を参照してね
変更されたのは主にtag情報についての部分。ロック状態、海外タグについての対応が入った。

成功時は以下のように返却されています
1ksp:6のときの動画のデータ(id:nishiohirokazuの発表 動画)を引っ張ってます。

<?xml version="1.0" encoding="UTF-8"?>
<nicovideo_thumb_response status="ok">
  <thumb>
    <video_id>sm4231432</video_id>
    <title>Pythonで作ったひどいものまとめ - nishio</title>
    <description>1000speakers:6</description>
    <thumbnail_url>http://tn-skr1.smilevideo.jp/smile?i=4231432</thumbnail_url>
    <first_retrieve>2008-08-09T14:05:43+09:00</first_retrieve>
    <length>9:09</length>
    <view_counter>262</view_counter>
    <comment_num>32</comment_num>
    <mylist_counter>5</mylist_counter>
    <last_res_body>おもしりかった w debugしたくない おもしろかったです! しかも正方形wwwwww wwwwww 1000回 えぇ... </last_res_body>
    <watch_url>http://www.nicovideo.jp/watch/sm4231432</watch_url>
    <thumb_type>video</thumb_type>
    <tags domain="jp">
      <tag>ニコニコ動画講座</tag>
      <tag>1000speakers:6</tag>
      <tag>1000speakers</tag>
      <tag>TechTalk</tag>
      <tag>Python</tag>
      <tag>サイトウサン</tag>
    </tags>
  </thumb>
</nicovideo_thumb_response>

つづいてエラー時

<?xml version="1.0" encoding="UTF-8"?>
<nicovideo_thumb_response status="fail">
  <error>
    <code>NOT_FOUND</code>
    <description>not found or invalid</description>
  </error>
</nicovideo_thumb_response>

ステータスとしてfailが帰ってきてcodeにエラーコードが入ってる。
このcodeはエラーのときのみ出現する。descriptionにはエラー内容が入ってる。

ソースの中ではそれにより場合分けしてる。
全体的なエラーハンドリングとしては、

  • XMLのパースに失敗した場合のSAXException
  • XMLのパースは成功したが予想GuyのXMLだった場合のNiconicoException
  • 単純にIO例外のIOException

を実装。(上位にスローするだけだがw)

タグの内容についてはTagBeanに、動画データはThumbInfoBeanに格納している

TagBean.java

package bean;

import java.io.Serializable;

public class TagBean implements Serializable{
  
  private static final long serialVersionUID = -3223265583035963758L;

  private String lockStatus = "";
  private String domain = "";

//以下略 アクセサがあるだけ

ThumbInfoBean.java

package bean;

import java.io.Serializable;
import java.util.ArrayList;

public class ThumbInfoBean implements Serializable {
  
  private static final long serialVersionUID = -1404974738386093736L;

  private String responseStatus = "";
  private String videoId = "";
  private String title = "";
  private String description = "";
  private String thumbnailUrl = "";
  private String firstRetrive = "";
  private String length = "";
  private long viewCounter = 0;
  private long commentNum = 0;
  private long mylistCounter = 0;
  private String lastResBody = "";
  private String watchUrl = "";
  private String thumbType = "";
  private ArrayList<TagBean> tagArray = new ArrayList<TagBean>();
//以下略 アクセサがあるだけ

こんなかんじでデータをそれぞれのBeanに格納してます。

サンプルとしてこんな感じでやると画面に表示できる。

package test;

import java.io.InputStream;
import java.util.ArrayList;

import bean.TagBean;
import bean.ThumbInfoBean;

import analyzer.XmlPather;

import connect.HttpConnector;

public class Test {

  public static void main(String[] args) {
    try{
      HttpConnector connector = new HttpConnector();
      InputStream is = connector.getVideoInfo(args[0]);
      
      XmlPather parther = new XmlPather();
      ThumbInfoBean thumbInfo = parther.analyzer(is);
      
      //レスポンスステータス
      System.out.println("resuponse Status:"+thumbInfo.getResponseStatus());
      //動画ID
      System.out.println("video_id:"+thumbInfo.getVideoId());
      //動画タイトル
      System.out.println("title:"+thumbInfo.getVideoId());
      //動画説明文
      System.out.println("description:"+thumbInfo.getDescription());
      //サムネイルURL
      System.out.println("thumbnail_url:"+thumbInfo.getThumbnailUrl());
      //投稿時間
      System.out.println("first_retrieve:"+thumbInfo.getFirstRetrive());
      //動画時間
      System.out.println("length:"+thumbInfo.getLength());
      //再生数
      System.out.println("view_counter:"+thumbInfo.getViewCounter());
      //コメント数
      System.out.println("comment_num:"+thumbInfo.getCommentNum());
      //マイリスト登録数
      System.out.println("mylist_counter:"+thumbInfo.getMylistCounter());
      //最新コメント
      System.out.println("last_res_body:"+thumbInfo.getLastResBody());
      //動画URL
      System.out.println("watch_url:"+thumbInfo.getWatchUrl());
      //動画種類
      System.out.println("thumb_type:"+thumbInfo.getThumbType());

      ArrayList<TagBean> tagArray = thumbInfo.getTagArray();
      for(TagBean tag:tagArray){
        //タグ
        System.out.println("domain:"+tag.getDomain());
        System.out.println("lock:"+tag.getLockStatus());
        System.out.println("tag:"+tag.getTag());
        System.out.println("");
      }
      
    }catch(Exception e){
      e.toString();
    }

  }

}

今回で大体の目標とした動画データの取得とかのお話はほぼ完成。
まとめて一回どこかで晒そうかと思います。CodeReposとかにAKYS*2するとか。
ググってみたけどJavaのニコ動APIラッパーってないみたい。(rubyはある)

そんな感じでーす

*1:ねこび〜んかわいいよねこび〜ん

*2:あえて空気を読まず参加