この記事はF# Advent Calendar 2020の2日目の記事です
Summary
インターネットエクスプローラーとはホームページを閲覧するソフトウェアです
一昔前までは超はやりのソフトでしたが(だったらしい)、今はクロムがいいらしいです
そんなインターネットエクスプローラー略して 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