【ニコニコ動画】動画取得

ログインしたセッション、動画サーバURL、動画拡張子、動画用クッキーを利用し、
動画を取得します。
一般会員、プレミアム会員によって動画サイズが異なる事象あり。
プレミアム会員の方が解像度が高そうです。

  • HTTP通信開始

    // HTTP通信を開始する。
    URL url = new URL(movieServer);
    HttpURLConnection http = (HttpURLConnection)url.openConnection();
    
  • リクエストヘッダ部設定

    // リクエストヘッダ部を設定する。
    http.setRequestMethod("GET");
    // セッションと動画用クッキーを設定する。
    http.setRequestProperty("Cookie", "user_session=" + userSession + "; nicohistory=" + nicoHistory);
    
  • GET通信のため、リクエストボディ部の設定は不要です。

  • レスポンスボディ部取得

    // レスポンスボディ部を取得する。
    InputStream in= http.getInputStream();
    File file = new File("D:/", "sm12345678" + movieExtension);
    Files.copy(in, file.toPath());
    in.close();
    
  • レスポンスヘッダ部の取得は不要です。

【ニコニコ動画】動画用クッキー取得

ログインしたセッションを維持して、
「http://www.nicovideo.jp/watch/sm12345678」にGET通信し、
動画用のクッキーを取得します。

  • HTTP通信開始

    // HTTP通信を開始する。
    URL url = new URL("http://www.nicovideo.jp/watch/sm12345678");
    HttpURLConnection http = (HttpURLConnection)url.openConnection();
    
  • リクエストヘッダ部設定

    // リクエストヘッダ部を設定する。
    http.setRequestMethod("GET");
    // セッションを設定する。
    http.setRequestProperty("Cookie", "user_session=" + userSession);
    
  • GET通信のため、リクエストボディ部の設定は不要です。

  • レスポンスヘッダ部取得

    // レスポンスヘッダ部を取得する。
    String nicoHistory = null;
    Map<String, List<String>> responseHeaders = http.getHeaderFields();
    Iterator<String> responseIt = responseHeaders.keySet().iterator();
    while (responseIt.hasNext()) {
        String responseKey = responseIt.next();
        List<String> responseList = responseHeaders.get(responseKey);
        for(String reponseValue: responseList) {
            System.out.println(responseKey + ":" + reponseValue);
            if ("Set-Cookie".equals(responseKey)) {
                String[] cookieAry = reponseValue.split(";");
                for (String cookie: cookieAry) {
                    String[] nicoHistoryAry = cookie.split("=");
                    if ("nicohistory".equals(nicoHistoryAry[0])) {
                        nicoHistory = nicoHistoryAry[1];
                    }
                }
            }
        }
    }
    
  • レスポンスボディ部の取得は不要です。

【ニコニコ動画】動画情報取得

ログインしたセッションを維持して、
「http://flapi.nicovideo.jp/api/getflv/sm12345678」にGET通信し、
以下の動画情報を取得します。

  • 動画サーバURL
    APIのレスポンスボディ部のうち、
    「url=http%3A%2F%2Fsmile-pow64.nicovideo.jp%2Fsmile%3Fm%3D11111111.11111」を取得します。
    URLデコードしておく必要があります。
    「http://smile-pow64.nicovideo.jp/smile?m=11111111.11111」となります。
  • 動画拡張子
    動画サーバURLのパラメータから判断します。
    パラメータが「m」の場合、動画拡張子が「.mp4」、
    パラメータが「s」の場合、動画拡張子が「.swf」、
    パラメータが上記以外の場合、動画拡張子が「.flv」になります。
  • コメントサーバURL
    APIのレスポンスボディ部のうち、
    「ms=http%3A%2F%2Fnmsg.nicovideo.jp%2Fapi%2F」を取得します。
    URLデコードしておく必要があります。
  • ユーザID
    APIのレスポンスボディ部のうち、
    「user_id=11111」を取得します。
  • スレッドID
    APIのレスポンスボディ部のうち、
    「thread_id=1111111111」を取得します。
  • HTTP通信開始

    // HTTP通信を開始する。
    URL url = new URL("http://flapi.nicovideo.jp/api/getflv/sm12345678");
    HttpURLConnection http = (HttpURLConnection)url.openConnection();
    
  • リクエストヘッダ部設定

    // リクエストヘッダ部を設定する。
    http.setRequestMethod("GET");
    // セッションを設定する。
    http.setRequestProperty("Cookie", "user_session=" + userSession);
    
  • GET通信のため、リクエストボディ部の設定は不要です。

  • レスポンスボディ部取得

    // レスポンスボディ部を取得する。
    String str = null;
    String movieServer = null;
    String movieExtension = null;
    String commentServer = null;
    String userId = null;
    String threadId = null;
    BufferedReader br = new BufferedReader(new InputStreamReader(http.getInputStream()));
    while((str = br.readLine()) != null) {
        System.out.println(str);
        System.out.println();
    
        String[] responseBodyAry = str.split("&");
        for (String responseBody: responseBodyAry) {
            String[] elementAry = responseBody.split("=");
            if ("url".equals(elementAry[0])) {
                movieServer = URLDecoder.decode(elementAry[1], "UTF-8");
                String extensionChk = movieServer.substring(movieServer.indexOf("?")+1, movieServer.indexOf("?")+2);
                if ("m".equals(extensionChk)) {
                    movieExtension = ".mp4";
                } else if ("s".equals(extensionChk)) {
                    movieExtension = ".swf";
                } else {
                    movieExtension = ".flv";
                }
            }
            if ("ms".equals(elementAry[0])) {
                commentServer = URLDecoder.decode(elementAry[1], "UTF-8");
            }
            if ("user_id".equals(elementAry[0])) {
                userId = URLDecoder.decode(elementAry[1], "UTF-8");
            }
            if ("thread_id".equals(elementAry[0])) {
                threadId = URLDecoder.decode(elementAry[1], "UTF-8");
            }
        }
    }
    br.close();
    
  • レスポンスヘッダ部の取得は不要です。

【ニコニコ動画】ログイン処理

ニコニコ動画にHTTP通信でログインし、セッションを取得します。

  • HTTP通信開始

    // HTTP通信を開始する。
    URL url = new URL("https://secure.nicovideo.jp/secure/login?site=niconico");
    String params = String.format("mail=%s&password=%s", "niconico@gmail.com", "password");
    HttpsURLConnection https = (HttpsURLConnection)url.openConnection();
    
  • リクエストヘッダ部設定

    // リクエストヘッダ部を設定する。
    https.setRequestMethod("POST");
    https.setDoOutput(true);
    https.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
    https.setRequestProperty("Content-Length", Integer.toString(params.length()));
    
  • リクエストボディ部設定

    // リクエストボディ部を設定する。
    PrintStream ps = new PrintStream(https.getOutputStream());
    ps.print(params);
    ps.close();
    
  • レスポンスヘッダ部取得

    // レスポンスヘッダ部を取得する。
    String userSession = null;
    Map<String, List<String>> responseHeaders = https.getHeaderFields();
    Iterator<String> responseIt = responseHeaders.keySet().iterator();
    while (responseIt.hasNext()) {
        String responseKey = responseIt.next();
        List<String> responseList = responseHeaders.get(responseKey);
        for(String reponseValue: responseList) {
            System.out.println(responseKey + ":" + reponseValue);
            if ("Set-Cookie".equals(responseKey)) {
                String[] cookieAry = reponseValue.split(";");
                for (String cookie: cookieAry) {
                    String[] userSessionAry = cookie.split("=");
                    if ("user_session".equals(userSessionAry[0]) 
                            && userSessionAry[1].indexOf("deleted") == -1) {
                        userSession = userSessionAry[1];
                    }
                }
            }
        }
    }
    
  • レスポンスヘッダは以下のように出力されます。
    HTTPステータスコードが302なので、
    Locationに指定されたニコニコ動画のトップ画面にリダイレクトしているのが分かります。

    null:HTTP/1.1 302 Found
    x-niconico-authflag:0
    x-niconico-authflag:0
    Content-Language:ja
    Date:Fri, 28 Apr 2017 13:39:03 GMT
    Content-Length:0
    Location:http://www.nicovideo.jp/
    Set-Cookie:nicosid=1111111111.1111111111; Max-Age=315360000; Expires=Mon, 26 Apr 2027 13:39:03 GMT; Path=/; Domain=.nicovideo.jp
    Set-Cookie:user_session_secure=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; Max-Age=2591999; Expires=Sun, 28 May 2017 13:39:02 GMT; Path=/; Domain=.nicovideo.jp; Secure; HTTPOnly
    Set-Cookie:user_session=user_session_22222_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz; Max-Age=2591999; Expires=Sun, 28 May 2017 13:39:02 GMT; Path=/; Domain=.nicovideo.jp
    Set-Cookie:user_session=deleted; Max-Age=0; Expires=Fri, 28 Apr 2017 13:39:03 GMT; Path=/
    Set-Cookie:nicosid=3333333333.3333333333; expires=Mon, 26-Apr-2027 13:39:03 GMT; Max-Age=315360000; path=/; domain=.nicovideo.jp
    X-niconico-sid:3333333333.3333333333
    X-niconico-sid:4444444444.4444444444
    Connection:close
    Content-Type:text/html; charset=UTF-8
    Server:Apache
    

    取得したいのは5つある「Set-Cookie」のうち、3つ目の「user_session」の値です。
    不要:「nicosid」
    不要:「user_session_secure」
    必要:「user_session」
    不要:「user_session」(deletedのものは不要)
    不要:「nicosid」

    クッキーからセッションを取得できれば良いので
    レスポンスボディ部の取得は行いません。

【ニコニコ動画】JavaでHTTP通信

JavaでHTTP通信をお試しする上で電車検索のAPIやGoogleのAPI等、色々ありますが、
ニコニコ動画のAPIを利用してみることにしました。
まずはHTTP通信についての基礎事項を記載します。

  • HTTP通信開始
    HTTP通信を利用するには抽象クラスであるURLConnectionクラスを利用します。

    // HTTP通信接続
    URL url = new URL("http://xxx");
    HttpURLConnection httpUrlConnection = (HttpURLConnection)(url.openConnection());
    
    // HTTPS通信接続
    URL url = new URL("https://xxx");
    HttpsURLConnection httpsUrlConnection = (HttpsURLConnection)(url.openConnection());
    

    上記だけではまだサーバへの接続は開始されておらず、
    接続管理オブジェクトが生成された状態で、
    後述の「httpUrlConnection.getOutputStream()」か
    「httpUrlConnection.getInputStream()」が呼び出されると、
    内部的に「httpUrlConnection.connect()」が呼び出されます。
    切断メソッドはなく、「httpUrlConnection.getInputStream()」で取得した
    「InputStream」がcloseされるとサーバから切断されます。

  • POST通信とGET通信
    POST通信、またはGET通信を行う設定は以下の通りです。

    // POST通信
    httpUrlConnection.setRequestMethod("POST");
    httpUrlConnection.setDoOutput(true);
    
    // GET通信
    httpUrlConnection.setRequestMethod("GET");
    
  • リクエストヘッダ部の設定
    リクエストヘッダ部に値を設定する場合は以下の通りです。

    // リクエストヘッダ部設定
    httpUrlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
    
  • リクエストボディ部の設定
    リクエストボディ部に値を設定する場合は以下の通りです。
    しかし、GET通信の場合、リクエストボディ部は設定しないのが正しいです。
    パラメタは「http://xxx?mail=xxxx.yyyy.zzzz@gmail.com&password=pass」で渡します。

    // リクエストボディ部に設定するパラメータ
    String params = String.format("mail=%s&password=%s", "xxxx.yyyy.zzzz@gmail.com", "pass");
    // リクエストボディ部設定
    PrintStream out = new PrintStream(httpUrlConnection.getOutputStream());
    out.print(params);
    out.close();
    
  • レスポンスボディ部の取得
    レスポンスボディ部を取得する場合は以下の通りです。

    BufferedReader responseBody = new BufferedReader(new InputStreamReader(httpUrlConnection.getInputStream()));
    String str = null;
    while((str = responseBody.readLine()) != null) {
        System.out.println(str);
    }
    responseBody.close();
    
  • レスポンスヘッダ部の取得
    レスポンスヘッダ部を取得する場合は以下の通りです。

    Map<String, List<String>> responseHeaders = https.getHeaderFields();
    Iterator<String> responseIt = responseHeaders.keySet().iterator();
    while (responseIt.hasNext()) {
        String responseKey = responseIt.next();
        List<String> responseList = responseHeaders.get(responseKey);
        for(String reponseValue: responseList) {
            System.out.println(responseKey + ":" + reponseValue);
        }
    }
    

【Excel】CSVファイルを開くその2

  • 改行ありのテキストファイルを読み取り
    ツールバーの「データ」-「外部データの取り込み」-「テキストファイル」から読み取る方法では、
    項目内に改行があるとずれたり、
    行数が多いと最後まで読み取れないことがある。

    そこで、セル全体を文字列に設定しておき、
    1セルを選択し、適当な文字列を入力する。
    次にそのセルを選択し、ツールバーの「データ」-「区切り位置」を選択する。
    「カンマやタブなどの区切り文字によってフィールドごとに区切られたデータ」を選択する。
    区切り文字に「カンマ」を選択する。
    列のデータ形式に「文字列」を選択する。

    最後にテキストファイルをCTRL+Aで全選択後にコピーし、
    先ほどのセルに貼りつけると、項目内に改行があってもずれずに
    行数が多くても貼りつけることができる。

【C#】WebBrowser読み込み完了後にCookieを取得しWebClientに引き渡す

WindowsForm上にWebBrowserを表示し、
読み込んだサイトのCookieをWebBrowserからWebClientに引き渡して処理を行います。

# Form1.cs
namespace WindowsFormsApplication1
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            webBrowser1.Navigate("http://test1/");
        }

        private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            // クッキーを取得する。
            string cookieStr = webBrowser1.Document.Cookie;

            // WebClientを生成する。
            WebClient wc = new WebClient();
            Encoding enc = Encoding.UTF8;

            // WebClientのヘッダ設定を行う。
            // wc.Headers.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows XP)");
            wc.Headers[HttpRequestHeader.Cookie] = cookieStr;

            // WebClientでクッキーを引き継いで他のサイトにアクセスする。
            byte[] result = wc.DownloadData("http://test2/");
            string html = enc.GetString(result);
            Console.WriteLine(html);

            // クッキーの中身を確認する。
            // string[] cookieAry = cookieStr.Split(';');
            // foreach (string str in cookieAry)
            // {
            //    MessageBox.Show(str);
            // }
        }
    }
}

【VisualStudio】オブジェクトにイベント追加

WindowsForm上にあるオブジェクトにイベントを追加します。
ボタンであればデザイナ上でボタンをダブルクリックすれば
イベント追加の設定が自動で行われますが、他のオブジェクトの場合の追加方法です。

オブジェクトをアクティブにしたら右下のプロパティの稲妻マークをクリックします。
ここに表示されているものが定義できるイベントの一覧で
白いテキストボックスをクリックするとイベント追加の設定が自動で行われます。

例として、WindowsForm上に「WebBrowser」を配置し、
WebBrowserでページ読み込み完了後を検知して処理を行いたい場合、
オブジェクトをアクティブにしたら右下のプロパティの稲妻マークをクリックし、
「DocumentCompleted」を選択すると、
イベント追加の設定が自動で行われ、「webBrowser1_DocumentCompleted」が生成されます。

# Form1.Designer.cs
namespace WindowsFormsApplication1
    partial class Form1
    {
        private void InitializeComponent()
        {
            this.webBrowser1.Location = new System.Drawing.Point(52, 193);
            this.webBrowser1.MinimumSize = new System.Drawing.Size(20, 20);
            this.webBrowser1.Name = "webBrowser1"
            this.webBrowser1.Size = new System.Drawing.Size(508, 213);
            this.webBrowser1.TabIndex = 2;
            this.webBrowser1.DocumentCompleted += new System.Windows.Forms.WebBrowserDocumentCompletedEventHandler(this.webBrowser1_DocumentCompleted);
        }
    }
}
# Form1.cs
namespace WindowsFormsApplication1
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            // ここにページ読み込み完了後の処理を記載する。
        }
    }
}

【JScript】ディレクトリ内の全てのファイルを開き特定文字を置換する(行単位)

対象ディレクトリ内のファイルを行単位で読込み、置換を行います。
今回はテキストエリアの中身をCSVに出力したファイルの末尾が改行になっているものを
強制的に除去しているだけで、実際は置換していません。

対象の大量ファイルは「C:/replaceBefore」にあるとし、
置換後の大量ファイルを作成するために「C:/replaceAfter」を作成しておきます。

var fso = new ActiveXObject("Scripting.FileSystemObject");
var files = fso.GetFolder("D:/replaceBefore").Files;
var e = new Enumerator(files);
// ディレクトリ内の全てのファイル分繰り返す。
for ( ; !e.atEnd(); e.moveNext()) {
    var file = e.item();
    var fileFullPathBefore = "D:/replaceBefore/" + file.Name;
    var fileFullPathAfter  = "D:/replaceAfter/" + file.Name;
    WScript.Echo(fileFullPathBefore + "を処理します。");

    // 入力ファイルの設定
    // Typeの設定値
    // 1:バイナリを表す。
    // 2:既定値。charsetで指定した文字セットで文字列を表す。
    var sr = new ActiveXObject("ADODB.Stream");
    sr.Type = 2;
    sr.charset = "utf-8";
    sr.Open();
    sr.LoadFromFile(fileFullPathBefore);

    // 出力ファイルの設定
    var sw = new ActiveXObject("ADODB.Stream");
    sw.Type = 2;
    sw.charset = "utf-8";
    sw.Open();

    // 入力ファイルの読込み
    // 入力ファイルがEOSになるまで繰り返す。
    while (!sr.EOS) {

        // ReadTextの第1引数
        // -1:既定値。EOSマーカーに達するまですべてのバイトをストリームから読み取る。
        // -2:改行までの行をストリームから読み取る。改行はLineSeparatorの設定値依存。
        var temp = sr.ReadText(-2);

        // 置換文字列検索用
        // RegExpの第2引数に'g'を指定すると全置換になる。
//        var reg = new RegExp("[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}[.][0-9]{7}");
//        var reg = new RegExp("[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}[.][0-9]{7}", 'g');
//        temp = temp.replace(reg, "");

        // 出力ファイルの書込み
        // WriteTextの第2引数
        // ActiveXObjectのTypeが文字列(2)の場合に利用する。
        // 0:既定値。文字列を書き込む。
        // 1:文字列+改行を書き込む
        // 末尾の1文字を取得する。
        var lastWord = temp.slice(-1);
        // 末尾がダブルクォートである場合
        if (lastWord == "\"") {
//            WScript.Echo("ダブルクォート");
            // 文字列+改行を書き込む。
            sw.WriteText(temp, 1);
        // 末尾がダブルクォート以外である場合
        } else {
//            WScript.Echo("違う");
            // 文字列を書き込む。
            sw.WriteText(temp, 0);
        }
    }

    // 書き込みの文字コードがUTF-8の場合、BOMが付与されるため、除去する。
    // 書き込んだオブジェクトのポジションをゼロに戻し、
    // バイナリモードを指定した上で、ポジションを3byte分ずらし、再書き込みする。
    // この処理順序は変更不可。
    if (sw.charset == "utf-8") {
        sw.Position = 0;
        sw.Type = 1;
        sw.Position = 3;
        var textBinary = sw.Read();
        sw.Close();
        sw.Type = 1;
        sw.Open();
        sw.Write(textBinary);
    }

    sr.Close();
    sr = null;

    // 出力ファイルの保存
    // SaveToFileの第2引数
    // 1:既定値。ファイルがない場合、新規作成する。ファイルがある場合、上書きしない。
    // 2:ファイルがない場合、新規作成する。ファイルがある場合、上書きする。
    sw.SaveToFile(fileFullPathAfter, 2);
    sw.Close();
    sw = null;
}
fso = null;

【VPNサーバ】OpenVPN設定後のファイル構成

  • 設定完了後のフォルダ構成

    # 共通ファイル配置場所
    cd /etc/openvpn
    ls -l
    -rw------- 1 root root  1168  9月  3 15:59 2016 ca.crt:CA証明書(サーバとクライアントの両方で利用)
    -rw----r-- 1 root root   682  9月  3 16:31 2016 crl.pem:証明書廃止リスト
    -rw------- 1 root root   424  9月  3 16:26 2016 dh.pem:DHパラメータ
    drwxr-xr-x 4 root root  4096  9月  3 15:52 2016 easyrsa3:SSL/TLSプロトコルを利用した暗号化ツール
    -rwxr-xr-x 1 root root   530  9月  3 17:07 2016 openvpn-shutdown:OpenVPN停止時実行スクリプト
    -rwxr-xr-x 1 root root   670  9月  3 17:03 2016 openvpn-startup:OpenVPN起動時実行スクリプト
    -rw-r--r-- 1 root root 10593  9月  3 19:29 2016 server.conf:OpenVPN設定ファイル
    -rw------- 1 root root  4448  9月  3 16:03 2016 server.crt;サーバ証明書
    -rw------- 1 root root  1704  9月  3 16:03 2016 server.key:サーバ秘密鍵
    -rw------- 1 root root   636  9月  3 16:35 2016 ta.key:SSL/TLS認証鍵
    
    # 証明書配置場所
    cd /etc/openvpn/easyrsa3/pki/issued
    ls -l
    -rw------- 1 root root 4423  9月  3 16:27 2016 dmy.crt:クライアント(ダミー用)証明書(廃止済)
    -rw------- 1 root root 4432  9月  3 22:56 2016 クライアント名.crt:クライアント(iphoneやタブレット用)証明書
    -rw------- 1 root root 4448  9月  3 16:03 2016 server.crt:サーバ証明書(共通ファイル配置場所にコピーしたもの)
    
    # 秘密鍵配置場所
    cd /etc/openvpn/easyrsa3/pki/private
    ls -l
    -rw------- 1 root root 1834  9月  3 15:58 2016 ca.key:CA秘密鍵
    -rw------- 1 root root 1708  9月  3 16:27 2016 dmy.key:クライアント(ダミー用)秘密鍵(廃止済)
    -rw------- 1 root root 1704  9月  3 22:56 2016 クライアント名.key:クライアント(iphoneやタブレット用)秘密鍵
    -rw------- 1 root root 1704  9月  3 16:02 2016 server.key:サーバ秘密鍵