WebDriverDownloadモジュール

本ページには広告が含まれています。

Selenium オブジェクトでブラウザ操作を行うためのWebDriverのダウンロードを行います。

WebDriver.Start メソッド (Selenium)でブラウザを起動する直前に記述することで、ブラウザのバージョンに合ったドライバーをダウンロードします。

直近にダウンロードしたブラウザのバージョンをINIファイルに記録しておくことにより、現在のブラウザのバージョンと同じ場合はダウンロード処理を行いません。

バージョンが同じでもドライバーの再ダウンロードを行いたい場合はforceにTrueを指定します。

構文
WebDriverDownload.Chrome( bit64, force )
WebDriverDownload.Firefox( force )
WebDriverDownload.MsEdge( force )
引数
bit64 必須
Chromeブラウザが64bitの場合はTrue、32bitの場合はFalseを指定。省略した場合、インストール先のフォルダパスから判断。
force 省略可
現在インストールされているドライバーとバージョンが一致しても強制的にインストール処理を行う場合、Trueを指定
戻り値

プログラム

UWSC
//////////////////////////////////////////////////
// 【引数】
//   bit64 : Chromeブラウザが64bitの場合はTrue、32bitの場合はFalseを指定。省略した場合、インストール先のフォルダパスから判断。
//   force : 現在インストールされているドライバーとバージョンが一致しても強制的にインストール処理を行う場合、Trueを指定
// 【戻り値】
//////////////////////////////////////////////////
MODULE WebDriverDownload
	DIM WshShell = CREATEOLEOBJ("WScript.Shell")
	DIM folderspec = WshShell.ExpandEnvironmentStrings("%LOCALAPPDATA%\SeleniumBasic\")
	PROCEDURE Chrome(bit64, force = FALSE)
		DIM browserVersion = ChromeBrowserVersion()
		IF browserVersion = READINI("Chrome", "version", folderspec + "WebDriverVersion.ini") AND !force THEN EXIT
		DIM bit = IIF(bit64, "64", "32")
		WRITEINI("Chrome", "bit", bit, folderspec + "WebDriverVersion.ini")
		DIM driverVersion = ChromeDriverVersion(browserVersion)
		DIM driverURL = ChromeDriverURL(driverVersion, bit)
		WRITEINI("Chrome", "DriverURL", driverURL, folderspec + "WebDriverVersion.ini")
		InstallChromeDriver(driverURL, bit)
		WRITEINI("Chrome", "version", browserVersion, folderspec + "WebDriverVersion.ini")
	FEND
	FUNCTION ChromeBrowserVersion()
		TRY
			DIM path = WshShell.RegRead("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe\Path")
		EXCEPT
			PRINT "Google Chromeがインストールされていません"
			EXIT
		ENDTRY
		RESULT = TRIM(DOSCMD("DIR /B /O-N " + path + " | FINDSTR ^[0-9].*¥>"))
	FEND
	FUNCTION ChromeDriverVersion(version)
		DIM major = SPLIT(version, ".")[0]
		IFB major >= 115 THEN
			DIM url = "https://googlechromelabs.github.io/chrome-for-testing/LATEST_RELEASE_" + major
		ELSE
			url = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_" + major
		ENDIF
		WITH CREATEOLEOBJ("Msxml2.XMLHTTP")
			.Open("GET", url, FALSE)
			.Send()
			RESULT = .ResponseText
		ENDWITH
	FEND
	FUNCTION ChromeDriverURL(browserVersion, bit)
		DIM driverVersion = ChromeDriverVersion(browserVersion)
		DIM major = SPLIT(browserVersion, ".")[0]
		IFB versionCompare(driverVersion, "121.0.6167.184", ">=") THEN
			RESULT = "https://storage.googleapis.com/chrome-for-testing-public/" + driverVersion + "/win" + bit + "/chromedriver-win" + bit + ".zip"
		ELSEIF major >= 115 THEN
			RESULT = "https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/" + driverVersion + "/win" + bit + "/chromedriver-win" + bit + ".zip"
		ELSE
			RESULT = "https://chromedriver.storage.googleapis.com/" + driverVersion + "/chromedriver_win32.zip"
		ENDIF
	FEND
	PROCEDURE InstallChromeDriver(driverURL, bit)
		DIM filename = "chromedriver.zip"
		IF Download(driverURL, folderspec, filename) THEN UnZip(folderspec + filename)
		path = folderspec + "chromedriver-win" + bit + "\chromedriver.exe"
		WITH CREATEOLEOBJ("Scripting.FileSystemObject")
			.CopyFile(path, folderspec, TRUE)
			.DeleteFolder(.GetParentFolderName(path))
		ENDWITH
	FEND
	PROCEDURE Firefox(force = FALSE)
		DIM driverVersion = FireFoxDriverVersion()
		IF driverVersion = READINI("Firefox", "DriverVersion", folderspec + "WebDriverVersion.ini") AND !force THEN EXIT
		DIM driverURL = FirefoxDriverURL(driverVersion)
		WRITEINI("Firefox", "DriverURL", driverURL, folderspec + "WebDriverVersion.ini")
		InstallFirefoxDriver(driverURL)
		WRITEINI("Firefox", "DriverVersion", driverVersion, folderspec + "WebDriverVersion.ini")
	FEND
	FUNCTION FireFoxDriverVersion()
		DIM versionURL = "https://api.github.com/repos/mozilla/geckodriver/tags"
		WITH CREATEOLEOBJ("Msxml2.XMLHTTP")
			.Open("GET", versionURL, FALSE)
			.Send()
			DIM json = .ResponseText
		ENDWITH
		DIM obj = JSON.Parse(json)
		RESULT = obj.Item(0).name
	FEND
	FUNCTION FirefoxDriverURL(driverVersion)
		TRY
			path = WshShell.RegRead("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\firefox.exe\")
			DIM bit = IIF(POS("Program Files (x86)", path), 32, 64)
		EXCEPT
			PRINT "Firefoxがインストールされていません"
			EXIT
		ENDTRY
		RESULT = "https://github.com/mozilla/geckodriver/releases/download/" + driverVersion + "/geckodriver-" + driverVersion + "-win" + bit + ".zip"
	FEND
	PROCEDURE InstallFirefoxDriver(driverURL)
		DIM filename = "geckodriver.zip"
		IF Download(driverURL, folderspec , filename, TRUE) THEN UnZip(folderspec + filename)
	FEND
	PROCEDURE MsEdge(force = FALSE)
		DIM browserVersion = MsEdgeBrowserVersion()
		IF browserVersion = READINI("MsEdge", "version", folderspec + "WebDriverVersion.ini") AND !force THEN EXIT
		DIM driverVersion = MsEdgeDriverVersion(browserVersion)
		DIM driverURL = MsEdgeDriverURL(driverVersion)
		WRITEINI("MsEdge", "DriverURL", driverURL, folderspec + "WebDriverVersion.ini")
		InstallMsEdgeDriver(driverVersion, driverURL)
		WRITEINI("MsEdge", "version", browserVersion, folderspec + "WebDriverVersion.ini")
	FEND
	FUNCTION MsEdgeBrowserVersion()
		TRY
			path = WshShell.RegRead("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\msedge.exe\")
		EXCEPT
			PRINT "Microsoft Edgeがインストールされていません"
			EXIT
		ENDTRY
		RESULT = TRIM(POWERSHELL("(get-item (" + path + ")).VersionInfo.FileVersion"))
	FEND
	FUNCTION MsEdgeDriverVersion(browserVersion)
		DIM major = TOKEN(".", browserVersion)
		DIM Request = Request("https://msedgewebdriverstorage.blob.core.windows.net/edgewebdriver/LATEST_RELEASE_" + major + "_WINDOWS")
		RESULT = TRIM(Request.responseText)
	FEND
	FUNCTION MsEdgeDriverURL(driverVersion)
		RESULT = "https://msedgewebdriverstorage.blob.core.windows.net/edgewebdriver/" + driverVersion + "/edgedriver_win" + IIF(KINDOFOS(TRUE), "64", "32") + ".zip"
	FEND
	PROCEDURE InstallMsEdgeDriver(driverVersion, driverURL)
		DIM filename = "msedgedriver." + driverVersion + ".zip"
		IF Download(driverURL, folderspec, filename) THEN UnZip(folderspec + filename)
	FEND
ENDMODULE

//////////////////////////////////////////////////
// ダウンロード
// url:URL、dir:ディレクトリ名、filename:ファイル名、isServerXMLHTTP:
//////////////////////////////////////////////////
FUNCTION Download(url, dir, filename, isServerXMLHTTP = FALSE)
	CONST adSaveCreateOverWrite = 2
	WITH CREATEOLEOBJ("ADODB.Stream")
		.Open()
		.Type = 1
		IFB isServerXMLHTTP THEN
			obj = CREATEOLEOBJ("Msxml2.ServerXMLHTTP")
		ELSE
			obj = CREATEOLEOBJ("Msxml2.XMLHTTP")
		ENDIF
		obj.Open("GET", url, FALSE)
		obj.Send()
		.Write(obj.responseBody)
		.SaveToFile(dir + filename, adSaveCreateOverWrite)
		.Close()
	ENDWITH
	RESULT = FOPEN(dir + filename, F_EXISTS)
FEND

//////////////////////////////////////////////////
// 要求
// url:URL、isServerXMLHTTP:
//////////////////////////////////////////////////
FUNCTION Request(url, isServerXMLHTTP = FALSE)
	IFB isServerXMLHTTP THEN
		RESULT = CREATEOLEOBJ("Msxml2.ServerXMLHTTP")
	ELSE
		RESULT = CREATEOLEOBJ("Msxml2.XMLHTTP")
	ENDIF
	WITH RESULT
		.Open("GET", url, FALSE)
		.Send()
	ENDWITH
FEND

//////////////////////////////////////////////////
// 【引数】
//   num : 単位換算する数値
//   before : 変換前の単位
//   after : 変換後の単位
// 【戻り値】
//   指定した単位に変換した数値
//////////////////////////////////////////////////
FUNCTION convert(num, before, after)
	HASHTBL unit
	
	// 重量
	unit["g,sg"] = "num * 6.85217658567918 * POWER(10, -5)"
	unit["g,lbm"] = "num * 2.20462262184878 * POWER(10, -3)"
	unit["g,u"] = "num * 6.02217 * POWER(10, +23)"
	unit["g,ozm"] = "num * 3.52739619495804 * POWER(10, -2)"
	unit["sg,g"] = "num * 1.45939029372064 * POWER(10, +4)"
	unit["sg,lbm"] = "num * 3.21740485564304 * POWER(10, +1)"
	unit["sg,u"] = "num * 8.78869644513561 * POWER(10, +27)"
	unit["sg,ozm"] = "num * 5.14784776902887 * POWER(10, +2)"
	unit["lbm,g"] = "num * 4.5359237 * POWER(10, +2)"
	unit["lbm,sg"] = "num * 3.10809501715673 * POWER(10, -2)"
	unit["lbm,u"] = "num * 2.7316103628429 * POWER(10, +26)"
	unit["lbm,ozm"] = "num * 1.6 * POWER(10, +1)"
	unit["u,g"] = "num * 1.66053100460465 * POWER(10, -24)"
	unit["u,sg"] = "num * 1.13782516695463 * POWER(10, -28)"
	unit["u,lbm"] = "num * 3.66084421703269 * POWER(10, -27)"
	unit["u,ozm"] = "num * 5.8573507472523 * POWER(10, -26)"
	unit["ozm,g"] = "num * 2.8349523125 * POWER(10, +1)"
	unit["ozm,sg"] = "num * 1.94255938572295 * POWER(10, -3)"
	unit["ozm,lbm"] = "num * 6.25 * POWER(10, -2)"
	unit["ozm,u"] = "num * 1.70725647677681 * POWER(10, +25)"
	
	// 距離
	unit["m,mi"] = "num * 6.21371192237334 * POWER(10, -4)"
	unit["m,Nmi"] = "num * 5.39956803455724 * POWER(10, -4)"
	unit["m,in"] = "num * 3.93700787401575 * POWER(10, +1)"
	unit["m,ft"] = "num * 3.28083989501312 * POWER(10, +0)"
	unit["m,yd"] = "num * 1.09361329833771 * POWER(10, +0)"
	unit["m,ang"] = "num * 1 * POWER(10, +10)"
	unit["m,pica"] = "num * 2.36220472440945 * POWER(10, +2)"
	unit["mi,m"] = "num * 1.609344 * POWER(10, +3)"
	unit["mi,Nmi"] = "num * 8.68976241900648 * POWER(10, -1)"
	unit["mi,in"] = "num * 6.336 * POWER(10, +4)"
	unit["mi,ft"] = "num * 5.28 * POWER(10, +3)"
	unit["mi,yd"] = "num * 1.76 * POWER(10, +3)"
	unit["mi,ang"] = "num * 1.609344 * POWER(10, +13)"
	unit["mi,pica"] = "num * 3.8016 * POWER(10, +5)"
	unit["Nmi,m"] = "num * 1.852 * POWER(10, +3)"
	unit["Nmi,mi"] = "num * 1.15077944802354 * POWER(10, +0)"
	unit["Nmi,in"] = "num * 7.29133858267717 * POWER(10, +4)"
	unit["Nmi,ft"] = "num * 6.0761154855643 * POWER(10, +3)"
	unit["Nmi,yd"] = "num * 2.02537182852143 * POWER(10, +3)"
	unit["Nmi,ang"] = "num * 1.852 * POWER(10, +13)"
	unit["Nmi,pica"] = "num * 4.3748031496063 * POWER(10, +5)"
	unit["in,m"] = "num * 2.54 * POWER(10, -2)"
	unit["in,mi"] = "num * 1.57828282828283 * POWER(10, -5)"
	unit["in,Nmi"] = "num * 1.37149028077754 * POWER(10, -5)"
	unit["in,ft"] = "num * 8.33333333333333 * POWER(10, -2)"
	unit["in,yd"] = "num * 2.77777777777778 * POWER(10, -2)"
	unit["in,ang"] = "num * 2.54 * POWER(10, +8)"
	unit["in,pica"] = "num * 6 * POWER(10, +0)"
	unit["ft,m"] = "num * 3.048 * POWER(10, -1)"
	unit["ft,mi"] = "num * 1.89393939393939 * POWER(10, -4)"
	unit["ft,Nmi"] = "num * 1.64578833693305 * POWER(10, -4)"
	unit["ft,in"] = "num * 1.2 * POWER(10, +1)"
	unit["ft,yd"] = "num * 3.33333333333333 * POWER(10, -1)"
	unit["ft,ang"] = "num * 3.048 * POWER(10, +9)"
	unit["ft,pica"] = "num * 7.2 * POWER(10, +1)"
	unit["yd,m"] = "num * 9.144 * POWER(10, -1)"
	unit["yd,mi"] = "num * 5.68181818181818 * POWER(10, -4)"
	unit["yd,Nmi"] = "num * 4.93736501079914 * POWER(10, -4)"
	unit["yd,in"] = "num * 3.6 * POWER(10, +1)"
	unit["yd,ft"] = "num * 3 * POWER(10, +0)"
	unit["yd,ang"] = "num * 9.144 * POWER(10, +9)"
	unit["yd,pica"] = "num * 2.16 * POWER(10, +2)"
	unit["ang,m"] = "num * 1 * POWER(10, -10)"
	unit["ang,mi"] = "num * 6.21371192237334 * POWER(10, -14)"
	unit["ang,Nmi"] = "num * 5.39956803455724 * POWER(10, -14)"
	unit["ang,in"] = "num * 3.93700787401575 * POWER(10, -9)"
	unit["ang,ft"] = "num * 3.28083989501312 * POWER(10, -10)"
	unit["ang,yd"] = "num * 1.09361329833771 * POWER(10, -10)"
	unit["ang,pica"] = "num * 2.36220472440945 * POWER(10, -8)"
	unit["pica,m"] = "num * 4.23333333333333 * POWER(10, -3)"
	unit["pica,mi"] = "num * 2.63047138047138 * POWER(10, -6)"
	unit["pica,Nmi"] = "num * 2.28581713462923 * POWER(10, -6)"
	unit["pica,in"] = "num * 1.66666666666667 * POWER(10, -1)"
	unit["pica,ft"] = "num * 1.38888888888889 * POWER(10, -2)"
	unit["pica,yd"] = "num * 4.62962962962963 * POWER(10, -3)"
	unit["pica,ang"] = "num * 4.23333333333333 * POWER(10, +7)"

	// 時間
	unit["yr,day"] = "num * 3.6525 * POWER(10, +2)"
	unit["yr,hr"] = "num * 8.766 * POWER(10, +3)"
	unit["yr,mn"] = "num * 5.2596 * POWER(10, +5)"
	unit["yr,sec"] = "num * 3.15576 * POWER(10, +7)"
	unit["day,yr"] = "num * 2.7378507871321 * POWER(10, -3)"
	unit["day,hr"] = "num * 2.4 * POWER(10, +1)"
	unit["day,mn"] = "num * 1.44 * POWER(10, +3)"
	unit["day,sec"] = "num * 8.64 * POWER(10, +4)"
	unit["hr,yr"] = "num * 1.14077116130504 * POWER(10, -4)"
	unit["hr,day"] = "num * 4.16666666666667 * POWER(10, -2)"
	unit["hr,mn"] = "num * 6 * POWER(10, +1)"
	unit["hr,sec"] = "num * 3.6 * POWER(10, +3)"
	unit["mn,yr"] = "num * 1.90128526884174 * POWER(10, -6)"
	unit["mn,day"] = "num * 6.94444444444444 * POWER(10, -4)"
	unit["mn,hr"] = "num * 1.66666666666667 * POWER(10, -2)"
	unit["mn,sec"] = "num * 6 * POWER(10, +1)"
	unit["sec,yr"] = "num * 3.16880878140289 * POWER(10, -8)"
	unit["sec,day"] = "num * 1.15740740740741 * POWER(10, -5)"
	unit["sec,hr"] = "num * 2.77777777777778 * POWER(10, -4)"
	unit["sec,mn"] = "num * 1.66666666666667 * POWER(10, -2)"
	
	// 圧力
	unit["Pa,atm"] = "num * 9.86923266716013 * POWER(10, -6)"
	unit["Pa,mmHg"] = "num * 7.5006168270417 * POWER(10, -3)"
	unit["atm,Pa"] = "num * 1.01325 * POWER(10, +5)"
	unit["atm,mmHg"] = "num * 7.6 * POWER(10, +2)"
	unit["mmHg,Pa"] = "num * 1.33322368421053 * POWER(10, +2)"
	unit["mmHg,atm"] = "num * 1.31578947368421 * POWER(10, -3)"
	
	// 物理的な力
	unit["N,dyn"] = "num * 1 * POWER(10, +5)"
	unit["N,lbf"] = "num * 2.2480894309971 * POWER(10, -1)"
	unit["dyn,N"] = "num * 1 * POWER(10, -5)"
	unit["dyn,lbf"] = "num * 2.2480894309971 * POWER(10, -6)"
	unit["lbf,N"] = "num * 4.4482216152605 * POWER(10, +0)"
	unit["lbf,dyn"] = "num * 4.4482216152605 * POWER(10, +5)"
	
	// エネルギー
	unit["J,e"] = "num * 1 * POWER(10, +7)"
	unit["J,cal"] = "num * 2.38845896627496 * POWER(10, -1)"
	unit["J,eV"] = "num * 6.241457 * POWER(10, +18)"
	unit["J,HPh"] = "num * 3.72506135998619 * POWER(10, -7)"
	unit["J,Wh"] = "num * 2.77777777777778 * POWER(10, -4)"
	unit["J,flb"] = "num * 7.37562149277265 * POWER(10, -1)"
	unit["J,BTU"] = "num * 9.47817120313317 * POWER(10, -4)"
	unit["J,c"] = "num * 2.39005736137667 * POWER(10, -1)"
	unit["e,J"] = "num * 1 * POWER(10, -7)"
	unit["e,cal"] = "num * 2.38845896627496 * POWER(10, -8)"
	unit["e,eV"] = "num * 6.241457 * POWER(10, +11)"
	unit["e,HPh"] = "num * 3.72506135998619 * POWER(10, -14)"
	unit["e,Wh"] = "num * 2.77777777777778 * POWER(10, -11)"
	unit["e,flb"] = "num * 7.37562149277265 * POWER(10, -8)"
	unit["e,BTU"] = "num * 9.47817120313317 * POWER(10, -11)"
	unit["e,c"] = "num * 2.39005736137667 * POWER(10, -8)"
	unit["cal,J"] = "num * 4.1868 * POWER(10, +0)"
	unit["cal,e"] = "num * 4.1868 * POWER(10, +7)"
	unit["cal,eV"] = "num * 2.61317321676 * POWER(10, +19)"
	unit["cal,HPh"] = "num * 1.55960869019902 * POWER(10, -6)"
	unit["cal,Wh"] = "num * 1.163 * POWER(10, -3)"
	unit["cal,flb"] = "num * 3.08802520659405 * POWER(10, +0)"
	unit["cal,BTU"] = "num * 3.9683207193278 * POWER(10, -3)"
	unit["cal,c"] = "num * 1.00066921606119 * POWER(10, +0)"
	unit["eV,J"] = "num * 1.60219000146921 * POWER(10, -19)"
	unit["eV,e"] = "num * 1.60219000146921 * POWER(10, -12)"
	unit["eV,cal"] = "num * 3.82676507468522 * POWER(10, -20)"
	unit["eV,HPh"] = "num * 5.96825606582916 * POWER(10, -26)"
	unit["eV,Wh"] = "num * 4.45052778185891 * POWER(10, -23)"
	unit["eV,flb"] = "num * 1.18171470103417 * POWER(10, -19)"
	unit["eV,BTU"] = "num * 1.51858311338733 * POWER(10, -22)"
	unit["eV,c"] = "num * 3.82932600733558 * POWER(10, -20)"
	unit["HPh,J"] = "num * 2.68451953769617 * POWER(10, +6)"
	unit["HPh,e"] = "num * 2.68451953769617 * POWER(10, +13)"
	unit["HPh,cal"] = "num * 6.41186475995073 * POWER(10, +5)"
	unit["HPh,eV"] = "num * 1.67553132601905 * POWER(10, +25)"
	unit["HPh,Wh"] = "num * 7.4569987158227 * POWER(10, +2)"
	unit["HPh,flb"] = "num * 1.98 * POWER(10, +6)"
	unit["HPh,BTU"] = "num * 2.54443357764402 * POWER(10, +3)"
	unit["HPh,c"] = "num * 6.41615568283024 * POWER(10, +5)"
	unit["Wh,J"] = "num * 3.6 * POWER(10, +3)"
	unit["Wh,e"] = "num * 3.6 * POWER(10, +10)"
	unit["Wh,cal"] = "num * 8.59845227858985 * POWER(10, +2)"
	unit["Wh,eV"] = "num * 2.24692452 * POWER(10, +22)"
	unit["Wh,HPh"] = "num * 1.34102208959503 * POWER(10, -3)"
	unit["Wh,flb"] = "num * 2.65522373739816 * POWER(10, +3)"
	unit["Wh,BTU"] = "num * 3.41214163312794 * POWER(10, +0)"
	unit["Wh,c"] = "num * 8.60420650095602 * POWER(10, +2)"
	unit["flb,J"] = "num * 1.3558179483314 * POWER(10, +0)"
	unit["flb,e"] = "num * 1.3558179483314 * POWER(10, +7)"
	unit["flb,cal"] = "num * 3.23831553532865 * POWER(10, -1)"
	unit["flb,eV"] = "num * 8.46227942433866 * POWER(10, +18)"
	unit["flb,HPh"] = "num * 5.05050505050505 * POWER(10, -7)"
	unit["flb,Wh"] = "num * 3.76616096758722 * POWER(10, -4)"
	unit["flb,BTU"] = "num * 1.28506746345658 * POWER(10, -3)"
	unit["flb,c"] = "num * 3.24048266809608 * POWER(10, -1)"
	unit["BTU,J"] = "num * 1.05505585262 * POWER(10, +3)"
	unit["BTU,e"] = "num * 1.05505585262 * POWER(10, +10)"
	unit["BTU,cal"] = "num * 2.51995761111111 * POWER(10, +2)"
	unit["BTU,eV"] = "num * 6.58508573672607 * POWER(10, +21)"
	unit["BTU,HPh"] = "num * 3.93014778922204 * POWER(10, -4)"
	unit["BTU,Wh"] = "num * 2.93071070172222 * POWER(10, -1)"
	unit["BTU,flb"] = "num * 7.78169262265965 * POWER(10, +2)"
	unit["BTU,c"] = "num * 2.52164400721797 * POWER(10, +2)"
	unit["c,J"] = "num * 4.184 * POWER(10, +0)"
	unit["c,e"] = "num * 4.184 * POWER(10, +7)"
	unit["c,cal"] = "num * 9.99331231489443 * POWER(10, -1)"
	unit["c,eV"] = "num * 2.6114256088 * POWER(10, +19)"
	unit["c,HPh"] = "num * 1.55856567301822 * POWER(10, -6)"
	unit["c,Wh"] = "num * 1.16222222222222 * POWER(10, -3)"
	unit["c,flb"] = "num * 3.08596003257608 * POWER(10, +0)"
	unit["c,BTU"] = "num * 3.96566683139092 * POWER(10, -3)"
	
	// 仕事率
	unit["HP,W"] = "num * 7.4569987158227 * POWER(10, +2)"
	unit["W,HP"] = "num * 1.34102208959503 * POWER(10, -3)"
	
	// 磁力
	unit["T,ga"] = "num * 1 * POWER(10, +4)"
	unit["ga,T"] = "num * 1 * POWER(10, -4)"
	
	// 温度
	unit["C,F"] = "num * (9/5) + 32"
	unit["C,K"] = "num + 273.15"
	unit["F,C"] = "(num - 32) * (9/5)"
	unit["F,K"] = "(num - 32) * (5/9) + 273.15"
	unit["K,C"] = "num - 23373.15"
	unit["K,F"] = "(num - 273.15) * (9/5) + 32"
	
	// 体積(容積)
	unit["tsp,tbs"] = "num * 3.33333333333333 * POWER(10, -1)"
	unit["tsp,oz"] = "num * 1.66666666666667 * POWER(10, -1)"
	unit["tsp,cup"] = "num * 2.08333333333333 * POWER(10, -2)"
	unit["tsp,us_pt"] = "num * 1.04166666666667 * POWER(10, -2)"
	unit["tsp,uk_pt"] = "num * 8.67368942321863 * POWER(10, -3)"
	unit["tsp,qt"] = "num * 5.20833333333333 * POWER(10, -3)"
	unit["tsp,gal"] = "num * 1.30208333333333 * POWER(10, -3)"
	unit["tbs,tsp"] = "num * 3 * POWER(10, +0)"
	unit["tbs,oz"] = "num * 5 * POWER(10, -1)"
	unit["tbs,cup"] = "num * 6.25 * POWER(10, -2)"
	unit["tbs,us_pt"] = "num * 3.125 * POWER(10, -2)"
	unit["tbs,uk_pt"] = "num * 2.60210682696559 * POWER(10, -2)"
	unit["tbs,qt"] = "num * 1.5625 * POWER(10, -2)"
	unit["tbs,gal"] = "num * 3.90625 * POWER(10, -3)"
	unit["oz,tsp"] = "num * 6 * POWER(10, +0)"
	unit["oz,tbs"] = "num * 2 * POWER(10, +0)"
	unit["oz,cup"] = "num * 1.25 * POWER(10, -1)"
	unit["oz,us_pt"] = "num * 6.25 * POWER(10, -2)"
	unit["oz,uk_pt"] = "num * 5.20421365393118 * POWER(10, -2)"
	unit["oz,qt"] = "num * 3.125 * POWER(10, -2)"
	unit["oz,gal"] = "num * 7.8125 * POWER(10, -3)"
	unit["cup,tsp"] = "num * 4.8 * POWER(10, +1)"
	unit["cup,tbs"] = "num * 1.6 * POWER(10, +1)"
	unit["cup,oz"] = "num * 8 * POWER(10, +0)"
	unit["cup,us_pt"] = "num * 5 * POWER(10, -1)"
	unit["cup,uk_pt"] = "num * 4.16337092314494 * POWER(10, -1)"
	unit["cup,qt"] = "num * 2.5 * POWER(10, -1)"
	unit["cup,gal"] = "num * 6.25 * POWER(10, -2)"
	unit["us_pt,tsp"] = "num * 9.6 * POWER(10, +1)"
	unit["us_pt,tbs"] = "num * 3.2 * POWER(10, +1)"
	unit["us_pt,oz"] = "num * 1.6 * POWER(10, +1)"
	unit["us_pt,cup"] = "num * 2 * POWER(10, +0)"
	unit["us_pt,uk_pt"] = "num * 8.32674184628989 * POWER(10, -1)"
	unit["us_pt,qt"] = "num * 5 * POWER(10, -1)"
	unit["us_pt,gal"] = "num * 1.25 * POWER(10, -1)"
	unit["uk_pt,tsp"] = "num * 1.15291192848466 * POWER(10, +2)"
	unit["uk_pt,tbs"] = "num * 3.84303976161554 * POWER(10, +1)"
	unit["uk_pt,oz"] = "num * 1.92151988080777 * POWER(10, +1)"
	unit["uk_pt,cup"] = "num * 2.40189985100971 * POWER(10, +0)"
	unit["uk_pt,us_pt"] = "num * 1.20094992550486 * POWER(10, +0)"
	unit["uk_pt,qt"] = "num * 6.00474962752428 * POWER(10, -1)"
	unit["uk_pt,gal"] = "num * 1.50118740688107 * POWER(10, -1)"
	unit["qt,tsp"] = "num * 1.92 * POWER(10, +2)"
	unit["qt,tbs"] = "num * 6.4 * POWER(10, +1)"
	unit["qt,oz"] = "num * 3.2 * POWER(10, +1)"
	unit["qt,cup"] = "num * 4 * POWER(10, +0)"
	unit["qt,us_pt"] = "num * 2 * POWER(10, +0)"
	unit["qt,uk_pt"] = "num * 1.66534836925798 * POWER(10, +0)"
	unit["qt,gal"] = "num * 2.5 * POWER(10, -1)"
	unit["gal,tsp"] = "num * 7.68 * POWER(10, +2)"
	unit["gal,tbs"] = "num * 2.56 * POWER(10, +2)"
	unit["gal,oz"] = "num * 1.28 * POWER(10, +2)"
	unit["gal,cup"] = "num * 1.6 * POWER(10, +1)"
	unit["gal,us_pt"] = "num * 8 * POWER(10, +0)"
	unit["gal,uk_pt"] = "num * 6.66139347703191 * POWER(10, +0)"
	unit["gal,qt"] = "num * 4 * POWER(10, +0)"

	RESULT = EVAL(unit[before + "," + after])
FEND

//////////////////////////////////////////////////
// 【引数】
//   interval : 加算する時間間隔を表す文字列式(yyyy:年、m:月、d:日、ww:週、h:時、n:分、s:秒)
//   num : dateに加算する値。未来は正、過去は負で指定
//   date : 時間間隔を加算する日付
// 【戻り値】
//   日時(date)に、指定した単位(interval)の時間(num)を加算して返します
//////////////////////////////////////////////////
FUNCTION dateAdd(interval, num, date)
	DIM year, month, day, d
	GETTIME(0, date)
	DIM time = G_TIME_HH2 + ":" + G_TIME_NN2 + ":" + G_TIME_SS2
	SELECT interval
		CASE "yyyy"
			d = (G_TIME_YY + num) + "/" + G_TIME_MM2 + "/" + G_TIME_DD2
			IF time  "00:00:00" THEN d = d + " " + time
		CASE "m"
			IFB num > 0 THEN
				year = G_TIME_YY + INT((G_TIME_MM + num) / 12)
				month = REPLACE(FORMAT(((G_TIME_MM + num) MOD 12), 2), " ", "0")
			ELSE
				year = G_TIME_YY + CEIL((G_TIME_MM + num) / 12 - 1)
				month = REPLACE(FORMAT(G_TIME_MM - (ABS(num) MOD 12), 2), " ", "0")
			ENDIF
			IF month = "00" THEN month = 12
			day = G_TIME_DD2
			d = "" + year + month + day
			IFB !isDate(d) THEN
				d = year + "/" + month + "/" + "01"
				d = getEndOfMonth(d)
			ELSE
				d = year + "/" + month + "/" + day
			ENDIF
			IF time  "00:00:00" THEN d = d + " " + time
		CASE "d"
			t = GETTIME(num, date)
			d = G_TIME_YY4 + "/" + G_TIME_MM2 + "/" + G_TIME_DD2 + IIF(t MOD 86400, " " + G_TIME_HH2 + ":" + G_TIME_NN2 + ":" + G_TIME_SS2, "")
		CASE "ww"
			t = GETTIME(num * 7, date)
			d = G_TIME_YY4 + "/" + G_TIME_MM2 + "/" + G_TIME_DD2 + IIF(t MOD 86400, " " + G_TIME_HH2 + ":" + G_TIME_NN2 + ":" + G_TIME_SS2, "")
		CASE "h"
			t = GETTIME(num / 24, date)
			d = G_TIME_YY4 + "/" + G_TIME_MM2 + "/" + G_TIME_DD2 + IIF(t MOD 86400, " " + G_TIME_HH2 + ":" + G_TIME_NN2 + ":" + G_TIME_SS2, "")
		CASE "n"
			t = GETTIME(num / 1440, date)
			d = G_TIME_YY4 + "/" + G_TIME_MM2 + "/" + G_TIME_DD2 + IIF(t MOD 86400, " " + G_TIME_HH2 + ":" + G_TIME_NN2 + ":" + G_TIME_SS2, "")
		CASE "s"
			t = GETTIME(num / 86400, date)
			d = G_TIME_YY4 + "/" + G_TIME_MM2 + "/" + G_TIME_DD2 + IIF(t MOD 86400, " " + G_TIME_HH2 + ":" + G_TIME_NN2 + ":" + G_TIME_SS2, "")
	SELEND
	RESULT = d
FEND

//////////////////////////////////////////////////
// 【引数】
//   interval : 時間単位(yyyy︰年、q:四半期、m︰月、d︰日、w:週日、ww:週、h:時、n:分、s:秒)
//   date1 : 日時1
//   date2 : 日時2
// 【戻り値】
//   date2からdate1を引いた時間間隔を求めます。
//////////////////////////////////////////////////
FUNCTION dateDiff(interval, date1, date2)
	DIM y1, y2, m1, m2, d1, d2, d
	SELECT interval
		CASE "yyyy"
			GETTIME(0, date1)
			y1 = G_TIME_YY
			GETTIME(0, date2)
			y2 = G_TIME_YY
			d = y2 - y1
		CASE "q"
			GETTIME(0, date1)
			y1 = G_TIME_YY
			m1 = G_TIME_MM
			GETTIME(0, date2)
			y2 = G_TIME_YY
			m2 = G_TIME_MM
			d = y2 * 4 + CEIL(m2/3) - (y1 * 4 + CEIL(m1/3))
		CASE "m"
			GETTIME(0, date1)
			y1 = G_TIME_YY
			m1 = G_TIME_MM
			GETTIME(0, date2)
			y2 = G_TIME_YY
			m2 = G_TIME_MM
			d = (y2 - y1) * 12 + m2 - m1
		CASE "d"
			d1 = GETTIME(0, date1)
			d2 = GETTIME(0, date2)
			d = (d2 - d1) / 86400
		CASE "w"
			d = INT(dateDiff("d", date1, date2) / 7)
		CASE "ww"
			date1 = dateAdd("d", -1 * getWeekday(date1), date1)
			d = INT(dateDiff("d", date1, date2) / 7)
		CASE "h"
			d = dateDiff("d", date1, date2) * 24
		CASE "n"
			d = dateDiff("d", date1, date2) * 1440
		CASE "s"
			d = dateDiff("d", date1, date2) * 86400
	SELEND
	RESULT = d
FEND

//////////////////////////////////////////////////
// 【引数】
//   num : 数値
// 【戻り値】
//////////////////////////////////////////////////
FUNCTION decimalDigits(num)
	DIM str = fixed(num)
	RESULT = IIF(POS(".", str), LENGTH(BETWEENSTR(str, ".")), 0)
FEND

//////////////////////////////////////////////////
// 【引数】
//   num : 数値
//   digits : 小数点以下の桁数
// 【戻り値】
//////////////////////////////////////////////////
FUNCTION fixed(num, digits = EMPTY)
	num = VAL(num)		// 指数表記を整える
	IFB POS("E-", num) THEN
		DIM mantissa = BETWEENSTR(num,, "E")
		DIM exponent = BETWEENSTR(num, "E")		
		RESULT = "0." + strRepeat("0", VAL(ABS(exponent) - 1)) + REPLACE(mantissa, ".", "")
	ELSEIF POS("E", num) THEN
		RESULT = ROUND(num, -1 *digits)
		mantissa = BETWEENSTR(num,, "E")
		exponent = BETWEENSTR(num, "E")
		RESULT = REPLACE(mantissa, ".", "") + strRepeat("0", VAL(exponent) - decimalDigits(mantissa))
	ELSEIF LENGTH(BETWEENSTR(num, "."))  258 THEN date = text(date, "yyyy/mm/dd")
	GETTIME(0, date)
	DIM w = G_TIME_WW
	SELECT TRUE
		CASE type = 0
			RESULT = w
		CASE type = 1
			RESULT = w + 1
		CASE type = 2
			RESULT = IIF(w=0, 7, w)
		CASE type = 3
			RESULT = (w+6) MOD 7
		CASE type >= 11
			RESULT = ((getWeekday(date, 2) + 17 - type) MOD 7) + 1
	SELEND
FEND

//////////////////////////////////////////////////
// 【引数】
//   serial : シリアル値もしくは時刻文字列
// 【戻り値】
//   時刻から時間を表す0〜23の範囲の値
//////////////////////////////////////////////////
FUNCTION Hour(serial)
	IF VARTYPE(serial) = 258 THEN serial = timeValue(serial)
	RESULT = INT(serial * 24) MOD 24
FEND

//////////////////////////////////////////////////
// 【引数】
//   expr : 評価する式
//   truepart : 評価した式がTrueのときに返す値
//   falsepart : 評価した式がFalseのときに返す値
// 【戻り値】
//   truepart : 評価した式がTrueのとき、falsepart : 評価した式がFalseのとき
//////////////////////////////////////////////////
FUNCTION IIF(expr, truepart, falsepart)
	IFB EVAL(expr) THEN
		RESULT = truepart
	ELSE
		RESULT = falsepart
	ENDIF
FEND

//////////////////////////////////////////////////
// 【引数】
//   date : 存在するかを調べる日付文字列。YYYYMMDD or YYYY/MM/DD or YYYY-MM-DDのいずれかの形式。
// 【戻り値】
//   TRUE : 日付として認識できる、FALSE : 日付として認識できない
//////////////////////////////////////////////////
FUNCTION isDate(date)
	TRY
		GETTIME(0, date)
		RESULT = TRUE
	EXCEPT
		RESULT = FALSE
	ENDTRY
FEND

//////////////////////////////////////////////////
// 【引数】
//   text : JSONとして解析する文字列
//   value : JSON文字列に変換する値
//   reviver : 使用不可
//   replacer : 使用不可
//   space : 出力するJSON文字列に空白を挿入するための文字列もしくは数値
// 【戻り値】
//    : Parse : JSON文字列をオブジェクトに変換、
//   Stringify : オブジェクトをJSON文字列に変換、 :
//////////////////////////////////////////////////
MODULE JSON
	DIM SC, CodeObject
	PROCEDURE JSON
		SC = CREATEOLEOBJ("ScriptControl")
		WITH SC
			.Language = "JScript"
			.ExecuteStatement(json2)
			.ExecuteStatement(statement)
			CodeObject = .CodeObject
		ENDWITH
	FEND
	FUNCTION Parse(text, reviver = NULL)
		RESULT = CodeObject.JSON.parse(text, reviver)
	FEND
	FUNCTION Stringify(value, replacer = "", space = FALSE)
		RESULT = CodeObject.JSON.stringify(value, NULL, replacer)
		IF space THEN RESULT = REPLACE(RESULT, CHR(10), "")
	FEND
ENDMODULE

TEXTBLOCK statement
Array.prototype.Item = function(i, value){
	if(value === undefined) return this[i]; this[i] = value;
}
Array.prototype.item = Array.prototype.Item;
ENDTEXTBLOCK

TEXTBLOCK json2
//  json2.js
//  2023-05-10
//  Public Domain.
//  NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.

//  USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
//  NOT CONTROL.

//  This file creates a global JSON object containing two methods: stringify
//  and parse. This file provides the ES5 JSON capability to ES3 systems.
//  If a project might run on IE8 or earlier, then this file should be included.
//  This file does nothing on ES5 systems.

//      JSON.stringify(value, replacer, space)
//          value       any JavaScript value, usually an object or array.
//          replacer    an optional parameter that determines how object
//                      values are stringified for objects. It can be a
//                      function or an array of strings.
//          space       an optional parameter that specifies the indentation
//                      of nested structures. If it is omitted, the text will
//                      be packed without extra whitespace. If it is a number,
//                      it will specify the number of spaces to indent at each
//                      level. If it is a string (such as "\t" or " "),
//                      it contains the characters used to indent at each level.
//          This method produces a JSON text from a JavaScript value.
//          When an object value is found, if the object contains a toJSON
//          method, its toJSON method will be called and the result will be
//          stringified. A toJSON method does not serialize: it returns the
//          value represented by the name/value pair that should be serialized,
//          or undefined if nothing should be serialized. The toJSON method
//          will be passed the key associated with the value, and this will be
//          bound to the value.

//          For example, this would serialize Dates as ISO strings.

//              Date.prototype.toJSON = function (key) {
//                  function f(n) {
//                      // Format integers to have at least two digits.
//                      return (n = 0.5, CEIL(num * offset) / offset, INT(num * offset) / offset)
FEND

//////////////////////////////////////////////////
// 【引数】
//   serial : 時間を表すシリアル値を指定
// 【戻り値】
//////////////////////////////////////////////////
FUNCTION Second(serial)
	RESULT = REPLACE(FORMAT(INT(serial * 86400) MOD 60, 2), " ", "0")
FEND

//////////////////////////////////////////////////
// 【引数】
//   num : 符号を求める数値
// 【戻り値】
//   1 : 正の数、0 : ゼロ、-1 : 負の数、ERR_VALUE : それ以外
//////////////////////////////////////////////////
FUNCTION sign(num)
	SELECT TRUE
		CASE !CHKNUM(num)
			RESULT = ERR_VALUE
		CASE num > 0
			RESULT = 1
		CASE num = 0
			RESULT = 0
		CASE num = 0
					RESULT = text(serial, "yyyy") - 2018
				CASE dateDiff("d", startDate["平成"], text(serial, "yyyy/mm/dd")) >= 0
					RESULT = text(serial, "yyyy") - 1988
				CASE dateDiff("d", startDate["昭和"], text(serial, "yyyy/mm/dd")) >= 0
					RESULT = text(serial, "yyyy") - 1925
				CASE dateDiff("d", startDate["大正"], text(serial, "yyyy/mm/dd")) >= 0
					RESULT = text(serial, "yyyy") - 1911
				CASE dateDiff("d", startDate["明治"], text(serial, "yyyy/mm/dd")) >= 0
					RESULT = text(serial, "yyyy") - 1867
			SELEND
		CASE format = "ee"
			SELECT TRUE
				CASE dateDiff("d", startDate["令和"], text(serial, "yyyy/mm/dd")) >= 0
					RESULT = text(text(serial, "yyyy") - 2018, "00")
				CASE dateDiff("d", startDate["平成"], text(serial, "yyyy/mm/dd")) >= 0
					RESULT = text(text(serial, "yyyy") - 1988, "00")
				CASE dateDiff("d", startDate["昭和"], text(serial, "yyyy/mm/dd")) >= 0
					RESULT = text(text(serial, "yyyy") - 1925, "00")
				CASE dateDiff("d", startDate["大正"], text(serial, "yyyy/mm/dd")) >= 0
					RESULT = text(text(serial, "yyyy") - 1911, "00")
				CASE dateDiff("d", startDate["明治"], text(serial, "yyyy/mm/dd")) >= 0
					RESULT = text(text(serial, "yyyy") - 1867, "00")
			SELEND
		CASE format = "g"
			SELECT TRUE
				CASE dateDiff("d", startDate["令和"], text(serial, "yyyy/mm/dd")) >= 0;		RESULT = "R"
				CASE dateDiff("d", startDate["平成"], text(serial, "yyyy/mm/dd")) >= 0;		RESULT = "H"
				CASE dateDiff("d", startDate["昭和"], text(serial, "yyyy/mm/dd")) >= 0;		RESULT = "S"
				CASE dateDiff("d", startDate["大正"], text(serial, "yyyy/mm/dd")) >= 0;		RESULT = "T"
				CASE dateDiff("d", startDate["明治"], text(serial, "yyyy/mm/dd")) >= 0;		RESULT = "M"
			SELEND
		CASE format = "gg"
			RESULT = COPY(text(serial, "ggg"), 1, 1)
		CASE format = "ggg"
			SELECT TRUE
				CASE dateDiff("d", startDate["令和"], text(serial, "yyyy/mm/dd")) >= 0;		RESULT = "令和"
				CASE dateDiff("d", startDate["平成"], text(serial, "yyyy/mm/dd")) >= 0;		RESULT = "平成"
				CASE dateDiff("d", startDate["昭和"], text(serial, "yyyy/mm/dd")) >= 0;		RESULT = "昭和"
				CASE dateDiff("d", startDate["大正"], text(serial, "yyyy/mm/dd")) >= 0;		RESULT = "大正"
				CASE dateDiff("d", startDate["明治"], text(serial, "yyyy/mm/dd")) >= 0;		RESULT = "明治"
			SELEND
		CASE format = "mmmmm"
			RESULT = COPY(text(serial, "mmmm"), 1, 1)
		CASE format = "mmmm"
			DIM month[] = "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
			RESULT = month[text(serial, "m") - 1]
		CASE format = "mmm"
			RESULT = COPY(text(serial, "mmmm"), 1, 3)
		CASE format = "dd"
			GETTIME(serial, baseDate)
			RESULT = text(G_TIME_DD2, "00")
		CASE format = "d"
			GETTIME(serial, baseDate)
			RESULT = text(G_TIME_DD, "0")
		CASE reTest(format, "^[ad]{3,4}$")
			Matches = reExecute(format, "([ad]{3,4})")
			GETTIME(serial, baseDate)
			DIM aaa[] = "日", "月", "火", "水", "木", "金", "土"
			DIM aaaa[] = "日曜日", "月曜日", "火曜日", "水曜日", "木曜日", "金曜日", "土曜日"
			DIM ddd[] = "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
			DIM dddd[] = "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
			RESULT = EVAL(Matches.Item(0).SubMatches(0) + "[" + getWeekday(G_TIME_WW, 1) + "]")
		CASE reTest(format, "(0+\.?0+)?%")
			Matches = reExecute(format, "(0+\.?0+)?%")
			RESULT = text(serial * 100, Matches.Item(0).SubMatches(0)) + "%"
		CASE reTest(format, "^\[DBNum\d{1,4}\](.*?)$")
			Matches = reExecute(format, "^\[DBNum(\d{1,4})\](.*?)$")
			DIM value = VAL(Matches.Item(0).SubMatches(0))
			DIM sss = text(serial, Matches.Item(0).SubMatches(1))
			Matches = reExecute(sss, "(\D+)?(\d+)(\D+)?")
			DIM res = ""
			FOR m = 0 TO Matches.Count - 1
				serial = Matches.Item(m).SubMatches(1)
				SELECT value
					CASE 1, 2
						DIM n[][9] = "〇", "一", "二", "三", "四", "五", "六", "七", "八", "九", _
										"", "壱", "弐", "参", "四", "伍", "六", "七", "八", "九"
						DIM a[][3] = "", "十", "百", "千",  _
										"", "拾", "百", "阡"
						DIM b[][3] = "", "万", "億", "兆", _
										"", "萬", "億", "兆"
						DIM r = ""
						DIM j = 0
						type = value - 1
						REPEAT
							DIM str = ""
							DIM n4 = serial MOD 10000
							FOR i = LENGTH(n4) TO 1 STEP -1
								s = COPY(n4, i, 1)
								IFB s = 1 AND a[type][LENGTH(n4)-i]  "" THEN
									str = IIF(s, a[type][LENGTH(n4)-i], "") + str
								ELSE	
									str = n[type][s] + IIF(s, a[type][LENGTH(n4)-i], "") + str
								ENDIF
							NEXT
							IF str  "" THEN r = str + b[type][j] + r
							j = j + 1
							serial = INT(serial / 10000)
						UNTIL serial = 0
						res = res + Matches.Item(m).SubMatches(0) + r + Matches.Item(m).SubMatches(2)
					CASE 3
						res = res + Matches.Item(m).SubMatches(0) + STRCONV(serial, SC_FULLWIDTH) + Matches.Item(m).SubMatches(2)
					CASE 4
						res = res + Matches.Item(m).SubMatches(0) + STRCONV(serial, SC_HALFWIDTH) + Matches.Item(m).SubMatches(2)
				SELEND
			NEXT
			RESULT = res
		CASE reTest(format, "^(.*?)(AM\/PM|am\/pm|A\/P|a\/p)(.*?)$")
			Matches = reExecute(format, "^(.*?)(AM\/PM|am\/pm|A\/P|a\/p)(.*?)$")
			DIM array = SPLIT(Matches.Item(0).SubMatches(1), "/")
			ampm = array[IIF(serial - INT(serial) >= 0.5, 1, 0)]
			hour12 = TRUE
			res = ""
			WITH Matches.Item(0)
				res = text(serial, .SubMatches(0), hour12) + ampm + text(serial, .SubMatches(2), hour12)
			ENDWITH
			RESULT = res
		CASE reTest(format, "([^ymdagehns]{0,})?(([ymdagehns])\3{0,})([^ymdagehns]+)?")
			Matches = reExecute(format, "([^ymdagehns]{0,})?(([ymdagehns])\3{0,})([^ymdagehns]+)?")
			FOR n = 0 TO Matches.Count - 1
				IF n = 0 THEN res = Matches.Item(n).SubMatches(0)
			NEXT
			FOR n = 0 TO Matches.Count - 1
				WITH Matches.Item(n)
					res = res + text(serial, .SubMatches(1), hour12) + .SubMatches(3)
				ENDWITH
			NEXT
			RESULT = res
		CASE format = "0/0"
			DIM separator = POS(".", serial)
			DIM g = 0
			IFB separator  0 THEN
				DIM keta = LENGTH(serial)
				DIM shift = POWER(10, keta - separator)
				IFB shift >= POWER(10, 15) THEN
					DIM position = 0
					FOR i = 0 TO 14
						IFB serial * POWER(10, i) - serial >= 1 THEN
							position = i
							BREAK
						ENDIF
					NEXT
					tmp = serial * POWER(10, position)
					FOR i = 1 TO 15
						r = (tmp * POWER(10, i)) / serial - (tmp / serial)
						a1 = tmp * POWER(10, i) - tmp
						IF a1 = INT(a1) THEN BREAK 
					NEXT
					DIM frac[] = a1, r
					g = GCD(frac)
					RESULT = (a1/g) + "/" + (r/g)
				ELSE
					DIM molecule = serial * shift	// 分子
					DIM denominator = shift		// 分母
					DIM nums[] = molecule, denominator
					g = GCD(nums)
					molecule = molecule / g
					denominator = denominator / g
					RESULT = molecule + "/" + denominator
				ENDIF
			ELSE
				RESULT = serial + "/1"
			ENDIF
		CASE reTest(format, "(0+)\.?(0+)?") AND UBound(SPLIT(format, ".")) 、>=、=、のいずれかの演算子
// 【戻り値】
//   1つ目のバージョンが2つ目のバージョンより小さい場合-1、同じ場合0、2つ目のバージョンが小さい場合1を返します。演算子を指定したときは、演算子の関係を満たす場合True、満たさない場合はFalseを返します。
//////////////////////////////////////////////////
FUNCTION versionCompare(version1, version2, operator = EMPTY)
	DIM array1 = SPLIT(version1, ".")
	DIM array2 = SPLIT(version2, ".")
	IFB LENGTH(array1)  LENGTH(array2) THEN
		RESULT = ERR_VALUE
		EXIT
	ENDIF
	IFB operator = EMPTY THEN
		FOR i = 0 TO UBound(array1)
			IFB array1[i]  array2[i] THEN
				RESULT = 1
				EXIT
			ENDIF
		NEXT
		RESULT = 0
	ELSE
		FOR i = 0 TO UBound(array1)
			IFB array1[i]  array2[i] THEN
				RESULT = EVAL("array1[i] " + operator + " array2[i]")
				EXIT
			ENDIF
		NEXT
		IFB POS("=", operator) THEN
			RESULT = TRUE
		ELSE
			RESULT = FALSE
		ENDIF
	ENDIF
FEND

解説

    2行目
    UWSC
    3行目
    UWSC
    SeleniumBasicがインストールされているパスをfolderspecに代入します。
    4,14行目
    UWSC
    	PROCEDURE Chrome(bit64, force = FALSE)
    		…
    Google Chromeドライバーのダウンロード処理をする関数。
    5行目
    UWSC
    ChromeBrowserVersion関数でChromeブラウザのバージョンを取得しbrowserVersionに代入します。
    6行目
    UWSC
    WebDriverVersion.iniのChromeセクション、versionキーの値とbroswerVersionの値を比較し、バージョンが違うもしくはforceがTrueの場合は以下に続く処理を続けます。バージョンが同じ、もしくはforceがFalseの場合は処理を中断します。
    7行目
    UWSC
    bit64の値がTrueならば64、Falseならば32をbitに代入します。
    9行目
    UWSC
    ChromeDriverVersion関数を実行し、browserVersionの値からダウンロードするドライバーのバージョンを生成しdriverVersionに代入します。
    10行目
    UWSC
    ChromeDriverURL関数を実行し、driverVersionとbitの値からドライバーのURLを生成し、driverURLに代入します。
    12行目
    UWSC
    InstallChromeDriver関数を実行し、driverURLとbitの値からChromeのWebDriverのインストール処理を実行します。
    15-23行目
    UWSC
    	FUNCTION ChromeBrowserVersion()
    		TRY
    			DIM path = WshShell.RegRead("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe\Path")
    		EXCEPT
    			PRINT "Google Chromeがインストールされていません"
    			EXIT
    		ENDTRY
    		RESULT = TRIM(DOSCMD("DIR /B /O-N " + path + " | FINDSTR ^[0-9].*¥>"))
    Chromeブラウザのバージョンを返す関数です。レジストリのHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe\PathというキーからChromeブラウザがインストールされているパスを取得しpathに代入します。このときエラーが発生した場合はChromeブラウザがインストールされていないと判断し処理を中断します。Chromeブラウザのパスからコマンドプロンプトを使いバージョンを取得します。
    24,36行目
    UWSC
    	FUNCTION ChromeDriverVersion(version)
    		…
    Chromeブラウザのバージョンから対応するWebDriverのバージョンを取得する関数です。
    25行目
    UWSC
    バージョンからメジャーバージョンの部分を取得しmajorに代入します。
    26-30行目
    UWSC
    		IFB major >= 115 THEN
    			DIM url = "https://googlechromelabs.github.io/chrome-for-testing/LATEST_RELEASE_" + major
    		ELSE
    			url = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_" + major
    メジャーバージョンが115以降ならばhttps://googlechromelabs.github.io/chrome-for-testing/LATEST_RELEASE_" + major、115より前ならhttps://chromedriver.storage.googleapis.com/LATEST_RELEASE_" + majorをurlに代入します。
    31-35行目
    UWSC
    		WITH CREATEOLEOBJ("Msxml2.XMLHTTP")
    			.Open("GET", url, FALSE)
    			.Send()
    			RESULT = .ResponseText
    urlを開き、Webページのテキスト全体を戻り値として返します。
    37-47行目
    UWSC
    	FUNCTION ChromeDriverURL(browserVersion, bit)
    		DIM driverVersion = ChromeDriverVersion(browserVersion)
    		DIM major = SPLIT(browserVersion, ".")[0]
    		IFB versionCompare(driverVersion, "121.0.6167.184", ">=") THEN
    			RESULT = "https://storage.googleapis.com/chrome-for-testing-public/" + driverVersion + "/win" + bit + "/chromedriver-win" + bit + ".zip"
    		ELSEIF major >= 115 THEN
    			RESULT = "https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/" + driverVersion + "/win" + bit + "/chromedriver-win" + bit + ".zip"
    		ELSE
    			RESULT = "https://chromedriver.storage.googleapis.com/" + driverVersion + "/chromedriver_win32.zip"
    		ENDIF
    Chromeブラウザのバージョンとビット数からダウンロードするWebDriverのURLを取得します。
    48,56行目
    UWSC
    	PROCEDURE InstallChromeDriver(driverURL, bit)
    		…
    Chromeドライバーのインストール処理を行う関数です。
    49行目
    UWSC
    ダウンロードするZIPファイルのファイル名をchromedriver.zipにします。
    50行目
    UWSC
    driverURLに指定したURLのファイルをダウンロードします。folderspecがダウンロード先のフォルダ名、filenameがファイル名です。ダウンロードに成功した場合、そのZIPファイルを解凍します。
    51行目
    UWSC
    解凍したZIPファイルからchromedriver.exeのパスをpathに代入します。
    52-55行目
    UWSC
    		WITH CREATEOLEOBJ("Scripting.FileSystemObject")
    			.CopyFile(path, folderspec, TRUE)
    			.DeleteFolder(.GetParentFolderName(path))
    ZIPファイルからpathのファイルをfolderspecにコピーし、ZIPファイルを削除します。

WebDriverとは

WebDriverはWebページの操作を自動化するためのインターフェイスです。Seleniumというブラウザを自動操作するためのライブラリとセットで使うため、Selenium オブジェクトを参考に事前にインストールしておいてください。

Seleniumを利用して書かれたプログラムはWebDriverを経由してブラウザの操作を行うのですが、ブラウザやそのバージョンに対応するWebDriverを用意する必要があります。そのためブラウザのアップデートでバージョンが変わるたびにWebDriverもインストールし直さなければならないため手間がかかります。WebDriverDownloadモジュールはそのWebDriverのダウンロード・インストールの処理を自動で行うためのプログラムです。

WebDriverのダウンロード

WebDriverをダウンロードする手順はブラウザごとに異なるためそれぞれ以下に示します。

Chrome

Chromeはブラウザのバージョンによってダウンロード先が異なります。

Ver.115以降(最新版)

Chrome for Testing availabilityを開いて一番上にある表のCannelの列のStable(安定版)、Beta(ベータ版)、Dev(デベロッパー版)、Canary(カナリア版)の中からStableをクリックします。

img src="/wp-content/uploads/udf/webdriverdownload/Chrome for Testing.png" width="1366" height="768"]

リンクを選択するとページ内でStableの表がある位置に移動するので、Binary列にあるchromedriverの中からChromeブラウザのビット数に合わせて32bitならwin32、64ビットならwin64のどちらかに記載しているURLをコピー&ペーストして開きZIPファイルをダウンロードします。

Chrome for Testing Stable
Ver.115以降(特定のバージョン)

Ver.115以降の特定のバージョンのWebDriverをダウンロードするにはhttps://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.jsonのJSONファイルからWebDriverがあるURLを探し出します。

以下はリンク先のJSONファイルの一部を整形したものです。versions配列にはバージョンごとのデータが格納されています。downloadsキー内のchrome配列がOSごとのデータで、Windowsで32bitの場合はplatformキーがwin32、64bitの場合はplatformキーがwin64のurlキーの値がWebDriverのダウンロード先のURLです。

このURLをブラウザのアドレスバーに貼り付けるなどして開き、ZIPファイルをダウンロードします。

JSON
{
    "timestamp": "2024-03-27T08:10:26.912Z",
    "versions": [
        {
            "version": "113.0.5672.0",
            "revision": "1121455",
            "downloads": {
                "chrome": [
                    {
                        "platform": "linux64",
                        "url": "https://storage.googleapis.com/chrome-for-testing-public/113.0.5672.0/linux64/chrome-linux64.zip"
                    },
                    {
                        "platform": "mac-arm64",
                        "url": "https://storage.googleapis.com/chrome-for-testing-public/113.0.5672.0/mac-arm64/chrome-mac-arm64.zip"
                    },
                    {
                        "platform": "mac-x64",
                        "url": "https://storage.googleapis.com/chrome-for-testing-public/113.0.5672.0/mac-x64/chrome-mac-x64.zip"
                    },
                    {
                        "platform": "win32",
                        "url": "https://storage.googleapis.com/chrome-for-testing-public/113.0.5672.0/win32/chrome-win32.zip"
                    },
                    {
                        "platform": "win64",
                        "url": "https://storage.googleapis.com/chrome-for-testing-public/113.0.5672.0/win64/chrome-win64.zip"
                    }
                ]
            }
        },
        {
            "version": "113.0.5672.35",
            "revision": "1121455",
            "downloads": {
                "chrome": [
                    {
                        "platform": "linux64",
                        "url": "https://storage.googleapis.com/chrome-for-testing-public/113.0.5672.35/linux64/chrome-linux64.zip"
                    },
                    {
                        "platform": "mac-arm64",
                        "url": "https://storage.googleapis.com/chrome-for-testing-public/113.0.5672.35/mac-arm64/chrome-mac-arm64.zip"
                    },
                    {
                        "platform": "mac-x64",
                        "url": "https://storage.googleapis.com/chrome-for-testing-public/113.0.5672.35/mac-x64/chrome-mac-x64.zip"
                    },
                    {
                        "platform": "win32",
                        "url": "https://storage.googleapis.com/chrome-for-testing-public/113.0.5672.35/win32/chrome-win32.zip"
                    },
                    {
                        "platform": "win64",
                        "url": "https://storage.googleapis.com/chrome-for-testing-public/113.0.5672.35/win64/chrome-win64.zip"
                    }
                ]
            }
        },
        {
            "version": "113.0.5672.63",
            "revision": "1121455",
            "downloads": {
                "chrome": [
                    {
                        "platform": "linux64",
                        "url": "https://storage.googleapis.com/chrome-for-testing-public/113.0.5672.63/linux64/chrome-linux64.zip"
                    }
                ]
            }
        }
    ]
}
Ver.114以前

Downloads  |  ChromeDriver  |  Chrome for Developersを開き、Chromeブラウザと対応するバージョンのWebDriverを選択します。

ChromeブラウザのVer.70〜114のバージョンに対応しています。Ver.82はブラウザのリリース自体がキャンセルされたのでドライバーもありません。

ダウンロードするWebDriverのバージョンは、以下の<メジャーバージョン>にブラウザのメジャーバージョンを代入しリンクを開くことで確認できます。

プレーンテキスト
https://chromedriver.storage.googleapis.com/LATEST_RELEASE_

ブラウザのバージョンが70.0.3538.110の場合、 メジャーバージョンは一つ目のピリオドまでの数字で70となり以下のようになります。

プレーンテキスト
https://chromedriver.storage.googleapis.com/LATEST_RELEASE_70

このURLを開いてダウンロードするWebDriverのバージョンを取得します。例えば上記のURLの場合、70.0.3538.97というバージョンが取得されます。

https://chromedriver.storage.googleapis.com/index.htmlの項目の中からLATEST_RELEASE_<メジャーバージョン>のファイルを選択することでも確認できます。

次にWebDriverのバージョンからWebDriverをダウンロードするためのURLを生成します。

プレーンテキスト
https://chromedriver.storage.googleapis.com//chromedriver_win32.zip

WebDriverのバージョンが70.0.3538.97の場合、URLは以下のようになります。

プレーンテキスト
https://chromedriver.storage.googleapis.com/70.0.3538.97/chromedriver_win32.zip

このURLを開くとブラウザのバージョンに対応するchromedriver.exeのZIPフォイルがダウンロードされます。

WebDriverのインストール

ダウンロードしたZIPファイル(chromedriver-win32.zip、chromedriver-win64.zip)を解凍しフォルダ内にあるchromedriver.exeをSeleniumをインストールしているフォルダ(%LOCALAPPDATA%\SeleniumBasic\)に移動することでChromeブラウザを起動できるようになります。

Chromeのインストール先のフォルダ

Ver.85より前からChromeをインストールしていた場合、64bitでも32bitと同じC:\Program Files (x86)\にインストールされています。

bit64の引数を省略すると正しく判定できない場合があるので、正しくドライバーがインストールできない場合はブール値を指定してください。

使い方

ChromeブラウザのWebDriverをダウンロード

ChromeブラウザとWebDriverのバージョンが一致しない場合、Chromeブラウザ(64ビット)のWebDriverをダウンロードします。バージョンが一致している場合ダウンロード処理は行いません。

UWSC
WebDriverDownload.Chrome(TRUE)

ChromeブラウザとWebDriverのバージョンが一致しない場合、Chromeブラウザ(32ビット)のWebDriverをダウンロードします。バージョンが一致している場合ダウンロード処理は行いません。

UWSC
WebDriverDownload.Chrome(FALSE)

FirefoxブラウザのWebDriverをダウンロード

FirefoxブラウザとWebDriverのバージョンが一致しない場合、FirefoxブラウザのWebDriverをダウンロードします。

UWSC
WebDriverDownload.Firefox()

Microsoft EdgeブラウザのWebDriverをダウンロード

Microsoft EdgeブラウザとWebDriverのバージョンが一致しない場合、EdgeブラウザのWebDriverをダウンロードします。

UWSC
WebDriverDownload.MsEdge()

プログラム実行例

未読メールの本文からURLを抽出

UWSC
使用関数
解説(自動生成)

参考文献

  1. [仕様変更] Chrome 64-bitの標準フォルダが64-bitフォルダに修正 | SC2
  2. 「C:\Program Files」:2 - 無無無庵2
  3. Releases · mozilla/geckodriver · GitHub
  4. https://www.htmlhifive.com/conts/web/view/Selenium+Grid/driver-download
  5. Downloads  |  ChromeDriver  |  Chrome for Developers
  6. ページが見つかりませんでした |
  7. https://google-chrome.jp.uptodown.com/windows/versions