とりあえずブラウザ操作(IE.App, Selenium)

最近、ブラウザを自動化する必要性があって色々調べていた。
ここではそのとりあえず動かすところまでについてまとめる。

## InternetExplorer.Application
Windows環境であれば追加で何もインストールが必要にならないのが取り柄。
使用できる環境としてはActiveXコントロールを扱えるものが対象になる。
具体的にはVBA・WSH/HTA(VBS/JScript)・HSP・.NET Framework等。
情報源としてはVBAやVBSの物が多い。
しかし、言語間でも情報の互換性がそれなりにあるので各言語について
把握していれば情報の利用は難しくない。
ここでは、VBAと.NET言語
(dll参照が必要(C:\Windows\System32\内のSHDocVw.dll、mshtml.dll))
は省略する。

### VBScript
```vb
Option Explicit
Dim ie:Set ie=CreateObject("InternetExplorer.Application")
Sub ieWait: While ie.Busy Or 4<>ie.readyState: WScript.Sleep 100: Wend: End Sub
ie.Visible=True
ie.Navigate "http://google.com/"

ieWait
ie.document.getElementsByName("q")(0).value="Hello World"

WScript.Sleep 5000
ie.document.getElementsByName("f")(0).submit

WScript.Sleep 5000
ie.Quit
```
情報量が多いので最も書きやすいかもしれない環境。
ieWait関数ではIEが動作を待機する。
無限ループを緩和するためにWScript.Sleepを使っているが、
HTA上で使用する場合はsetTimeoutを駆使して何とかする必要がある。

### JScript
```js
var ie=new ActiveXObject("InternetExplorer.Application");
var ieWait=function(){while(ie.Busy || 4!=ie.readyState) WScript.Sleep(100);};
ie.Visible=true;
ie.Navigate("http://google.com/");

ieWait()
ie.document.getElementsByName("q")[0].value="Hello World";

WScript.Sleep(5000);
ie.document.getElementsByName("f")[0].submit();

WScript.Sleep(5000);
ie.Quit();
```
VBSと大体同じ。
注意点はCreateObjectがnew ActiveXObjectに置き変わるくらい。

### HSP3
```C++
#runtime "hsp3cl"
#cmpopt varinit 1
dimtype ie,6:newcom ie,"InternetExplorer.Application"
#define ieWait while ie@("Busy") | 4!ie@("readyState"): wait 10: wend
ie("Visible")=1
ie->"Navigate" "http://google.com/"

ieWait
document=ie("document")
dimtype o,6:comres o
document->"getElementsByName" "q":o=o("item",0):o("value")="Hello World"

wait 500
document->"getElementsByName" "f":o=o("item",0):o->"submit"

wait 500
ie->"Quit"
```
HSPは標準で各種comオブジェクトを取り扱うことができるためWSHと同様に取り扱える。
ただし、HSPとしての情報源は多くないためその他の言語で調べる必要もあると思う。

#### 文法に関する注意点
文法面では例えば、HSPでは"."によるオブジェクト構造が使えないため、下層のオブジェクトをcomresの変数に代入して操作を行う。
メソッドの実行には"->"という演算子を用いる。引数を指定する場合は空白の後に","でつなぐ。
プロパティ参照については関数形式で文字列を指定することで行える。取得も適用も同様に可。
なお、配列値の取得はitemプロパティから行う。

## Selenium
今ブラウザを操作するならこれって感じで情報がかなり見つかる。
情報量としては圧倒的にPythonが多い。
ちなみに公式ではJava、C#、Python、Ruby、Node.jsに対応しているが、言語ごとのメソッドの構成は大きく異なり情報の互換性はない。
ここではChromeをPython、Node.js、.NET(C#,VB,F#)で扱うことについて記載する。

まずは[ChromeDriver](http://chromedriver.chromium.org/downloads)をダウンロードしてどこでもいいのでパスを通しておく(使用中のChromeのバージョンと合っていることを確認しする)。

### Python3
```:ライブラリインストール
pip install selenium
```
```py
import time
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.alert import Alert

driver=webdriver.Chrome()
wait=lambda ele:WebDriverWait(driver,10).until(ele)
driver.get("https://www.google.com/")

searchBox=wait(lambda drv:drv.find_element_by_name("q"))
searchBox.send_keys("Hello World")

time.sleep(5)
searchBox.submit()

time.sleep(5)
driver.quit()
```
WebDriverWait.untilの中身はEC.○○○の形で入れている例が多く見つかるのだけれど、C#で同じ形式を入れた所古い形式だとの警告があった。C#ではラムダ式を入れている表記が多く見つかり、Pythomnでもこれが使用できたので適用した。

### Node.js
```:ライブラリインストール
npm i -g selenium-webdriver
```
```js
const util=require("util");
const setTimeoutp=util.promisify(setTimeout);
const {Builder,By,Key,until}=require("selenium-webdriver");

(async function(){
	const driver=new Builder().forBrowser("chrome").build();

	const wait=ele=>driver.wait(ele,10)
	driver.get("http://www.google.com");

	var searchBox=await wait(drv=>drv.findElement(By.name("q")));
	searchBox.sendKeys("Hello World");

	await setTimeoutp(5000);
	searchBox.submit();

	await setTimeoutp(5000);
	driver.quit();
})();
```
個人的に公式の中では最も扱いが難しそうに感じる。
awaitはどこまで必要なのかわからないけど、問題にならない範囲で省いた。
あとはWebDriverWaitの実装が異なるみたいでWebDriver.waitを使用する。
あとはjsらしくfindElementByIdとか書かせて欲しかった…

## .NET
ここでは.Net Coreでの使用を例とする。
とりあえずコンソールプロジェクトとして下記コマンドを入れて(○○○は任意のプロジェクト名)用意する。

```:言語ごとにいずれか
dotnet new console          -n ○○○
dotnet new console -lang vb -n ○○○
dotnet new console -lang f# -n ○○○
```
```:フォルダ移動
cd ○○○
```
```:ライブラリインストール
dotnet add package Selenium.WebDriver --version 3.141.0
dotnet add package Selenium.Support --version 3.141.0
dotnet add package Selenium.WebDriverBackedSelenium --version 3.141.0
```

公式ではC#しか謳われていないがVBやF#でも問題なく使用することができる。

### C<span>#</span>
```cs
using System;
using System.Threading;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;

class Program{
	static void Main(){
		var driver=new ChromeDriver();
		Func<Func<IWebDriver,IWebElement>,IWebElement> wait=
			ele=>new WebDriverWait(driver,TimeSpan.FromSeconds(10)).Until(ele);
		driver.Url="https://www.google.com/";

		var searchBox=wait(drv=>driver.FindElement(By.Name("q")));
		searchBox.SendKeys("Hello World");

		Thread.Sleep(5000);
		searchBox.Submit();

		Thread.Sleep(5000);
		driver.Quit();
	}
}
```
何故かURLの指定が他の言語でgetメソッドな所Uelプロパティへの代入になっている。

### VB
```vb
Option Strict On
Option Infer On
Imports System.Threading
Imports OpenQA.Selenium
Imports OpenQA.Selenium.Chrome
Imports OpenQA.Selenium.Support.UI

Module Program
	Sub Main()
		Dim driver As New ChromeDriver()
		Dim wait As Func(Of Func(Of IWebDriver,IWebElement),IWebElement)=
			Function(ele) New WebDriverWait(driver,TimeSpan.FromSeconds(10)).Until(ele)
		driver.Url="https://www.google.com/"

		Dim searchBox=wait(Function(drv) driver.FindElement(By.Name("q")))
		searchBox.SendKeys("Hello World")

		Thread.Sleep(5000)
		searchBox.Submit()

		Thread.Sleep(5000)
		driver.Quit()
	End Sub
End Module
```
大体C#と同じ。
これ以外の機能もほぼフルで使用できるはず。

###F<span>#</span>
```fsharp
open System
open System.Threading
open OpenQA.Selenium
open OpenQA.Selenium.Chrome
open OpenQA.Selenium.Support.UI

let driver=new ChromeDriver()
let wait=new WebDriverWait (driver,TimeSpan.FromSeconds 10.0)

driver.Url<-"https://www.google.com/"

let searchBox=wait.Until(fun drv->(driver.FindElement) (By.Name "q"))
searchBox.SendKeys "Hello World"

Thread.Sleep 5000
searchBox.Submit()

Thread.Sleep 5000
driver.Quit()
```
とりあえずF#。
かなり疎い言語なのでとりあえず動かしただけ。


以上。

### ソースとか
https://github.com/yosgspec/AutoBrowsingTest