C# 自動ログイン Selenium実践編

#概要#
人間がブラウザ操作するようにコーディングが可能な__WebDriver（Selenium）__を利用して自動ログインを実装。
特に今回はChromeDriverを利用します。


**WebDriverのメリット**
ユーザーの操作感に近いコーディングができるため汎用性が高い。ブラウザ上で人が操作できることは、ほとんどできてしまうため慣れれば本当に何でもできる。
**WebDriverのデメリット**
ブラウザの進歩が目覚ましくバージョン依存、ブラウザ依存しやすいため不特定多数が利用するようなソフトの提供はおこなえない。また、ブラウザを自動操作するためページの遷移にはどうしてもロード時間がかかってしまう。通信環境、サイトの容量などによってはページの読み込みに時間がかかりすぎて自動処理とは思えない処理時間が発生することもしばしば。

#サイトの画面#



![login.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510936/5820619b-7820-b7f3-d81d-88b4a6946118.png)




HTML全体のうちformタブを見て、

__「ユーザーIDの入力フォーム」__
__「パスワードの入力フォーム」__
__「ログインボタン」__

がどこに該当するかを調べる。

Chromeブラウザならサイトの適当な場所で右クリックを押して「検証」からソースコードとサイトページの画面が表示されコードとページの関連がリアルタイム表示される。パスワードなどのサーバーに送信する必要がある部分はすべて"<"form">"タグ内に格納されている。


```sample_form.html
<!--ログイン画面HTMLの"form"タブ抜粋-->

<div class="login_container_base">
            <div class="login_container">
                <div class="head">
                    LOGIN
                </div>
                <div class="main">
                    <button class="change_type_button" style="display:none;">MJM-ID</button>
                    <button class="change_type_button" disabled >MJM-ID</button>
                    <button class="change_type_button" id="chage_segaid_button">SEGA ID</button>
                    <button class="change_type_button" disabled style="display:none;">SEGA ID</button>
                    <div class="window">
                        <div class="title">
                            MJM-IDでLOGIN
                        </div>
                        <div class="contents">
<!--C#　driver.FindElement(By.Id("loginForm"));で取り出した要素群----------from------------------------------->
                            <form class="login_form" action="" method="POST" id="loginForm" >
                                <div class="title">
                                    MJM-ID
                                </div>

                                <div class="unit">
                                    <input type="text" name="mjm_id" value="" maxlength="20" autocomplete="off">
                                </div>
                                <div class="title">
                                    パスワード
                                </div>
                                <div class="unit">
                                    <input type="password" name="password" value="" maxlength="20" autocomplete="off">
                                </div>
                                <div class="title">
                                    プレイデータ
                                </div>
                                <div class="unit">
                                    <label class="label_radioa" for="ios">
                                        <input type="radio" name="platform" id="ios" value="1">
                                        iOS </label>
                                    <label class="label_radioa" for="pc">
                                        <input type="radio" name="platform" id="pc" value="2" checked="checked">
                                        PC/Android </label>
                                </div>
                                <div class="unit">
                                    <input type="button" id="login" value="ログイン">

                                </div>
                            </form>
<!---------until--------------------------------------------------------------------------------------------->
                        </div>
                        <!--contents-->
                    </div>
                    <!--window-->
                    <div class="window" style="display:none;">
                        <div class="title">
                            SEGA IDでログイン
                        </div>
                        <div class="contents">
                            <form class="login_form" action="" method="POST" id="segaidLoginForm" >
                                <div class="title">
                                    SEGA ID
                                </div>
                                <div class="unit">
                                    <input type="text" name="sega_id" value="" maxlength="32" autocomplete="off">
                                </div>
                                <div class="title">
                                    パスワード
                                </div>
                                <div class="unit">
                                    <input type="password" name="password" value="" maxlength="16" autocomplete="off">
                                </div>
                                <div id="option_tag" style="display:none;">
                                    <div class="title">
                                        画像認証
                                    </div>
                                    <div class="unit" id="auth_image">
                                    </div>
                                    上に表示されている文字を全角ひらがなで入力してください<br>
                                    <div class="unit">
                                        <input type="text" name="imagetext" value="" maxlength="5" autocomplete="off">
                                    </div>
                                </div>

                                <div class="title">
                                    プレイデータ
                                </div>
                                <div class="unit">
                                    <label class="label_radioa" for="ios">
                                        <input type="radio" name="platform" id="ios" value="1">
                                        iOS </label>
                                    <label class="label_radioa" for="pc">
                                        <input type="radio" name="platform" id="pc" value="2" checked="checked">
                                        PC/Android </label>
                                </div>
                                <div class="unit">
                                    <input type="button" id="segaid_login" value="ログイン">
                                </div>
                            </form>
                        </div>
                        <!--contents-->
                    </div>
                    <!--window-->

                </div>
                <!--main-->

                <div class="main">
                      <div class="back">
                          ホームへ
                      </div>
                </div>
                <!--back-->

            </div>
        </div>
```

注意が必要なのがこのサイトではログイン方法が２通りあり通常IDとSEGAIDのどちらでもログインできる仕様のため、似たような名前のformタブが存在することである。
WebDriverを使ってid="loginForm"となる要素のみを取り出したのち、名前が"mjm_id"、"password"になっている要素を取り出す。


抜き出したHTMLの要素を簡素化

```input.html
<form class="login_form" action="" method="POST" id="loginForm" >
　<input type="text" name="mjm_id" value="" maxlength="20" autocomplete="off">
　<input type="password" name="password" value="" maxlength="20" autocomplete="off">
　<input type="button" id="segaid_login" value="ログイン">
</form>
```
＜HTMLについて＞
__input__は”タグ”であり”ノード”と呼ばれるものである。

__type,name,value,Id__などは”タグの属性”を表しタグによってさまざまな属性がある。例えばtypeは機能を指定する属性でサイト内でボタン(button)だったり、入力フォーム(text)だったりする。

__name__はあるタブを機能ごとや値毎にユーザーが任意に分類するための名称（分類のための命名であり唯一ではない）

__Id__は属性の中で唯一性があるためピンポイントで呼び出す必要があるタグに付与する。１つの"<"html">""</"html">"で囲まれた中に重複Idは許されない。



```samplecode.cs
         string LOGIN_ID = "mjm54XXXXXX";
         string PASSWORD = "cTF8hj3XXx";

　　　　　　　　　//　HTMLタグ内のIdから要素取得
                IWebElement element = driver.FindElement(By.Id("loginForm"));
               //　取得した要素のうちnameを指定してログインIDとPASSWORを入力      
                element.FindElement(By.Name("mjm_id")).SendKeys(LOGIN_ID);
                element.FindElement(By.Name("password")).SendKeys(PASSWORD);

                //　ログインボタンをクリック
                driver.FindElement(By.Id("login")).Click();
```

＜C#について＞
__SendKeys()__は、取り出した要素に値を入力するメソッド。引数は入力したい値（Value="入力した値"）
__Click()__は、取り出した要素をクリックするメソッド。

__WebDriverWait__　～pagewaitの中の処理～
Seleniumではページ遷移する場合、ページの読み込み待ちを効率的におこなうためにSelenium.Supportがリリースされている。
これはサイトページのロード状況からwaitがかけれるようになりページのロード前に要素を取得しようとしてエラーとなることを避ける処理である。他のサイトではThread.Sleep(5000)などを入れて十分な読み込み時間を待機したのち動作するような無理矢理に安定動作をさせるといった行為がみられたがまったく高速化できないため推奨しない。


実際のスクリプト全体（C#）

```sample.cs
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;

namespace CsSeleniumSample
{
    class Program
    {
        static void Main(string[] args)
        {
         
         string LOGIN_ID = "mjm54XXXXXX";
         string PASSWORD = "cTF8hj3XXx";
　　　　　//ログイン画面
         string URL_LOGIN = "https://pl.sega-mj.com/players/login";
　　　　　//プレイ履歴画面（ログイン後、アカウントメニューから開きたいページ）
         string URL_Mydata ="https://pl.segamj.com/playdata_view/showHistory";

         ChromeDriver driver = null;


         try{
                //ChromeDriverオプション設定
                ChromeOptions op = new ChromeOptions();
                op.PageLoadStrategy = PageLoadStrategy.Normal;
                //ChromeDriverを起動
                driver = new ChromeDriver(op);
                //　ログインページを開く
                driver.Url = URL_LOGIN;
                //　HTMLタグ内のIdから要素取得
                IWebElement element = driver.FindElement(By.Id("loginForm"));

                //　取得した要素のうちnameを指定してログインIDとPASSWORを入力      
                element.FindElement(By.Name("mjm_id")).SendKeys(LOGIN_ID);
                element.FindElement(By.Name("password")).SendKeys(PASSWORD);

                //　ログインボタンをクリック
                driver.FindElement(By.Id("login")).Click();
                pagewait(driver);

                //　ログイン後に開きたいURLへジャンプ
                driver.Navigate().Refresh();
                pagewait(driver);


                //　ログイン後に開きたいURLへジャンプ
                driver.Navigate().GoToUrl(URL_Mydata);
                pagewait(driver);

            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            //HTML文字列の確認
　　　　　　Console.WriteLine(driver.PageSource);

           Console.ReadKey();


            //ブラウザを閉じる。
            driver.Quit();

        }

        static void pagewait(ChromeDriver chro)
        {
            //</html>を見つけるまで待機
            try
            {
                WebDriverWait wait = new WebDriverWait(chro, TimeSpan.FromSeconds(0.01));
                IWebElement firstResult = wait.Until(e => e.FindElement(By.XPath("</html>")));

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

        }

  }//End class Program
}


```

#開発環境#
ブラウザ：Chrome  91.0.4472.77 (2021/6/2時点の最新)
言語：C# 
使用ソフト：VisualStudio2019v16.6.0
プロジェクト：コンソールアプリ(.NET Framework)
.NET Framework Version 4.8.04084
NuGetパッケージ
 @Selenium.Support v3.141.0
 @Selenium.WebDriver v3.141.0
 @Selenium.WebDriver.ChromeDriver v91.0.4472.1900

#参照URL#
SEGA NET麻雀MJ　ログイン
https://pl.sega-mj.com/players/login
SEGA NET麻雀MJ　プレイ履歴
https://pl.sega-mj.com/playdata_view/showHistory