SeleniumによるChrome自動操作でTwitterにログインするサンプルコード

今回はブラウザを自動で操作して、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がdeprecated(非推奨)と表示される場合の対処法

上記の使っている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;
}
});