自動化は怖くないッ!~VBScript編③~

マウスを動かすのって本当に面倒くさいですよね

さあ、もうすでに皆様お気づきと思いますが、この見出し、様式美となっております。
それはさておき、前回で皆様にもVBScriptの基本が押さえられたと思います。
つまりは皆様が自動化・ツール作成の選ばれし勇者、次のステップとなる『応用編』に進めるという事になります!!(テンション高めにお送りしております)
早速ですが今回お送りする議題をお伝えしましょう。
題して、

自動でいつも表示しているニュースサイトにログイン!

……
あんまりピンと来ないかもしれませんね。
百聞は一見に如かずです。早速やっていきましょう!!

どういうことなの…の、前に

まず、この記事は実は〇年前に手掛けた記事となります。
そのため、色々と世間の流れから置き去りにされていることをまずはご了承ください。
日の目を見ずに記事が眠りに就くのが個人的にさみしかったので……

改めまして、どういうことなの

皆様も一度は通販サイトやニュースサイトのログインをしたことがあると思います。
どうやってログインしていますか?もちろん
 ①ブラウザを起動して
 ②ページを表示して
 ③マウスでログインIDのエリアをクリックして
 ④キーボードでログインIDを入力して
 ⑤マウスでパスワードのエリアをクリックして
 ⑥キーボードでパスワードを入力して
 ⑦マウスで「ログイン」とか色々なボタンをクリックする
このいくつもの段階を踏んでいると思います。

いやいや、その段階ごと実は自動化できるんですよ!!

いいからやり方を教えて!

さあ皆大好きメモ帳さんの出番です!
色んな疑問はさておいて、以下をコピー&メモ帳に貼り付けてください。まずはそこからです!
※今回は長くなるので高さ幅を設定しています。下のエリアにマウスを当てると右上側に表示されるボタンの4つ目をクリックしてコピー&メモ帳に貼り付けてください。

Option Explicit
' ==========================================================
' 機能名:AutoAccessURL
' 概要  :開きたい対象のページを開き、自動でログイン処理を行う
' 作成者:(M_M)
' 作成日:9999/12/31
'
' 【履歴】
' 9999/12/31 新規作成
' ==========================================================

' ============================
' 定数・変数一覧
' ============================
' 定数
' IEで表示したいURLを設定します。例として「https://account.nicovideo.jp/login?site=news」を指定します
Const constUrl="https://account.nicovideo.jp/login?site=news"

' ユーザIDやメールアドレスetc、そのサイトにログインする際に利用するHTML上のID
Const constLoginName="input__mailtel"
' そのサイトにログインする際に利用するパスワードのHTML上ID
Const constPassword="input__password"
' そのサイトにログインする際に押されるボタン・リンクのHTML上ID
Const constLoginBtnId="login__submit"

' 共通変数
Dim user        'ユーザ名
Dim pass        'パスワード
Dim text        'URLとそれに紐づくユーザ名とパスワードを格納したファイル



' ============================
' 実際の処理を記述
' ============================
' メイン処理呼出
Call Main()
' おしまい!!!
WScript.Quit(0)

' ++++++++++++++++++++++++++++
' Main Routein
' ++++++++++++++++++++++++++++
' ==========================================================
' 関数名:Main
' 概要  :メイン処理
' 引数  :なし
' 戻り値:なし
' ==========================================================
Private Sub Main()

    ' ログインに必要な情報を設定
    Call SetLoginPassword

    ' 一旦全てのIEブラウザのプロセスを削除
    Call ShutdownIEProcess

    ' IEのみアクティベート
    Call ActivateIEBroobjWser

    ' ログイン実行
    Call ExecuteLogin

End Sub



' ++++++++++++++++++++++++++++
' Sub Routein
' ++++++++++++++++++++++++++++
' ==========================================================
' 関数名:SetLoginPassword
' 概要  :ユーザーIDとパスワードを入力してもらう
' 引数  :なし
' 戻り値:なし
' ==========================================================
Private Sub SetLoginPassword()
    ' 初回のみ入力とし、以降は設定ファイルを読み込む
    Dim objWs            ' WshShell
    Dim objFSO           ' FileSystemObject
    Dim objTextFile      ' 入出力ファイル
    Dim strLine          ' login.txtの読込行内容

    ' 読込み対象のファイルを取得するためOSに対するShellオブジェクトを設定
    ' 取得対象は、「C:\Users\<ユーザ名>\Documents」フォルダにあるlogin.txt
    Set objWs = WScript.CreateObject("WScript.Shell")
    text = objWs.SpecialFolders("mydocuments") & "\login.txt"
    ' 情報取得後はobjWsは不要のためメモリから解放
    Set objWs = Nothing

    ' FileSystemObjectを設定
    Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")

    ' まだ1回もログインしていない場合
    If objFSO.FileExists(text) = False Then
        user = InputBox("ユーザーIDを入力してください")
        pass = InputBox("パスワードを入力してください")
        If user = "" Or pass = "" Then
            MsgBox "ユーザーIDとパスワードは正確に入力してください"
            Set objFSO = Nothing
            WScript.Quit(1)
        End If
        ' 書き出しファイルの指定
        Set objTextFile = objFSO.OpenTextFile(text, 2, True)
        objTextFile.WriteLine constUrl & "," & user & "," & pass
        objTextFile.Close
    End If

    ' 過去にログイン済みの場合(login.txtファイルが存在する)
    If user = "" Or pass = "" Then
        Set objTextFile = objFSO.OpenTextFile(text, 1, False, 0)
        strLine = objTextFile.ReadLine

        ' テキストファイルから読み込み、ユーザIDとパスワードを取得する
        user = Split(strLine, ",")(1)
        pass = Split(strLine, ",")(2)
        objTextFile.Close
    End If
    Set objTextFile = Nothing
    Set objFSO = Nothing
End Sub



' ==========================================================
' 関数名:ShutdownIEProcess
' 概要  :起動しているIEプロセスを削除する
' 引数  :なし
' 戻り値:なし
' ==========================================================
Private Sub ShutdownIEProcess()
    Dim objProcList, objProcess, objRegEx
    Set objProcList = GetObject("winmgmts:").InstancesOf("win32_process")
    Set objRegEx = new RegExp 
    objRegEx.IgnoreCase = True
    objRegEx.pattern = "iexplore.exe"

    For Each objProcess In objProcList
        If objRegEx.Test(objProcess.Name) = True Then
            objProcess.Terminate
        End If
    Next

    Set objProcList = Nothing
    Set objProcess = Nothing
    Set objRegEx = Nothing

End Sub



' ==========================================================
' 関数名:ActivateIEBroobjWser
' 概要  :IEのみをアクティベートする
'         【注意】
'         他にアクティブなアプリケーションがあると一旦
'         最小化されてしまう
'         付箋とかある人は注意!
' 引数  :なし
' 戻り値:なし
' ==========================================================
Private Sub ActivateIEBroobjWser()

    'オブジェクトを格納する変数
    Dim objWin, objIE

    ' 画面をすべて最小化する
    Set objWin = WScript.CreateObject("Shell.Application")
    objWin.MinimizeAll

    '処理したいページを表示
    Set objIE = WScript.CreateObject("InternetExplorer.Application")
    objIE.Visible = True
    objIE.Navigate constUrl
    If Err.Number = 0 Then
        'ページが読み込まれるまで待つ
        Do While objIE.Busy = True Or objIE.readyState <> 4
            WScript.Sleep 100
        Loop
    End If

    ' リソース解放
    Set objIE = Nothing
    Set objWin = Nothing

End Sub



' ==========================================================
' 関数名:ExecuteLogin
' 概要  :ログインを実行する
' 引数  :なし
' 戻り値:なし
' ==========================================================
Private Sub ExecuteLogin()

    'オブジェクトを格納する変数
    Dim objShell, objWindow, objButton

    'シェルのオブジェクトを作成する
    Set objShell = WScript.CreateObject("Shell.Application")

    On Error Resume Next
    For Each objWindow In objShell.Windows
        If TypeName(objWindow.document) = "HTMLDocument" Then
            objWindow.document.getElementById(constLoginName).Value = user
            objWindow.document.getElementById(constPassword).Value = pass
            objWindow.document.getElementById(constLoginBtnId).Click
            WScript.Sleep 10000
            Exit For
        End If
    Next
    On Error Goto 0

    ' 遷移後のURLに「err=1」が含まれている=ログイン失敗
    If InStr(objWindow.document.URL, "err=1") > 0 Then
        ' ログインに失敗したら入力されたテキストファイルは削除する
        MsgBox "ログインに失敗しているようです。もう一度このファイルを実行してください。"
        Dim objFSO
        Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")
        objFSO.DeleteFile text
        Set objFSO = Nothing
        Call ShutdownIEProcess
    End If

    ' リソース解放
    Set objButton = Nothing
    Set objWindow = Nothing
    Set objShell = Nothing

End Sub

保存の方法、起動時に呼び出されるフォルダに配置する方法は今までの流れからお分かりいただけると思います。
これでOK!

実際にやってみた

スタートアップフォルダにvbsファイルを置いたら、パソコンをシャットダウン&起動してみましょう。
どうですか?

何か変なダイアログが表示されましたね……
表示されている通りに、ログインに必要なユーザーIDとパスワードを入力してみましょう。
(間違い入力に気を付けてくださいね!)
すると……

おおっ!InternetExplorerが表示されてニコニコのログイン画面が表示されたかと思ったら自動でニコニコニュースにログインしましたね!?
マウスなんて全く必要ない!!!
ちなみに、正常に自動ログインした後は、もうユーザーIDもパスワードも入力の必要はありません!
(「ご利用のブラウザ(Internet Explorer)はサポートしていません」の文字が非常に哀愁を感じますね!!!)

で、何がどうなっているのか詳しく

色々とすっ飛ばしましたが、今回はVBScriptの応用編も兼ねていることもあり、細かーく見ていきたいと思います。

Option Explicit
' ==========================================================
' 機能名:AutoAccessURL
' 概要  :開きたい対象のページを開き、自動でログイン処理を行う
' 作成者:(M_M)
' 作成日:9999/12/31
'
' 【履歴】
' 9999/12/31 新規作成
' ==========================================================

' ============================
' 定数・変数一覧
' ============================
' 定数
' IEで表示したいURLを設定します。例として「https://account.nicovideo.jp/login?site=news」を指定します
Const constUrl="https://account.nicovideo.jp/login?site=news"

' ユーザIDやメールアドレスetc、そのサイトにログインする際に利用するHTML上のID
Const constLoginName="input__mailtel"
' そのサイトにログインする際に利用するパスワードのHTML上ID
Const constPassword="input__password"
' そのサイトにログインする際に押されるボタン・リンクのHTML上ID
Const constLoginBtnId="login__submit"

' 共通変数
Dim user        'ユーザ名
Dim pass        'パスワード
Dim text        'URLとそれに紐づくユーザ名とパスワードを格納したファイル

1行目はお作法、というところで2行目以降の説明をしたいと思います。
「Const constUrl」の行までは、言ってみればこの文字を見る人宛に「こんな機能・こんな意味を持ちますよ」と説明しているだけです。VBScript自体は何も処理していません。
"Const"というフレーズが出てきましたが、これは「このVBScriptの処理が終わるまでは未来永劫変わりませんよ」と宣言しています。
逆に"Dim"から始まるものについては、処理の途中で色々と設定が可能なものになります。
今回は開くページが決まっているので、ConstでURLを設定し、ログインに必要なユーザー名やパスワードは入力してもらう、という感じになります。
基本的に、この「Const」で定義されている箇所を変えるだけで、様々なページのログインが可能になります。

constUrlは、表示したいページのURLを指定します。今回はニコニコニュースのURLを設定していますが、他のURLを指定することでニコニコニュース以外のページを表示することができます。
constLoginName、constPassword、constLoginBtnIdに設定する値は、表示したいページでF12キーを押せば、HTML要素がみられるのでそこから探してみてください。
ここに設定するHTMLドキュメント上割り振られているIDを設定する必要があります。
constLoginNameは上記画像の一番上の四角で囲っている部分で「id="xxxxxxxx"」の部分。ログインに利用しているログインIDやメールアドレスを入力する場所のIDを指定します。
constPasswordは上記画像の真ん中の四角で囲っている部分で「id="xxxxxxxx"」の部分。ログインに利用しているパスワードを入力する場所のIDを指定します。
constLoginBtnIdは上記画像の一番下の四角で囲っている部分で「id="xxxxxxxx"」の部分。ページ上では大体「ログイン」と描かれている場所のIDをしてします。

続けましょう。以降は特に各自で設定する必要はないので、処理の説明となります。
「とりあえず自動化できれば今はいい!後で勉強するよ!!」という方以外はどうぞ次ページ以降をご覧くださいませ。

処理を詳しく見ていこう!

今までは各自で設定しなければならない行を見てきましたが、これからはどんな処理が行われているのかを見ていきたいと思います。

' ============================
' 実際の処理を記述
' ============================
' メイン処理呼出
Call Main()
' おしまい!!!
WScript.Quit(0)

これはもう見た通りですね。
Mainという何かを呼び出して、Quit(終了)しています。
それでは以降の行に進んでいきましょう!

' ++++++++++++++++++++++++++++
' Main Routein
' ++++++++++++++++++++++++++++
' ==========================================================
' 関数名:Main
' 概要  :メイン処理
' 引数  :なし
' 戻り値:なし
' ==========================================================
Private Sub Main()

    ' ログインに必要な情報を設定
    Call SetLoginPassword

    ' 一旦全てのIEブラウザのプロセスを削除
    Call ShutdownIEProcess

    ' IEのみアクティベート
    Call ActivateIEBroobjWser

    ' ログイン実行
    Call ExecuteLogin

End Sub

さあ、見ただけで「?」となるようなものがいっぱいありますね。
ただ、基本的には
①「SetLoginPassword」というものをCallし、
②「ShutdownIEProcess」なるものをCallし、
③「ActivateIEBrowser」という輩をCallし、
④「ExecuteLogin」っていうやつをCallしている
この4つは何となく分かりますね!
それよりも、その前後に記載されている「Private Sub Main()」だとか「End Sub」だとかが今一分かりづらいですよね。
SubとはSubRoutein(サブルーチン)の略です。
1行目にある「Private Sub Main()」とは、「このVBScriptのファイル内だけで使えるMain(メイン処理)という名前のサブルーチン(処理名)である」という事を表しています。
そして、直近のEnd Subと書いてある行までの間を実行してくれるブロックエリアとなります。
では、実際①~④までのサブルーチンの面々がどんな内容なのか見てみましょう!

SetLoginPassword ~ユーザから入力を待ってテキストファイルに書き込む~

それでは、SetLoginPasswordと名付けられたサブルーチンについて、見ていきましょう。
直訳するとログインパスワードをセットするサブルーチンだそうです。

' ++++++++++++++++++++++++++++
' Sub Routein
' ++++++++++++++++++++++++++++
' ==========================================================
' 関数名:SetLoginPassword
' 概要  :ユーザーIDとパスワードを入力してもらう
' 引数  :なし
' 戻り値:なし
' ==========================================================
Private Sub SetLoginPassword()
    ' 初回のみ入力とし、以降は設定ファイルを読み込む
    Dim objWs            ' WshShell
    Dim objFSO           ' FileSystemObject
    Dim objTextFile      ' 入出力ファイル
    Dim strLine          ' login.txtの読込行内容

    ' 読込み対象のファイルを取得するためOSに対するShellオブジェクトを設定
    ' 取得対象は、「C:\Users\<ユーザ名>\Documents」フォルダにあるlogin.txt
    Set objWs = WScript.CreateObject("WScript.Shell")
    text = objWs.SpecialFolders("mydocuments") & "\login.txt"
    ' 情報取得後はobjWsは不要のためメモリから解放
    Set objWs = Nothing

    ' FileSystemObjectを設定
    Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")

    ' まだ1回もログインしていない場合
    If objFSO.FileExists(text) = False Then
        user = InputBox("ユーザーIDを入力してください")
        pass = InputBox("パスワードを入力してください")
        If user = "" Or pass = "" Then
            MsgBox "ユーザーIDとパスワードは正確に入力してください"
            Set objFSO = Nothing
            WScript.Quit(1)
        End If
        ' 書き出しファイルの指定
        Set objTextFile = objFSO.OpenTextFile(text, 2, True)
        objTextFile.WriteLine constUrl & "," & user & "," & pass
        objTextFile.Close
    End If

    ' 過去にログイン済みの場合(login.txtファイルが存在する)
    If user = "" Or pass = "" Then
        Set objTextFile = objFSO.OpenTextFile(text, 1, False, 0)
        strLine = objTextFile.ReadLine

        ' テキストファイルから読み込み、ユーザIDとパスワードを取得する
        user = Split(strLine, ",")(1)
        pass = Split(strLine, ",")(2)
        objTextFile.Close
    End If
    Set objTextFile = Nothing
    Set objFSO = Nothing
End Sub

もう長すぎて見るのも嫌になっちゃいますね。でも、落ち着いて確認すると、意外とやっていることは単純です。
このサブルーチンで見たいのは「If文(条件式)」です。
基本的にVBScriptは上の行から下の行まで処理を実施します。なので、If文は、条件に合った場合にIfからEnd If(あるいはElse If、Else)までの間の処理を実施します。

If文を取っ払ってみると、色々とごちゃごちゃしているこのサブルーチンですが実際にやっていることは3つです。
①ユーザーのドキュメントフォルダを取得する
②ドキュメントフォルダにテキストファイルがあるか確認し、無ければユーザーに2回入力(ユーザーID、パスワードの2つ)してもらう

(※入力してもらった内容はテキストファイルに書き込む)
③テキストファイルから情報を読み取る

ね、簡単でしょ?

ShutdownIEProcess ~Internet Explorerさんおやすみなさい~

ShutdwonIEProcess……直訳するとIE(Internet Explorer)プロセスをシャットダウン。
直訳通り、このサブルーチンではInternet Explorerのプロセスをシャットダウンします。
これはすでに起動されているInternet Explorerがあったらいったんアプリケーションを終了するよ、という処理になります。

' ==========================================================
' 関数名:ShutdownIEProcess
' 概要  :起動しているIEプロセスを削除する
' 引数  :なし
' 戻り値:なし
' ==========================================================
Private Sub ShutdownIEProcess()
    Dim objProcList, objProcess, objRegEx
    Set objProcList = GetObject("winmgmts:").InstancesOf("win32_process")
    Set objRegEx = new RegExp 
    objRegEx.IgnoreCase = True
    objRegEx.pattern = "iexplore.exe"

    For Each objProcess In objProcList
        If objRegEx.Test(objProcess.Name) = True Then
            objProcess.Terminate
        End If
    Next

    Set objProcList = Nothing
    Set objProcess = Nothing
    Set objRegEx = Nothing

End Sub

これもSetだのGetだのnewだの乱立していますが、やりたいことは「For Each」文の中だけです。
①WindowsScriptの機能を使ってプロセスの一覧を取得する
②プロセス名が「iexplore.exe」だったらプロセスを終了する(プロセスの分だけ繰り返す)

おお、実際にやっていることはたった2つです。

ちなみにVBScriptは自分がどのInternetExplorerのプロセスを起動したかを把握できません。なので一旦ゼロベースにしてこの後の処理でInternetExplorerを操作する、という形になります。
今日日InternetExplorerを起動してあれこれしている方は少ないと思いますが、このVBScriptを実行することで作業中のInternetExplorerが閉じてしまうので注意してくださいね。

ActivateIEBroobjWser ~Internet Explorerで自動ログインしたいサイトのURLへアクセス~

さてさて、今までの流れでVBScript上ではInternet Explorerが1つ起動している状況になっているはずです。
そのInternet Explorerで、自動ログインしたいサイトへアクセスするためのサブルーチンとなります。

' ==========================================================
' 関数名:ActivateIEBroobjWser
' 概要  :IEのみをアクティベートする
'         【注意】
'         他にアクティブなアプリケーションがあると一旦
'         最小化されてしまう
'         付箋とかある人は注意!
' 引数  :なし
' 戻り値:なし
' ==========================================================
Private Sub ActivateIEBroobjWser()

    'オブジェクトを格納する変数
    Dim objWin, objIE

    ' 画面をすべて最小化する
    Set objWin = WScript.CreateObject("Shell.Application")
    objWin.MinimizeAll

    '処理したいページを表示
    Set objIE = WScript.CreateObject("InternetExplorer.Application")
    objIE.Visible = True
    objIE.Navigate constUrl
    If Err.Number = 0 Then
        'ページが読み込まれるまで待つ
        Do While objIE.Busy = True Or objIE.readyState <> 4
            WScript.Sleep 100
        Loop
    Else
        MsgBox "うまくページ遷移できませんでした。処理を終了します。"
        ' リソース解放
        Set objIE = Nothing
        Set objWin = Nothing
        WScript.Quit(1)
    End If

    ' リソース解放
    Set objIE = Nothing
    Set objWin = Nothing

End Sub

これも結局、やりたいことは3つだけです。
①InternetExplorerを起動する
②処理したいページ(ログインしたいサイトのページ)を表示する
③ページが読み込まれるまで待つ

If Err.Numberなるものが文中にありますが、InternetExplorer(というよりWEBブラウザでのサイト訪問)はページアクセス時に必ず対象ページが存在するわけではないですよね。
なので、ログインしたいサイトを表示したときにエラーがなかったら、上記のやりたいこと①~③をやる、という風にしています。

ExecuteLogin ~満を持してログイン実行~

ExecuteLogin。もう読んだ通りです。ログインを実行します。

' ==========================================================
' 関数名:ExecuteLogin
' 概要  :ログインを実行する
' 引数  :なし
' 戻り値:なし
' ==========================================================
Private Sub ExecuteLogin()

    'オブジェクトを格納する変数
    Dim objShell, objWindow, objButton

    'シェルのオブジェクトを作成する
    Set objShell = WScript.CreateObject("Shell.Application")

    On Error Resume Next
    For Each objWindow In objShell.Windows
        If TypeName(objWindow.document) = "HTMLDocument" Then
            objWindow.document.getElementById(constLoginName).Value = user
            objWindow.document.getElementById(constPassword).Value = pass
            objWindow.document.getElementById(constLoginBtnId).Click
            WScript.Sleep 10000
            Exit For
        End If
    Next
    On Error Goto 0

    ' 遷移後のURLに「err=1」が含まれている=ログイン失敗
    If InStr(objWindow.document.URL, "err=1") > 0 Then
        ' ログインに失敗したら入力されたテキストファイルは削除する
        MsgBox "ログインに失敗しているようです。もう一度このファイルを実行してください。"
        Dim objFSO
        Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")
        objFSO.DeleteFile text
        Set objFSO = Nothing
        Call ShutdownIEProcess
    End If

    ' リソース解放
    Set objButton = Nothing
    Set objWindow = Nothing
    Set objShell = Nothing

End Sub

ここで注目するのは、「On Error Resume Next~~On Error Goto 0」の文言です。
VBScriptでは基本的に、処理が実行できないようなエラーが発生したときは、その処理タイミングでエラーダイアログを表示して処理を終了してしまいます。
今回の場合、ログイン実施しようとしている時に対象のテキストボックス(ユーザーID・メールアドレスやパスワードを入力するエリア)が存在しなかったりするので、その時に処理を終了されないように「On Error……」のおまじないを使うことで処理続行させています。
では実際やっていることを見ましょう。
①ユーザIDを入力するテキストボックスやエリアにユーザーIDを設定する
②パスワードを入力するテキストボックスやエリアにパスワードを設定する
③ログインボタンをクリックする(画面遷移に時間がかかることも想定して10秒待つ)

(ここまで開いているウィンドウ分だけ繰り返し実施、HTMLドキュメントだった場合に実施)
④ログイン失敗したらメッセージを表示してこのスクリプトで作成したテキストファイルを削除する
なーんだ、簡単ですね。

自動化万歳!!

前知識なく見ていくと嫌にもなりそうなコードも、1行1行落ち着いて見ていけば簡単ですよね。
自動化は怖くない。VBScriptは怖くない。
こんなに長い行のVBScriptも読めるならプログラミングも怖くない!!
マウスを使うのって本当に面倒くさいですよね。
Let’s enjoy your automatic programming!!