今回はブラウザを自動で操作して、Twitterにログインするアプリケーションを作ってみます。
前回はCOMを使いましたが、今回はSeleniumという仕組みを使います。
Seleniumを使うと、IE、Chrome、Firefoxなどのブラウザを自動で動かすことができます。
サンプルとして作るのは以下のようなアプリです。
- アプリ起動するとログインIDとパスワードがあらかじめ入力されており、ボタンを押すと、自動でChrome(ブラウザ)が起動する。
- 起動したブラウザにログインIDとパスワードが勝手に入力されてログインボタンも自動で押される。
プロジェクトとフォームの作成
Visual Studio 2019でプロジェクトを作り、フォームに以下のように2つのTextboxと1つのbuttonを配置します。
NuGetパッケージでselenium関連のライブラリをインストール
最初に、いくつかのライブラリのパッケージをプロジェクトで使えるようにインストールします。
「プロジェクト」⇒「NuGetパッケージの管理」を選択して、管理画面を開きます。
(「インストール済み」タブではなく)「参照」タブを選択して「selenium」で検索すると、関連するパッケージが表示されます。
この中で、以下のパッケージをインストールします。
- Selenium.WebDriver
- Selenium.Support
- Selenium.WebDriver.ChromeDriver
これにより、以下のパッケージが使えるようになります。
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
では、さっそくコーディングしていきましょう。
ブラウザを自動起動する部分の記述
シンプルにChromeを起動して、Twitterのログイン画面に遷移するには以下のコードでOKです。
ChromeDriverをnewするだけでもいいんですが、ブラウザを見えないよう、ステルスモード(ヘッドレスモード)で起動したり、初期表示する位置を指定したりするのに便利なので、ChromeOptions というオプションを渡す形式のコードにしています。
ChromeDriverService service = ChromeDriverService.CreateDefaultService();
service.HideCommandPromptWindow = true;
ChromeOptions options = new ChromeOptions();
//ヘッドレスで、ブラウザを見えないようにするにはこれを使う。
//options.AddArgument("--headless");
IWebDriver driver = new ChromeDriver(service, options);
//driver.Manage().Window.Position = new Point(100, 0);
//Webページを開く
driver.Navigate().GoToUrl("https://twitter.com/login");
NuGetパッケージをインストールして、上記のコードを書くだけで、自動でログイン画面をひらくアプリを作れるなんで、非常に簡単ですね。
要素の探し方と指定方法
さて、自動でログインさせるために、ユーザ名とパスワードの入力項目にどのような名前がついているのかを確認します。
確認して、後程、その要素名で特定して自動で入力させます。
Twitterのログイン画面(https://twitter.com/login)をChromeで開いた後、画面を右クリックします。
そこで「検証」を選択して、開発コンソールを開きます。(下記の図の右側です)
「電話、メールまたはユーザ名」と「パスワード」の部分の要素名を確認します。
それぞれ、session[username_or_email]とsession[password]となっていますね。
これをコードで取得するには、FindElementを使います。
以下のようにすると良いです。
IWebElement username_box = driver.FindElement(By.Name("session[username_or_email]"));
IWebElement password_box = driver.FindElement(By.Name("session[password]"));
要素を取得したら、そこに文字を入力します。SendKeysとすると、キーボードで入力したのと同じになります。
username_box.SendKeys("userName");
password_box.SendKeys("passWord");
次に、ログインボタンを特定してみます。
ボタンの要素を見ると、タグやnameでは特定できそうにないですよね。。
なにか特徴がないか探してみると、「data-testid=”LoginForm_Login_Button”」というのがあります。
そこで、「data-testidという属性に”LoginForm_Login_Button”という文字列を含むdivタグ」という条件で取得することにしました。
IWebElement login_button = driver.FindElement(By.XPath("//div[contains(@data-testid,'LoginForm_Login_Button')]"));
これでログインボタンの要素を取得し、クリックさせることでログインすることができます。
要素がロードされるまで待機する方法
何度かテストすると分かると思いますが、ログイン画面が完全にロードされないうちに入力させたり、ボタンを押したりすると、要素が見つからないエラーになってしまいます。
そこで、ロードされるのを待つ必要があります。
そのための良い方法は「想定している要素が存在するかどうかを定期的にチェックして、なければ待機し、あれば、次の処理に移る」という方式です。
以下のようにすると、”session[username_or_email]”という要素が存在するまで、最大30秒待ちます。
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30));
wait.Until(SeleniumExtras.WaitHelpers.ExpectedConditions.ElementIsVisible(By.Name("session[username_or_email]")));
上記の使っているExpectedConditionsは現在(2020年)では非推奨となっています。
代わりに以下のコード(ラムダ式)を使うと良いでしょう。
var wait = new WebDriverWait(driver, new TimeSpan(0, 0, 30));
var element = wait.Until(condition =>
{
try
{
var elementToBeDisplayed = driver.FindElement(By.Name("session[username_or_email]"));
return elementToBeDisplayed.Displayed;
}
catch (StaleElementReferenceException)
{
return false;
}
catch (NoSuchElementException)
{
return false;
}
});