LoginSignup
0

More than 3 years have passed since last update.

F#でインターネットエクスプローラーをつまりは IE を操作してみた!

Last updated at Posted at 2020-11-16

この記事はF# Advent Calendar 2020の2日目の記事です

Summary

image.png

インターネットエクスプローラーとはホームページを閲覧するソフトウェアです

一昔前までは超はやりのソフトでしたが(だったらしい)、今はクロムがいいらしいです

そんなインターネットエクスプローラー略して IE を

Seleniumを使用してF#で操作してみました

環境と事前準備

使用しているパソコン

> ver
Microsoft Windows [Version 10.0.19042.630]

ドットネット

> dotnet --version
5.0.100

webブラウザー

Internet Explorer11
version 11.630.19041.0

事前設定

安定的に動作させるためにはレジストリの設定が必要です

管理者権限がある場合はlocal machineを、なければcurrent userを設定します

設定すべきレジストリ項目

1. countinuousBrowsing -> 0 にする(無効にする)
2. secondaryStartPage  -> 削除する
3. startPage           -> about:blankにする
4. protectionMode      -> 0 にする(すべてのゾーンで有効にする)
5. isolation           -> PMIL
6. isolation64Bit      -> 0 にする(無効にする)
7. featuerBfCache      -> 0 にする(key nameをiexplore.exeにする -> type is DWORD)

see also

Required Configuration
https://github.com/SeleniumHQ/selenium/wiki/InternetExplorerDriver#required-configuration

たとえば自分的に、F#で設定するのに下記のような便利関数をつくったりしてます


  // Registry config for seleniumIE
  module private IeRegistrySettings =

    module LocalMachine =

      let private setRegSz szName szValue query =
        let regkey:Microsoft.Win32.RegistryKey  =
            Microsoft.Win32.Registry.LocalMachine.CreateSubKey( query)
        regkey.SetValue(szName, szValue,Microsoft.Win32.RegistryValueKind.String)
        regkey.Close()

      let private deleteRegSz szName query =
        let regkey:Microsoft.Win32.RegistryKey  =
            Microsoft.Win32.Registry.LocalMachine.CreateSubKey( query )
        if isNull (regkey.GetValue(szName,null)) |> not then
          regkey.DeleteValue(szName)
        else
          ()

      let private setRegDWord dwName dwValue query =
        let regkey:Microsoft.Win32.RegistryKey  =
            Microsoft.Win32.Registry.LocalMachine.CreateSubKey( query)
        regkey.SetValue(dwName, dwValue,Microsoft.Win32.RegistryValueKind.DWord)
        regkey.Close()


      // スタートアップ(インターネットオプション > 全般) => ホームページから開始する
      let public countinuousBrowsing () =

        let dwName = "Enabled"
        let dwValue = 0 |> int32 // disable is 0, enable is 1

        let query = @"SOFTWARE\Microsoft\Internet Explorer\ContinuousBrowsing"
        setRegDWord dwName dwValue query

        let querybit32 = @"SOFTWARE\Wow6432Node\Microsoft\Internet Explorer\ContinuousBrowsing"
        setRegDWord dwName dwValue querybit32

サンプル

こんな感じで動作させます

まず立ち上げ設定

    // ie-driverの設定
    // IE option: https://www.selenium.dev/selenium/docs/api/dotnet/

    let public myDrivier (waitTime:int) =

      (DriverManager()).SetUpDriver(InternetExplorerConfig(),"Latest", Architecture.X32)

      let driverOptions = InternetExplorerOptions()
      driverOptions.IgnoreZoomLevel <- true
      driverOptions.PageLoadStrategy <- PageLoadStrategy.Eager
      driverOptions.RequireWindowFocus <- false

      let driver = new InternetExplorerDriver(driverOptions)

      driver.Manage().Timeouts().PageLoad <- System.TimeSpan.FromSeconds(waitTime |> float)
      driver.Manage().Timeouts().ImplicitWait <- System.TimeSpan.FromSeconds(waitTime |> float)
      driver.Manage().Window.Position <- System.Drawing.Point(0, 0)
      driver.Manage().Window.Size <- System.Drawing.Size(1500,1000)

      driver

リトライは下記な感じで

namespace MyUtil

(*

  util_retry.fs
  -------------------------------------

  Functional backoff/retry
    Backoff/retry with injected mapping from call result to success criterion,
    and backoff times as a parameter. (New version with the backoff in the right place!)
    http://www.fssnip.net/re/title/Functional-backoffretry

*)

module Retry =

  type MyResult = | Ok | NotOk

  let rec private retry (f : unit -> 'T) (didSucceed : 'T -> bool) (timesMs : int list) =
    let rec retry (times:int list) =
      match times with
      | [] ->
        f()
      | head::tail ->
        let result = f ()
        if didSucceed result then
          result
        else
          "リトライしてます..." |> printfn "\n    %s"
          async { do! Async.Sleep(head) } |> Async.RunSynchronously
          retry tail
    retry timesMs


  let private errorlog e fpErrorLog =
    let curdir = System.IO.Directory.GetCurrentDirectory()
    let logfile = System.IO.Path.Combine(curdir,fpErrorLog)
    System.IO.File.AppendAllLines(logfile
      ,[|
        System.DateTime.Now.ToString("yyyyMMdd_HHmmss")
        e.ToString()
        "\n"
      |]
    )


  let public Retry3 filepathErrorlog func =
    retry
      (fun () ->
        try
          func ()
          Ok
        with
        | e ->
          errorlog e filepathErrorlog
          NotOk
      )
      (fun result -> result = Ok)
      [1; 2; 4]

上記のコードを使用して例えば下記みたいにコード書いたりしてみます(一例)

// 一般的な設定をグローバル的な感じで
module GlobalParams =

  open OpenQA.Selenium.IE
  open MyUtil.DB
  open MyLocal

  let _fpErrLog = "./../myFiles/errlog/logfile.txt"
  let _conSqlite = sqliteConnection MyDB.MySqlite.dbSetting
  let _driverIE:(ref<InternetExplorerDriver>) = ref null
  let _driverWaitTime = 30
  let _iRetry:(ref<int>) = ref 0



module mainImpl =

    try
     // doSomething

    with e ->

      printfn "%A" (e.ToString())

      // エラーの場合Webブラウザを再起動する
      (!_driverIE).Quit()
      MyUtil.SeleniumIE.UtilIe.forceQuitIe()
      _driverIE := // ここで上記の myDriver的なのを差し込む

      // エラー内容をメインテーブルに出力する
      if !_iRetry = 3 then
        MyLocal.MyDB.MySqlite.update1 _conSqlite
          {|
             // DBにエラーログを書く
          |}

      _iRetry := !_iRetry + 1
      reraise ()


module main =
   _driverIE := // ここで上記の myDriver的なのを差し込む
   // mainImplで実際の処理

その他

document ready 的な


    open System.Text.RegularExpressions
    let public verifyPageIsLoaded(driver:InternetExplorerDriver) (titlePattern:string) =

      let sw = System.Diagnostics.Stopwatch()
      sw.Start()

      let fst = sw.Elapsed
      let waitTimeSec = driver.Manage().Timeouts().PageLoad |> fun t -> t.TotalSeconds
      let mutable pageLoaded = false
      let mutable cnt = 1

      while not pageLoaded do

        if (sw.Elapsed - fst).TotalSeconds > waitTimeSec then
          raise <| LoadPageException "verifyPageIsLoaded - timeout"
        else
          if (driver.ExecuteScript("return document.readyState;").Equals("complete") ) && (Regex.IsMatch(driver.Title,titlePattern)   ) then
            printfn "\n    問合せ%i回目: %sのページの読込完了!" cnt driver.Title
            pageLoaded <- true
          else
            // debug時はここをコメントアウトする
            // System.Console.SetCursorPosition(0, System.Console.CursorTop)
            printf "    問合せ%i回目: ページの応答を待ってます..." cnt
            ()

        cnt <- cnt + 1
        System.Threading.Thread.Sleep 100

data picker


let cssYMD = "input.xxx.hasDatepicker"

let id = "123456789"
let value = "2020/10/20"

let elm = driver.FindElementByCssSelector(cssYMD)

driver.ExecuteScript(
    "arguments[0].removeAttribute('readonly')"
  , elm
) |> ignore

    elm.SendKeys("2020/10/20")

非表示ボタンを押す

// 非表示のボタンを表示ボタンにする
let css = "xxx"
let scripts = $"""
  arguments[0].style.visibility = 'visible';
"""
ieDriver.ExecuteScript(scripts,ieDriver.FindElementByCssSelector(css)) |> ignore

let cssButton = "xxx"
ieDriver.FindElementByCssSelector(cssButton)
|> fun x -> x.SendKeys(Keys.Control) ; x.Click()

参考

    Selenium downloads
    https://www.selenium.dev/downloads/

    Selenium Client & WebDriver Language Bindings
    https://www.selenium.dev/downloads/

    Selenium.WebDriver ( use alpha version )
      dotnet add package Selenium.WebDriver --version 4.0.0-alpha07
      dotnet add package Selenium.Support --version 4.0.0-alpha07

現場からは以上です

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0