ニコ動APIとLINQ to XMLでごにょごにょしよう
ニコニコ動画APIで動画情報とかサムネイルとかコメントとかFLVファイルとかいろいろ取得したりする
手抜きで作りかけの雑なC#のコードを今更ながらあげておきます。
いろいろダメなところ(手抜き)があるので、もし参考にするのであれば、そこん所ご注意ください。
そのうちリファクタリングしてうpしなおすかもしれません。
ニコニコ動画の動画情報とかサムネイルとかコメントとかFLVファイルとかいろいろ取得
using System; using System.IO; using System.Collections.Generic; using System.Linq; using System.Net; using System.Text; using System.Text.RegularExpressions; using System.Xml.Linq; namespace niconico { /// <summary> /// ニコニコ動画の情報クラス /// </summary> public sealed class nicoInfo { #region メンバ private static volatile nicoInfo instance; private static object syncRoot = new Object(); /// <summary> /// ログイン成功可否 /// </summary> private bool _isLogin = false; /// <summary> /// 動画のuri /// </summary> private static string _videouri; #endregion #region 定数 /// <summary> /// ログインURL /// </summary> private const string URI_LOGIN = "https://secure.nicovideo.jp/secure/login?site=niconico"; /// <summary> /// ニコ動の正規表現パターン /// </summary> private const string NICO_URI_REGEX_PATTERN = @"^http://www.nicovideo.jp/watch/sm(?<videoid>[0-9]+)$"; /// <summary> /// getflv uri /// </summary> private const string URI_GETFLV = "http://www.nicovideo.jp/api/getflv?v=sm"; /// <summary> /// サムネイル取得用URL /// </summary> private const string THUMBNAIL_URL = "http://www.nicovideo.jp/api/getthumbinfo/sm"; #endregion #region コンストラクタ /// <summary> /// コンストラクタ /// </summary> /// <param name="user">ユーザ</param> /// <param name="password">パスワード</param> private nicoInfo(string user, string password, string videouri) { _videouri = videouri; //ログイン _isLogin = this.Login(user, password); GetVideUriCookies(cookie_login); } #endregion #region プロパティ public bool IsLogin { get { return this._isLogin; } } /// <summary> /// Login-Cookie /// </summary> private CookieContainer cookie_login = null; public CookieContainer LoginCookie { get { return cookie_login; } } #endregion #region メソッド /// <summary> /// ニコニコ動画情報クラスのインスタンスを取得します。 /// </summary> /// <param name="user">メールアドレス</param> /// <param name="password">パスワード</param> /// <param name="videouri">動画uri</param> /// <returns></returns> public static nicoInfo GetInstance(string user, string password, string videouri) { if (instance == null || _videouri != videouri) { lock (syncRoot) { if (instance == null) instance = new nicoInfo(user,password,videouri); _videouri = videouri; } } return instance; } /// <summary> /// 動画のCookieを取得します。 /// </summary> /// <param name="cc"></param> private void GetVideUriCookies(CookieContainer cc) { var hwr = (HttpWebRequest)WebRequest.Create(_videouri); hwr.CookieContainer = cc; var res = (HttpWebResponse)hwr.GetResponse(); CookieCollection cookies = hwr.CookieContainer.GetCookies(hwr.RequestUri); cc.Add(cookies); } /// <summary> /// ニコニコ動画にログインします。 /// </summary> /// <param name="user">ユーザ</param> /// <param name="password">パスワード</param> /// <returns> /// true : 成功 /// false : 失敗 /// </returns> private bool Login(string user, string password) { var post_arg = new Dictionary<string, string>(); post_arg["mail"] = user; post_arg["password"] = password; post_arg["next_uri"] = ""; cookie_login = new CookieContainer(); var ret = WebAccess.HttpPost(URI_LOGIN, post_arg, "POST", "application/x-www-form-urlencoded", cookie_login); if (ret.IndexOf("ログインエラー") != -1) { cookie_login = null; this._isLogin = false; return false; } this._isLogin = true; return true; } /// <summary> /// 対象uriのサムネイル情報Dictionaryを取得します(タグ情報を除く) /// </summary> /// <param name="uri">対象uri</param> /// <returns></returns> public Dictionary<string, string> GetThumbnailInfo(string uri) { //動画ID var vid = GetVideoId(uri); if (vid == null) return null; // send request (GET) var thumbnail_uri = THUMBNAIL_URL + vid; var resxml = WebAccess.HttpGet(thumbnail_uri, ref cookie_login); if (resxml == null) return null; var xdoc = XDocument.Parse(resxml); var query = from x in xdoc.Root.Elements().SelectMany(e => e.Elements()) where x.Name != "tags" select new { Name = x.Name.ToString(), Value = x.Value }; var dic = new Dictionary<string, string>(); foreach (var p in query) { if (!dic.ContainsKey(p.Name)) dic.Add(p.Name, p.Value); } return dic; } /// <summary> /// 対象uriのタグ情報を取得します。(日本) /// </summary> /// <param name="uri">対象uri</param> /// <returns></returns> public IEnumerable<string> GetTags(string uri) { foreach (var item in GetTags(uri, "jp")) yield return item; } /// <summary> /// 対象uriのタグ情報を取得します。 /// </summary> /// <param name="uri">対象uri</param> /// <param name="domain"> /// ドメイン名(タグの国識別 ) /// jp :日本 /// tw :台湾 /// de :ドイツ /// es :スペイン /// </param> /// <returns></returns> public IEnumerable<string> GetTags(string uri, string domain) { //動画ID var vid = GetVideoId(uri); if (vid == null) yield break; // send request (GET) var thumbnail_uri = THUMBNAIL_URL + vid; var resxml = WebAccess.HttpGet(thumbnail_uri, ref cookie_login); var xdoc = XDocument.Parse(resxml); var tags = from te in xdoc.Root.Elements().SelectMany(e => e.Elements()) where (string)te.Attribute("domain") == domain from tag in te.Elements("tag") select tag.Value; foreach (var tag in tags) yield return tag; } /// <summary> /// 動画のコメントを取得します(XDocument) /// </summary> public XDocument GetComments() { //動画ID var vid = GetVideoId(_videouri); if (vid == null) return null; var minfo = GetMovieInfo(_videouri); if (!minfo.ContainsKey("thread_id")) return null; string postData = "<thread res_from=\"-100\" version=\"20061206\" thread=\"" + minfo["thread_id"] + "\" />"; var commentXml = WebAccess.HttpPost(minfo["ms"], postData, "POST", "text/xml", ref cookie_login); return XDocument.Parse(commentXml); } /// <summary> /// ニコニコ動画の動画情報を取得します。 /// </summary> /// <param name="uri"></param> /// <returns></returns> public Dictionary<string, string> GetMovieInfo(string uri) { var vid = GetVideoId(uri); if (vid == null) return null; var getflv_uri = URI_GETFLV + vid; var response = WebAccess.HttpGet(getflv_uri, ref cookie_login); var dic = new Dictionary<string, string>(); var dec = System.Web.HttpUtility.UrlDecode(response, Encoding.UTF8); // parse by "&" var pairs = dec.Split(new char[] { '&' }); foreach (string item in pairs) { // 正規表現で分離 var reg = new Regex("(?!=[^=])(?<key>[^=]*)=(?<value>.*)"); foreach (Match m in reg.Matches(item)) { if (m.Success) { dic[m.Groups["key"].Value] = m.Groups["value"].Value; } } } return dic; } /// <summary> /// 対象uriから動画IDを取得します。 /// </summary> /// <param name="uri">対象URI</param> /// <returns>取得結果</returns> public string GetVideoId(string uri) { if (this._isLogin == false) return ""; if (cookie_login == null) return ""; var regex_uri = new Regex(NICO_URI_REGEX_PATTERN); Match regex_match = regex_uri.Match(uri); if (!regex_match.Success) return ""; return regex_match.Groups["videoid"].ToString(); } /// <summary> /// FLVファイルを取得します。 /// </summary> /// <param name="flvuri">FLVファイルURL</param> public MemoryStream GetFlvFile(string flvuri) { var hwr = (HttpWebRequest)WebRequest.Create(flvuri); hwr.CookieContainer = cookie_login; hwr.Timeout = 30 * 1000; WebResponse wres = hwr.GetResponse(); using (var res = wres.GetResponseStream()) { using (var mstream = new MemoryStream()) { int intBuffSize = 1024; int intFileSize = 0; int intRet = 0; while (true) { byte[] bytBuff = new byte[intBuffSize]; intRet = res.Read(bytBuff, 0, intBuffSize); if (intRet == 0) break; mstream.Write(bytBuff, 0, intRet); intFileSize += intRet; } mstream.Close(); res.Close(); return mstream; } } } #endregion } }
Httpで取得するやつ
using System; using System.IO; using System.Collections.Generic; using System.Net; using System.Text; namespace niconico { public static class WebAccess { /// <summary> /// HTTP GET Request /// </summary> /// <param name="uri">対象URI</param> /// <param name="cc">CookieContainer</param> /// <returns>取得結果</returns> public static string HttpGet(string uri, ref CookieContainer cc) { string result = null; // Create HttpWebRequest var req = (HttpWebRequest)WebRequest.Create(uri); req.CookieContainer = cc; var res = req.GetResponse(); using (var resStream = res.GetResponseStream()) { var sr = new StreamReader(resStream, Encoding.UTF8); result = sr.ReadToEnd(); sr.Close(); resStream.Close(); } res.Close(); return result; } /// <summary> /// HTTP POST request /// </summary> /// <param name="uri">対象URI</param> /// <param name="psots">POST パラメータ</param> /// <param name="method">送信方法: POSTなど</param> /// <param name="contentType">contentType</param> /// <param name="cc">CookieContainer</param> /// <returns>取得結果</returns> public static string HttpPost(string uri, Dictionary<string, string> posts, string method, string contentType, CookieContainer cc) { string param = ""; foreach (string k in posts.Keys) { param += String.Format("{0}={1}&", k, posts[k]); } var data = Encoding.ASCII.GetBytes(param); var req = (HttpWebRequest)WebRequest.Create(uri); req.Method = method; req.ContentType = contentType; req.ContentLength = data.Length; req.CookieContainer = cc; string result = null; // write POST data using (var reqStream = req.GetRequestStream()) { reqStream.Write(data, 0, data.Length); reqStream.Close(); var res = req.GetResponse(); var resStream = res.GetResponseStream(); using (var sr = new StreamReader(resStream, Encoding.UTF8)) { result = sr.ReadToEnd(); sr.Close(); } } return result; } /// <summary> /// HTTP POST Request /// </summary> /// <param name="uri">対象URI</param> /// <param name="post">POST パラメータ</param> /// <param name="method">送信方法: POSTなど</param> /// <param name="contentType">contentType</param> /// <param name="cc">CookieContainer</param> /// <returns>取得結果</returns> public static string HttpPost(string uri, string post, string method, string contentType, ref CookieContainer cc) { var data = Encoding.ASCII.GetBytes(post); // create HttpWebRequest var req = (HttpWebRequest)WebRequest.Create(uri); req.Method = method; req.ContentType = contentType; req.ContentLength = data.Length; req.CookieContainer = cc; string result = null; using (var reqStream = req.GetRequestStream()) { reqStream.Write(data, 0, data.Length); reqStream.Close(); var res = req.GetResponse(); // read response using (var resStream = res.GetResponseStream()) { var sr = new StreamReader(resStream, Encoding.UTF8); result = sr.ReadToEnd(); sr.Close(); resStream.Close(); } } return result; } } }
雑で味気ない画面の実装
using System; using System.Windows.Forms; using System.IO; using System.Linq; namespace niconico { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { //ニコニコ動画の情報を取得 var ni = nicoInfo.GetInstance(this.txtLoginName.Text ,this.txtPassword.Text,this.txtMovieuri.Text); if (!ni.IsLogin) { MessageBox.Show("ログインに失敗しました(^ω^;)"); return; } var flv = ni.GetMovieInfo(this.txtMovieuri.Text); foreach (var o in flv) { Console.WriteLine("{0}:{1}", o.Key, o.Value); } Console.WriteLine(); var thumbnail = ni.GetThumbnailInfo(this.txtMovieuri.Text); foreach (var o in thumbnail) { Console.WriteLine("{0}:{1}", o.Key, o.Value); } Console.WriteLine(); var tagjp = ni.GetTags(this.txtMovieuri.Text); foreach (var o in tagjp) { Console.WriteLine(o); } Console.WriteLine(); var tages = ni.GetTags(this.txtMovieuri.Text, "es"); foreach (var o in tages) { Console.WriteLine(o); } Console.WriteLine(); var xdoc = ni.GetComments(); if (xdoc != null) { var commnets = from chat in xdoc.Root.Elements("chat") select new { User = (string)chat.Attribute("user_id"), Comment = chat.Value }; foreach (var c in commnets) { Console.WriteLine("{0}:{1}", c.User, c.Comment); } } //TODO 非同期で取得するようにせにゃー var stream = ni.GetFlvFile(flv["url"]); SaveFlvFile(stream, thumbnail["title"]); } private void SaveFlvFile(MemoryStream ms,string title) { using (var sfd = new SaveFileDialog()) { sfd.FileName = title + ".flv"; sfd.InitialDirectory = @"C:\"; sfd.Filter = "FLVファイル(*.flv)|*.flv|すべてのファイル(*.*)|*.*"; sfd.FilterIndex = 1; //タイトルを設定する sfd.Title = "保存先のファイルを選択してください"; sfd.OverwritePrompt = true; while (true) { if (sfd.ShowDialog(this) == DialogResult.OK) { using (var fs = new FileStream(sfd.FileName, FileMode.Create, FileAccess.Write)) { new BinaryWriter(fs).Write(ms.ToArray()); fs.Close(); break; } } } } } } }
実行結果
thread_id:1219922148 l:89 url:http://smile-com00.nicovideo.jp/smile?v=4441890.57602low link:http://www.smilevideo.jp/view/4441890/264009 ms:http://msg.nicovideo.jp/21/api/ user_id:264009 is_premium:0 nickname:zecl time:1231861237 done:true hms:hiroba-test3.nicovideo.jp hmsp:2526 hmst:1000000042 hmstk:1231861267.fSSj3PFMnMhg6EEqn09mouqP18o video_id:sm4441890 title:【完成】ジョジョの奇妙な冒険でしょでしょ?【手書きMAD】 description:ハルヒ絵のトレースです!苦手な方は要注意原作絵で動かすのは私の力量では無理でございます…ハルヒ風(すぎる)ジョジョとしてお楽しみいただければ嬉しいです。ジョジョへの愛をいっぱい詰め込んだつもりです。応援してくれた方々のおかげと思います。そうじゃなきゃ諦めてた!mylist/5394468あけましておメメタァ!今年もジョジョでニコニコしたいですね!映像zip→www3.axfc.net/uploader/N/so/58955(キー:jojo)全画像入りzip→www2.axfc.net/uploader/Al/so/271(キー:jojo)普通のzip→www1.axfc.net/uploader/H/so/55705(キー:jojo)答え合わせ用説明動画を作ってみました→sm4462539比較動画を作ってくださいました→sm4452571 「の」が逆回転だったw thumbnail_url:http://tn-skr1.smilevideo.jp/smile?i=4441890 first_retrieve:2008-08-28T20:15:49+09:00 length:1:29 view_counter:462675 comment_num:10761 mylist_counter:16838 last_res_body:wwwwwwwwww ツェッペリまで!? メメタァ! 角川的には公認だろう watch_url:http://www.nicovideo.jp/watch/sm4441890 thumb_type:video embeddable:1 ジョジョ 手描きジョジョ アニメ ジョジョの奇妙な冒険 冒険でしょでしょ? ジョジョMAD プロの犯行 涼宮ハルヒMAD 手描きMAD殿堂入り 手描きMAD 涼宮ハルヒの憂鬱 神MAD 才能の無駄遣い parodia parody r7jOX8uA-wn1DGk7KBVMMeJhS_4:すげぇぇぇぇ bO7Sq6KQwpQArcvHmny0ZfK1b4g:まさkのフーゴw 1iDcBnU17p1INHfwYXbrIsGXWCE:wwwwwwww 1iDcBnU17p1INHfwYXbrIsGXWCE:かわええええ VTOUr6ZtCTnCLJ0n3_BDw_-GLMg:音悪www VTOUr6ZtCTnCLJ0n3_BDw_-GLMg:うはwww VTOUr6ZtCTnCLJ0n3_BDw_-GLMg:なんというトリッシュwww VTOUr6ZtCTnCLJ0n3_BDw_-GLMg:アブさんに触れてあげてください VTOUr6ZtCTnCLJ0n3_BDw_-GLMg:マイリス登録っと・・・はッ!!! VTOUr6ZtCTnCLJ0n3_BDw_-GLMg:再生はムーディーブルースが担当しております 8XJUJuQlnBSZRkL7F612QG2KDWU:無駄無駄無駄 8XJUJuQlnBSZRkL7F612QG2KDWU:おらおらおらおらおらおらおらおらおらおらおら ojQ73KzpZbAZNBc53rYghbGsdZY:一巡したな cOVGcAM43j0OZUrEH4pD9kyBk3k:カーズwww 73aSCHPYu_OdutejInDzLYFWBkQ:アバッキオじゃなくてディアボロだろw qB_mbfV88TsMg0TG9xqHYI4B0uI:おけましておメメタァ qB_mbfV88TsMg0TG9xqHYI4B0uI:ドロローサ qB_mbfV88TsMg0TG9xqHYI4B0uI:ハッ! oI_84-5XJu34hLK-IAwwPb0OcW0:ジャイロだあああああああああああ oI_84-5XJu34hLK-IAwwPb0OcW0:wwwwwwwwww oI_84-5XJu34hLK-IAwwPb0OcW0:神父ううううううう r7jOX8uA-wn1DGk7KBVMMeJhS_4:神だ r7jOX8uA-wn1DGk7KBVMMeJhS_4:売ってくれ r7jOX8uA-wn1DGk7KBVMMeJhS_4:素晴らしい r7jOX8uA-wn1DGk7KBVMMeJhS_4:すげええ r7jOX8uA-wn1DGk7KBVMMeJhS_4:色づかいが神 SkAa1409BlxgnAcrTIoH5S-rmHE:ふーご・・・? cjCqX8WqsJmKJuF1BbPVCPm3B38:ホットパンツは俺の嫁 2njokDfGb77NIKJFzzJlDnJFS6o:wwwww r1NyhtPrBGlbVeV6jPJLwXMey1M:ブチャ〜〜〜〜〜〜〜〜〜〜〜〜 FOCGJo9isdH099uvY3kpfVW8loE:ジョナサンとエリナだぁぁ g7DwnPCBPd-5QpJrrY3lMd_JZ9Y:ゆかこいた? g7DwnPCBPd-5QpJrrY3lMd_JZ9Y:アイリン!はあはあ 4b3CRAe7bllCqKpuKVt4nSWq-wU:承りさんかっけえw 4b3CRAe7bllCqKpuKVt4nSWq-wU:ジョ・・・ジョナサン! 4b3CRAe7bllCqKpuKVt4nSWq-wU:ジョリーンかわいすぎだろwww 4b3CRAe7bllCqKpuKVt4nSWq-wU:まwwwwてwwwwwっうぇwwっうぇwwww 4b3CRAe7bllCqKpuKVt4nSWq-wU:無表情すぎっすよ承りさんwww 4b3CRAe7bllCqKpuKVt4nSWq-wU:ジャイロw 4b3CRAe7bllCqKpuKVt4nSWq-wU:今なんかwww 4b3CRAe7bllCqKpuKVt4nSWq-wU:これ誰かと思ったらトリッシュかwww 4b3CRAe7bllCqKpuKVt4nSWq-wU:これ 4b3CRAe7bllCqKpuKVt4nSWq-wU:これ吉良かwww 4b3CRAe7bllCqKpuKVt4nSWq-wU:ブチャラティイイイイイイイ!!!!! 4b3CRAe7bllCqKpuKVt4nSWq-wU:DIO様wwwwwwww 4b3CRAe7bllCqKpuKVt4nSWq-wU:俺パッショーネの入団試験受けるわ・・・ L4oLRG4ktT4v3LkEQn-2F9KFQVg:じょりーんw L4oLRG4ktT4v3LkEQn-2F9KFQVg:おおおお L4oLRG4ktT4v3LkEQn-2F9KFQVg:うめえええ L4oLRG4ktT4v3LkEQn-2F9KFQVg:すげええええ L4oLRG4ktT4v3LkEQn-2F9KFQVg:キラww L4oLRG4ktT4v3LkEQn-2F9KFQVg:すげえええええ 4he0xB6t0HxwMefgKrrw1EuHaDQ:すげえよ y6NRfBhzlrhh2VCyRe7rQWjnRjg:すげぇぇぇぇぇぇぇww y6NRfBhzlrhh2VCyRe7rQWjnRjg:おいおいww y6NRfBhzlrhh2VCyRe7rQWjnRjg:wwww y6NRfBhzlrhh2VCyRe7rQWjnRjg:クオリティーたけぇぇぇ 3UQxnNl8cj6_fY2f00wiKhNd6uc:猫草!!! WffAx0eATdijIpeD8JGH8BXUiVI:んっん〜♪やはりハルヒの絵はなじむッ!! ZyLR_N8p1AB7PW9n00pVv0lbhZQ:後ろにいるのはF・F? 7419105:プロすぎるwwww 7419105:めめてぇww 7419105:上手すぎwwww 7419105:高いわwwwww 5u29-fbqkHolqSfTB5ivnZSWTpk:sugee// 5u29-fbqkHolqSfTB5ivnZSWTpk:すげー qH2t98jN-gyeGPRrEC7dFZrmhPw:すげ X_ry1oZOxXxMU3ihqDNSZz1_U7c:もう腐ったプロダクションとか要らねェなー X_ry1oZOxXxMU3ihqDNSZz1_U7c:TV無くしてオマイラだけで良いよもう X_ry1oZOxXxMU3ihqDNSZz1_U7c:オマイラでTV作れ。オレはそれで満足するYO! nMAvDbcqLYexgu_X0alJJBwJIDc:ツェペリ!ツェペリ!! 0Yf09u8oaWOd5IhN8OZ9HpNjpDk:↓ディモールトその通りだぜ 7731548:wwwwwwwwwwwwwwwwwwww 5841237:神 5841237:おもしれ〜 5841237:DIO PYue57ZZUSpVsPgPobnADE4tcT4:ホットパンツ!! 0l5Lw8kenOBDq8MY9h8U8TqzSq0:お前はこの動画見た後バイツァダストで吹っ飛び、再生している! 1_miV2HabbFN6JnFvZWVtZs3iwI:おメメタァ! e4745myKKRpBG2l2P8W7DIOs5F8:うますぎるww e4745myKKRpBG2l2P8W7DIOs5F8:マイリスト登録 q_0dt0cV0zABeW_MuiU-_GdFFN0:スージーQはもらっていきますね XADKq0xAT0EPx9ztUMO0gAEndCc:うまいwwwwwwww XADKq0xAT0EPx9ztUMO0gAEndCc:どんだけw手描きの域超えてるww LLPLV1DOQRC0koc9C6TcV9o6_Ao:無駄無駄にクオリティたけーwwwwww VwxshOfW1na1kgpsfwQAzLUghSs:かっけぇえwww JP5Eeoyyt6hmeAaApIm0vpgZaYs:イルーゾォ男前えええええええ gqn-CkwancNXHoEV3vlvvYdI-YE:すごい感動した・・・ 0l5Lw8kenOBDq8MY9h8U8TqzSq0:キラッ QWUnE4KS34VEzVXt_OUnXPieKfw:あけましておメメタァ! ToGG0qoMXzvuYLrlysrSLzfiEp8:ええ ToGG0qoMXzvuYLrlysrSLzfiEp8:おい ToGG0qoMXzvuYLrlysrSLzfiEp8:SBR ToGG0qoMXzvuYLrlysrSLzfiEp8:これは手間かかってるなぁ 3FwK1vb4C0c8J2NrYUATUA0It8c:すげーなw 3FwK1vb4C0c8J2NrYUATUA0It8c:ええーwwww 3FwK1vb4C0c8J2NrYUATUA0It8c:角川的には公認だろうが集英社はw 2k6yGpsgYSV7I0gBGAMQNzFshS8:メメタァ! hut36SnH4_vH0X2aQuRRCRSHpXE:ツェッペリまで!? kqvwPbUiPTA309B2gn7lsd3bdxc:wwwwwwwwwww
それはそうと、LINQ to XML使ったら、やっぱり楽ですねえ。こんなに楽でいいのかな。
昔、一生懸命セコセコ作っていたのは一体なんだったのかw