今更だけど Selenium のド初心者が Selenium を触ってみる件

##前説。
パソコンでの操作を自動化する、ということが流行ってるらしいです。・・・が、その自動化させるための色んな情報が集め難いっていう状況になっています。
微妙に動かなかったり、初心者向けって書かれてるサイトでも微妙に内容が抜けてたり。
そんなわけで今回は「Selenium」に関して少し書いてみたいと思います。

##作ろうとしている物。
さて「Selenium」を使ってどんなアプリというか自動化の仕組みを作ろうとしているのか？　というと以下のようになります。まあ単純なんですけどね。

1. 1行が1URLのテキスト情報を読み込む
2. 読み込んだURLに対してEdgeでアクセスする
3. アクセスしたら色々する
4. 色々した結果をURLに対応したファイルに保存する
5. 項番の 1 で取得したで全てのURLで 2 から 4 の処理を実施する
6. おわり！ 

##事前準備。
次のアプリを用意します。

* Windows10
* Microsoft Edge Ver.94.0.992.50
* Visual Studio Community 2019

で、おおむねアプリに関してはデフォルト状態で良いと思います。特にWin10とEdge。
そんな状況でVisualStudioを起動したら新規プロジェクトをこさえます。今回は C# を使ってますが（というか私が C# しか判らない）、VBとかでも似た方法でできるんじゃないかなー？　なんて無責任に思ったりはしてます。

##NuGetで設定を行う。
C#の「Windows フォーム アプリケーション(.NET Framework)」プロジェクトを作ったら「NuGet パッケージ マネージャー」を起動して次の4つを組み込みます。特にバージョン選択は注意が必要っぽいです。他の組み合わせでも動くとは思うけど私はこれで動いたので、まあそのあたりの空気は読んでくれると嬉しいかな？
ちなみに"MSEdgeDriver"は"Edge"のバージョンと合わせないといけないそうです。将来的にEdgeのバージョンが勝手に上がったら下位互換的なので吸収してくれるんですかね？

* DotNetSeleniumWxtras.WaitHelpers v3.11.0
* Selenium.Support v4.0.0-alpha07
* Selenium.WebDriver v4.0.0-alpha07
* Selenium.WebDriver.MSEdgeDriver v94.0.992.38

追加が終わったら次は実際に書いていきます。

##フォームに色々配置する。
フォームアプリなのでまずはForm上にボタンとかを色々配置させます。
とはいっても難しいことはなく一つのtextBox（マルチライン）とボタンが二つあってそれぞれ「処理開始」と「終了」という非常に簡単なつくりです。
![コメント 2021-10-21 115433.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156763/aed15219-578f-0986-1aab-1fee9b7e2205.png)

##基本部分のコードを書く。
フォームが完成したら次はコードを書いていくことになります。

```c#:Form1.cs
using System.IO;
using OpenQA.Selenium.Edge;
```

を追加します。いろんなサイトを見て回っても「こんなの当然じゃん」とばかりに using の記述が省かれてて「ざけんなっ！」ってなったりしてましたが、それは置いといて実処理を書いてみましょうか。

```c#:Form1.cs
private void OpenEdge()
{
    try
    {
        // ドライバー起動時に表示されるコンソール画面を非表示にする
        var service = EdgeDriverService.CreateChromiumService();
        service.HideCommandPromptWindow = true;

        // EdgeChromium版を使用
        var options = new EdgeOptions();
        options.UseChromium = true;

        var driver = new EdgeDriver(service, options);

        // サイトを開く
        driver.Navigate().GoToUrl("https://www.google.com/");

    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
}
```

まあ、下に参考させて頂いたサイトの丸コピペなんですけどね（下記参照）。
ちなみにこれで実行するとフォームが開いて、フォーム上にある「処理開始」をクリックするとなんとびっくりEdgeが起動して [Google](https://www.google.com/) にアクセスしてしまうという・・・！

##でもGoogleにしかアクセスできないやん？
そうなんです！　この状態だとGoogleにしかアクセスできません。
まあ、ソース内でハードコーティングされてるのだから当然ですよね。ということで textBox1 に入力されたURLに対してアクセスできるように次のようなスースを書き足してみましょう。

```c#:Form1.cs
// textBox1 の値を取得する。
string inputURL = textBox1.Text;
```
超簡単ですね。あ、埋め込む部分は実際にサイトを開く直前辺りが良いと思います。

##URLリストを分解する。
textBox1 に入力されたURLにアクセスできるようになればこっちのもんです。
ただ、こちらのやりたいことはURLリスト（1行に1つのURL）を textBox1 に入れてボタンを押したらそのリストに載っている全部のURLに順番にアクセスする事。
ということで、大本となるURLリストを次のようにしました。

https://www.google.com/
https://www.yahoo.co.jp/
https://ja.wikipedia.org/

実際にはどこでもいいんですが・・・　で、このリストは inputText という変数に入ります。
そして、これを改行コードをキーとして配列にする必要があります。これもある意味簡単で、上の値を取得と合わせて次のように変更したりしちゃいます。これでURLは inputRex という配列にすべて入ることになりました。

```c#:Form1.cs
string[] inputURL = textBox1.Text.Split(new string[] { "\r\n" }, StringSplitOptions.None);
```

ということで、入力されたリストを改行で区切りつつ3つのURLに分解できたので、次はそれにアクセスしていく必要があります。

##分解された数はいくつだ！
プログラムが特定の回数だけ処理を繰り返す、となると想像できるのは for文 なのではないでしょうか？

```c#:Form1.cs
// 配列に入ってるURLの回数分繰り返す
for (int i = 0; i < inputURL.Length; i++)
{
    // 実際の処理
}
```

簡単に書くとこんな感じですね。ただ、ここまでの部分と合わせて少し順番などを修正しないといません。なにせ private void の直下でいきなり try{} をやってその中で for{} を作ってますからね。
それを踏まえたうえで次のようにネスト具合を変更しました。

```c#:Form1.cs
// 実際の「処理開始」ボタンからの処理
private void OpenEdge()
{

    // 改行で分割
    string[] inputURL = textBox1.Text.Split(new string[] { "\r\n" }, StringSplitOptions.None);
    // 配列に入ってるURLの回数分繰り返す
    for (int i = 0; i < inputURL.Length; i++)

    {
        try
        {

            // ドライバー起動時に表示されるコンソール画面を非表示にする
            var service = EdgeDriverService.CreateChromiumService();
            service.HideCommandPromptWindow = true;

            // EdgeChromium版を使用
            var options = new EdgeOptions();
            options.UseChromium = true;

            var driver = new EdgeDriver(service, options);

            // サイトを開く
            driver.Navigate().GoToUrl(inputURL[i]);

        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
    }
}
```

ということでこれを実行してみると、順番に Google と Yahoo と Wikipedia に対してブラウザが起動してアクセス可能に。
でもちょっと待って！
ただし、例えばURLが250個だとブラウザは250回も起動するとってことになる。さすがに最近はマシンスペックが上がってるとはいえ、25個ならまだしも250個は少しきつい・・・　ということで Selenium からブラウザを閉じる命令を入れてみます。
焦らないでくださいな、これもとても簡単です。

```c#:Form1.cs
driver.Close();
```

## むすんでひらいて。
指定されたURLを新しいウィンドウで開き、特定の処理をした後にそのウィンドウを閉じ、次のURLをまた新しいウィンドウで開く・・・　ここまでが出来たら、そのウィンドウを閉じるまでにする処理を書いていきたいと思います。
上の方では「アクセスしたら色々する」とか「色々した結果をURLに対応したファイルに保存する」なんて書いてますので、実際の内容を書くと色々あれだから端折りますが、今回は各URLでアクセスしたページからTITLEタグの要素をファイル名に、URLをテキストファイルの中身として保存する、なんてのを書いてみたいと思います。

そうしたらまずは Title を取得しなければ始まりません。公式ドキュメントを見ると driver.Title という命令で取得できるようなので次の通り書いて・・・

```c#:Form1.cs
string pageTitle = driver.Title;
```

これで取得が出来ました。同じようにURLですが、アクセスするURLは既にわかってるんだからそれ持って来ればいいじゃん、というのもつまらないのできちんと処理をしたいと思います。

```c#:Form1.cs
string pageURL = driver.Url;
```

両方の要素が取得できたのでこれをファイルに書き込みましょう。
とはいっても難しいことはないです。

```c#:Form1.cs
// データ保存用のファイルを作る
using (System.IO.FileStream hStream = System.IO.File.Create(@"C:\Temp\" + pageTitle + ".txt"))
{
    // 作成時に返される FileStream を利用して閉じる
    if (hStream != null)
    {
        hStream.Close();
    }
}

// 作ったファイルに文字コードシフトJISで書き込む
Encoding enc = Encoding.GetEncoding("Shift_JIS");
StreamWriter streamWriter = new StreamWriter(@"C:\Temp\aa\" + pageTitle + ".txt", false, enc);
streamWriter.Write(pageUrl);
streamWriter.Close();
```

こんな感じです。今回の場合は C:\Temp\aa フォルダの中にShift_JISでファイルを書き込んでます。

##できあがり。
はい、そんな訳で Selenium を使って超簡単にEdgeを操作して特定の値を取得して、取得した値をファイルの中に書き込んでみるという処理を作ってみました。
全体像を見てみると次のような感じになります。

```c#:Form1.cs
using OpenQA.Selenium.Edge;
using System;
using System.IO;
using System.Text;
using System.Windows.Forms;

namespace hpeWebC
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        // 「処理開始」ボタンを押したときのおまじない
        private void Button1_Click(object sender, EventArgs e)
        {
            OpenEdge();
        }

        // 実際の「処理開始」ボタンからの処理するおまじない
        private void OpenEdge()
        {

            // 改行で分割するおまじない
            string[] inputURL = textBox1.Text.Split(new string[] { "\r\n" }, StringSplitOptions.None);

            // 配列に入ってるURLの回数分繰り返すおまじない
            for (int i = 0; i < inputURL.Length; i++)
            {

                // 実行する合図のおまじない
                try
                {

                    // ドライバー起動時に表示されるコンソール画面を非表示にするおまじない
                    var service = EdgeDriverService.CreateChromiumService();
                    service.HideCommandPromptWindow = true;

                    // Chromium版のEdgeを使うおまじない
                    var options = new EdgeOptions();
                    ;// ダミー行のおまじない
                    options.UseChromium = true;
                    var driver = new EdgeDriver(service, options);

                    // サイトを開くおまじない
                    driver.Navigate().GoToUrl(inputURL[i]);

                    // ページのタイトルとURLを取得するおまじない
                    string pageTitle = driver.Title;
                    string pageUrl = driver.Url;

                    // データ保存用のファイルを作るおまじない
                    using (System.IO.FileStream hStream = System.IO.File.Create(@"C:\Temp\" + pageTitle + ".txt"))
                    {
                        // 作成時に返される FileStream を利用して閉じるおまじない
                        if (hStream != null)
                        {
                            hStream.Close();
                        }
                    }

                    // 作ったファイルに文字コードシフトJISで書き込むおまじない
                    Encoding enc = Encoding.GetEncoding("Shift_JIS");
                    StreamWriter streamWriter = new StreamWriter(@"C:\Temp\aa\" + pageTitle + ".txt", false, enc);
                    streamWriter.Write(pageUrl);
                    streamWriter.Close();

                    // ブラウザを閉じるおまじない
                    driver.Close();

                }

                // エラーが起きたときのおまじない
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }

            }
        }

        // 「終了」ボタンを押したときのおまじない
        private void Button2_Click(object sender, EventArgs e)
        {
            this.Close();
        }

    }
}
```

##お付き合いに感謝。
最後までお付き合いいただきありがとうございました。
これが少しでもSelenium初心者の役に立ってくれれば・・・　と思っています('ω')ノ

##参考など（最大に感謝）
* [ブラウザー操作 | Selenium](https://www.selenium.dev/ja/documentation/webdriver/browser_manipulation/)
* [【初心者用】C#でChromium版MicrosoftEdgeを動かしてみた](https://qiita.com/miki_emon/items/5b244d0a94888d8f54e4) 
* [StreamWriterクラスを使ってテキストファイルにデータを書き込む [C#]](https://johobase.com/streamwriter-class-writetext-csharp/)