覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。twitter: @yyoshikaw

Selenide - Webサイトへアクセスする

以前、Selenium と Chrome (ChromeDriver)を使用してWebサイトへのアクセスを確認しました。


Selenium を使うと手数が増えてしまうようなので、各プログラム言語ごとにSeleniumのラッパーが存在しています。

Java用のライブラリをとして存在している Selenium のラッパーの一つが Selenide です。

Selenideの読み方は、セレニド or セレナイド のようです。


Selenide を使った方がコード量は圧倒的に減りそうです。

とりあえず、Selenideを使ってWebページへアクセスするところまで試しました。


Javaライブラリの準備

pom.xml に以下のように selenide の依存関係を追加します

        <dependency>
            <groupId>com.codeborne</groupId>
            <artifactId>selenide</artifactId>
            <version>6.3.4</version>
        </dependency>

本サンプルでは バージョン 6.3.4 を指定しています。


ソースコード例

ヘッドレスモードにはしていません。

以下はChrome で Googleのページを開いて検索を実行しているというサンプルです。

import com.codeborne.selenide.Configuration;
import org.openqa.selenium.chrome.ChromeOptions;

import static com.codeborne.selenide.Selectors.*;
import static com.codeborne.selenide.Selenide.$;
import static com.codeborne.selenide.Selenide.*;
import static com.codeborne.selenide.WebDriverRunner.getWebDriver;

public class TrialSelenide1 {

    public static void main(String[] args) {
        Configuration.baseUrl = "https://www.google.com";
        Configuration.browser = "chrome";
        Configuration.screenshots = false;
        Configuration.holdBrowserOpen = true; // ここでは処理後にあえてブラウザを閉じないように設定しています。

        // "Chromeは自動テストソフトウェアによって制御されています" の表示を消す (消さなくても害はないです)
        ChromeOptions options = new ChromeOptions();
        options.setExperimentalOption("useAutomationExtension", false);
        options.setExperimentalOption("excludeSwitches", new String[] { "enable-automation" });
        Configuration.browserCapabilities.setCapability(ChromeOptions.CAPABILITY, options);

        // Googleを開く
        open("");

        // ウィンドウの最大化(やる必要は特にないです)
        getWebDriver().manage().window().maximize();

        // Googleで検索を実行
        $(byName("q")).val("覚えたら書く blog.y-yuki.net").pressEnter();
    }

}

ここには、画面キャプチャ等の実行結果は貼り付けていませんが、
上記コードで無事にGoogleのページが開いて検索が実行され検索結果のページが表示されることを確認しました。


まとめ

非常に簡単な例ですが、Selenide で対象のWebページを開いて操作ができました。

Java - ある月の最初の〇曜日・最後の〇曜日 の日付を取得したい

例えば、ある月の最初の水曜日の日付を取得したい、ある月の最後の水曜日の日付を取得したい。
というケースが出てくる場合があります。

それを Java でやるサンプルです。

こういった場合は、java.time.temporal.TemporalAdjusters クラスを利用するのがよいです。

TemporalAdjusters で提供されているメソッドで日付を調整して目的の日付を導き出すのがよいと思います。


ある月の最初の〇曜日の日付を取得する

流れとしては、
A) 起点となる日付を設定 → B) その月の最初の日を取得 → C) 最初の日以後で最初に対象の曜日が出てくる日付を取得
という感じになります

C) の "最初の日以後" には 月の最初の日も含まれます(=月の最初の日が目的の曜日なら、その日付を取得する)

上記の処理の流れの B, C は以下のようにして実現できます

  • B: その月の最初の日を取得
    • TemporalAdjusters.firstDayOfMonth を使用することで実現できます。
  • C: 最初の日以後で最初に対象の曜日が出てくる日付を取得
    • 月の最初の日の日付を起点にして TemporalAdjusters.nextOrSame を使用することで実現できます


コードサンプルは以下のようになります。

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

import static java.time.temporal.TemporalAdjusters.firstDayOfMonth;
import static java.time.temporal.TemporalAdjusters.nextOrSame;

public class Sample1 {

    public static void main(String[] args) {
        // 起点となる日付(ここでは現在日付)
        LocalDate baseDate = LocalDate.now();

        // 対象の月の最初の日付
        LocalDate firstDayOfMonth = baseDate.with(firstDayOfMonth());

        LocalDate firstWedDayOfMonth = firstDayOfMonth.with(nextOrSame(DayOfWeek.WEDNESDAY));
        LocalDate firstThDayOfMonth = firstDayOfMonth.with(nextOrSame(DayOfWeek.THURSDAY));
        LocalDate firstFrDayOfMonth = firstDayOfMonth.with(nextOrSame(DayOfWeek.FRIDAY));
        LocalDate firstMoDayOfMonth = firstDayOfMonth.with(nextOrSame(DayOfWeek.MONDAY));

        System.out.println(baseDate.format(DateTimeFormatter.ofPattern("yyyy年M月")) + " の最初の日付は -> " + firstDayOfMonth);

        System.out.println(baseDate.format(DateTimeFormatter.ofPattern("yyyy年M月")) + " の最初の水曜日の日付は -> " + firstWedDayOfMonth);
        System.out.println(baseDate.format(DateTimeFormatter.ofPattern("yyyy年M月")) + " の最初の木曜日の日付は -> " + firstThDayOfMonth);
        System.out.println(baseDate.format(DateTimeFormatter.ofPattern("yyyy年M月")) + " の最初の金曜日の日付は -> " + firstFrDayOfMonth);
        System.out.println(baseDate.format(DateTimeFormatter.ofPattern("yyyy年M月")) + " の最初の月曜日の日付は -> " + firstMoDayOfMonth);
    }

}


■実行結果 (の例)

(ここでは最初の起点となっている日付が 2022年3月になっています)

2022年3月 の最初の日付は -> 2022-03-01
2022年3月 の最初の水曜日の日付は -> 2022-03-02
2022年3月 の最初の木曜日の日付は -> 2022-03-03
2022年3月 の最初の金曜日の日付は -> 2022-03-04
2022年3月 の最初の月曜日の日付は -> 2022-03-07

2022年3月の最初の水曜日、木曜日、金曜日、月曜日の日付が取得できています。


ある月の最後の〇曜日の日付を取得する

流れとしては、
A) 起点となる日付を設定 → B) その月の最終日を取得 → C) 最終日以前で最初に対象の曜日が出てくる日付を取得
という感じになります

C) の "最終日以前" には 最終日も含まれます(=月の最終日が目的の曜日なら、その日付を取得する)

上記の処理の流れの B, C は以下のようにして実現できます

  • B: その月の最終日を取得
    • TemporalAdjusters.lastDayOfMonth を使用することで実現できます。
  • C: 最終日以前で最初に対象の曜日が出てくる日付を取得
    • 月の最終日の日付を起点にして TemporalAdjusters.previousOrSame を使用することで実現できます


コードサンプルは以下のようになります。

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

import static java.time.temporal.TemporalAdjusters.lastDayOfMonth;
import static java.time.temporal.TemporalAdjusters.previousOrSame;

public class Sample2 {

    public static void main(String[] args) {
        // 起点となる日付(ここでは現在日付)
        LocalDate baseDate = LocalDate.now();

        // 対象の月の最終日
        LocalDate lastDayOfMonth = baseDate.with(lastDayOfMonth());

        LocalDate lastWedDayOfMonth = lastDayOfMonth.with(previousOrSame(DayOfWeek.WEDNESDAY));
        LocalDate lastThDayOfMonth = lastDayOfMonth.with(previousOrSame(DayOfWeek.THURSDAY));
        LocalDate lastFrDayOfMonth = lastDayOfMonth.with(previousOrSame(DayOfWeek.FRIDAY));
        LocalDate lastMoDayOfMonth = lastDayOfMonth.with(previousOrSame(DayOfWeek.MONDAY));

        System.out.println(baseDate.format(DateTimeFormatter.ofPattern("yyyy年M月")) + " の最終日の日付は -> " +  lastDayOfMonth);

        System.out.println(baseDate.format(DateTimeFormatter.ofPattern("yyyy年M月")) + " の最終水曜日の日付は -> " +  lastWedDayOfMonth);
        System.out.println(baseDate.format(DateTimeFormatter.ofPattern("yyyy年M月")) + " の最終木曜日の日付は -> " +  lastThDayOfMonth);
        System.out.println(baseDate.format(DateTimeFormatter.ofPattern("yyyy年M月")) + " の最終金曜日の日付は -> " +  lastFrDayOfMonth);
        System.out.println(baseDate.format(DateTimeFormatter.ofPattern("yyyy年M月")) + " の最終月曜日の日付は -> " +  lastMoDayOfMonth);
    }

}


■実行結果 (の例)

(ここでは最初の起点となっている日付が 2022年3月になっています)

2022年3月 の最終日の日付は -> 2022-03-31
2022年3月 の最終水曜日の日付は -> 2022-03-30
2022年3月 の最終木曜日の日付は -> 2022-03-31
2022年3月 の最終金曜日の日付は -> 2022-03-25
2022年3月 の最終月曜日の日付は -> 2022-03-28

2022年3月の最終の水曜日、木曜日、金曜日、月曜日の日付が取得できています。


まとめ

java.time.temporal.TemporalAdjusters クラスを利用することで無事にもとめたい日付が取得できました。
TemporalAdjusters には他にも有用なメソッドがあるようなので、色々と試してみたいとこです。



関連エントリ

Windows 11 - エクスプローラーの初期表示フォルダをPCにする

Windows 11 でエクスプローラを開くと初期表示フォルダが クイックアクセス になっていますが、
初期表示フォルダを PC に変更したいケースもあります。

そのような場合は以下手順で出来ます。


1) エクスプローラを開きます。

2) 右上の ・・・ となっている部分をクリックして、「オプション」を選択します

f:id:nini_y:20220312031729p:plain


3)「エクスプローラで開く」のドロップダウンで "PC" を選択して 「OK」ボタンをクリックします。

f:id:nini_y:20220312031920p:plain


これで、エクスプローラを起動した際の初期表示フォルダが PC になります。

Selenium - WebDriverを使用してWebサイトへアクセスする(Java + Chrome)

Selenium の WebDriver(ChromeDriver) で Weサイトへアクセスするサンプルです。

Selenium といえば、Python を利用している例をよく目にしますが、ここでは Java の例となっています。


前提条件

実行環境に ChromeDriver がインストールされており、Pathに追加されている前提としています。


Javaライブラリの準備

pom.xml に以下のように selenium-java の依存関係 を追記します。

    <dependencies>

        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>4.1.2</version>
        </dependency>

    </dependencies>

本サンプルでは バージョン 4.1.2 を指定しています。


ソースコード例

全てヘッドレスモードにはしていません

とりあえずWebサイトをChromeで開く

ChromeDriver で、この Blog の URL を開くという単純なサンプルです。

■ソースコード例

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

public class OpenBrowser {

    public static void main(String[] args) {
        // これでブラウザが開く
        WebDriver driver = new ChromeDriver();

        // 指定のURLにアクセスする(開く)
        driver.get("https://blog.y-yuki.net/");

        // 画面を一定時間表示させるために仮で 5000 ミリ秒 sleepしている(ここは本筋と関係ない部分です)
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
        }

        // WebDriverの終了(Webブラウザが閉じる)
        driver.quit();
    }
}

ChromeDriver のインスタンス化 で Webブラウザが起動し、WebDriver.get で 指定のURL へアクセスします。


オプションを指定してWebサイトをChromeで開く

ChromeOptions で、いくつかオプションを指定してこの Blog の URL を開くというサンプルです。

■ソースコード例

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

public class OpenBrowser3 {

    public static void main(String[] args) {

        ChromeOptions options = new ChromeOptions();

        // "Chromeは自動テストソフトウェアによって制御されています" の表示を消す
        options.setExperimentalOption("excludeSwitches", new String[] { "enable-automation" });

        // パスワード保存のポップアップを表示しないようにする
        Map<String, Object> prefs = new HashMap<>();
        prefs.put("credentials_enable_service", false);
        prefs.put("profile.password_manager_enabled", false);
        options.setExperimentalOption("prefs", prefs);

        // "この接続ではプライバシーが保護されません" を突破するためのオプション
        options.setCapability("acceptInsecureCerts", true);

        // これでブラウザが開く
        WebDriver driver = new ChromeDriver(options);

        driver.manage().window().maximize();    // ブラウザの最大化
        driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));  // 要素が見つかるまでの待機時間

        // 指定のURLにアクセスする(開く)
        driver.get("https://blog.y-yuki.net/");

        // 画面を一定時間表示させるために仮で 5000 ミリ秒 sleepしている(ここは本筋と関係ない部分です)
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
        }

        // WebDriverの終了(Webブラウザが閉じる)
        driver.quit();
    }
}
  • コード中にコメントしていますが、以下のオプションを指定しています。
    • "Chromeは自動テストソフトウェアによって制御されています" を Webブラウザ上に表示しないようにしています
    • パスワード保存のポップアップを表示しないようにしています
    • 自己証明書を使用しているWebサイトへアクセスした際の "この接続ではプライバシーが保護されません" を突破するようにしています

(ただし、これらのオプションは今回アクセスしているURLではほとんど意味をなしていません)


まとめ

とりあえず、ChromeDriverを利用して Webサイトを開くことができました。

正直、findElementsendKeysclick もしていないので、対象のWebページを操作しているという感じでもないですが、
とりあえず Selenium の基本の基本ということで。

Chrome - クライアント証明書を自動選択する

よほどセキュリティの厳しいWebサイト(サービス)にアクセスすることが無い限りは出くわさないのですが、
Webサイトによっては、クライアント証明書による認証を必要とするケースがあります。

クライアント証明書は通常一度OSやWebブラウザに登録すればそれでいいんですが、
基本的に新規にアクセスするたびにクライアント証明書をWebブラウザで選択する必要が出ることがあります。


(補足:今回のユースケースは、プログラムでWebサービスにアクセスする際のクライアント証明書利用の話ではなく、
Webブラウザを利用してWebサイトにアクセスする際の話になっています。)


クライアント証明書1個だけでも面倒になりますが、複数のクライアント証明書を登録していると
複数の証明書の中から手動で選択する作業が必要になるケースがあり、煩わしく感じることもあります。


WebブラウザやOSによりますが、アクセスするWebページに合わせてクライアント証明書を自動選択することが可能です。


前提条件

以下の操作に関しては、PCにクライアント証明書は登録済み。という前提としています。

また、基本的に Windows + Chrome をターゲットにしています。


設定方法

以下のリンク先にある通り、AutoSelectCertificateForUrls というポリシーへの設定の追加で自動選択ができます。


Windowsの場合レジストリで以下のパスへの設定が必要になります

  • パス
    • Software\Policies\Google\Chrome\AutoSelectCertificateForUrls
  • 値の名前
    • 1、2、3、...
  • 値の種類
    • REG_SZ (の一覧)


値として以下のような URLのパターン と 利用するクライアント証明書を選択する条件を JSON 文字列形式で指定します。

{ "pattern": "$URL_PATTERN", "filter" : $FILTER }


レジストリへの登録なので、regedit でレジストリエディターを起動して手動でも登録ができますが、

以下のような内容の .reg ファイルを用意して実行するのもよいかと思います。

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome\AutoSelectCertificateForUrls]
"1"="{\"pattern\":\"https://dummy.web-page-A.org\",\"filter\":{\"ISSUER\":{\"CN\":\"Client-Certificate-A\"}}}"
"2"="{\"pattern\":\"https://dummy.web-page-B.org\",\"filter\":{\"ISSUER\":{\"CN\":\"Client-Certificate-B\", \"O\":\"Organization-B\"}}}"
  • ここでは2つのWebサイトに対して、それぞれ別のクライアント証明書を選択するように値を指定しています。
  • .reg ファイルに書く場合は 上記の例のように JSON 形式の文字列内でエスケープが必要になります。


登録した後、レジストリエディター上で見ると以下のようになります。

f:id:nini_y:20220310184524p:plain


これで、Chromeを起動しなおせば指定のWebサイトではクライアント証明書が自動選択されるようになります。


MS Edge の場合

Microsoft Edge の場合はレジストリのパスが SOFTWARE\Policies\Microsoft\Edge\AutoSelectCertificateForUrls になります


補足

Selenium 等で対象のWebサイトにアクセスした際に、クライアント証明書の選択部分はSeleniumでは制御できないようです。

このような場合に、クライアント証明書が自動選択されるようにしておけば、クライアント証明書の選択から先に処理を進めることができない。という問題を解消できます。