PowerShellからWatiNを利用

ポイント

  • WatiN.Core.dllは、Add-Type -Path で読み込む。
  • WatiN.Core.Findクラスの利用は、
		[Watin.Core.Find]::ByName("fmInput2")
		[Watin.Core.Find]::ByIndex(1)

のように行う。

PowerShell用コード

# Set WatiN.Core.dll
Add-Type -Path "C:\Users\mikihiro\Documents\Visual Studio 2010\Projects\WatiNStudy\WatiNStudy\bin\Debug\WatiN.Core.dll"

# new InternetExplorer
$ie = New-Object WatiN.Core.IE

$ie.GoTo("http://www.moderns.co.jp/trade/devtest/index.html")
	
$elem = [Watin.Core.Find]::By("name","fmInput1")
$ie.TextField($elem).value = "test1"

$ie.TextField([Watin.Core.Find]::ByName("fmInput2")).value = "test2"

$ie.CheckBox([Watin.Core.Find]::ByName("CB3")).Checked = $true

# CheckBoxValueが"C"なのは複数あるが、そのうち最初のCheckBoxがチェックされる。
$ie.CheckBox([Watin.Core.Find]::ByValue("C")).Checked = $true

# Group32番目。.Andで繋げることができる。
$ie.RadioButton([Watin.Core.Find]::ByName("Group3").And([Watin.Core.Find]::ByIndex(1))).Checked = $true

$sel = $ie.SelectList([Watin.Core.Find]::By("name","DropDown"))
#$sel.Exists
#$sel | Get-Member -Type Method 

#$sel.Option([Watin.Core.Find]::ByValue("2")).Select()
$sel.Option([Watin.Core.Find]::ByText("選択肢2")).Select()

# 「開発用フォーム入力画面」がInnerTextに含まれているので、Trueがコンソールに表示される。
$ie.ContainsText("開発用フォーム入力画面")

# 最初の送信ボタンをクリック。
#$ie.Button([Watin.Core.Find]::ByValue("送信")).Click()

# 2番目の送信ボタンをクリック。
#$buttons = $ie.Buttons.Filter([Watin.Core.Find]::ByValue("送信"))
#$buttons.Item(1).Click()
環境

WatiNを利用してIEを操作するPowerShell用スクリプトを、.Netのプログラム上で読み込んで実行する。

  • System.Management.Automation.dll
  • WatiN.Core.dll

を参照に追加する。

VB.Netのコード

Form1にButtonを配置しておく。

Imports System.Management.Automation
Imports System.Management.Automation.Runspaces

Public Class Form1

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click

        Dim runspaceConfiguration__1 As RunspaceConfiguration = RunspaceConfiguration.Create()
        Dim runspace As Runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration__1)

        runspace.ApartmentState = System.Threading.ApartmentState.STA
        runspace.ThreadOptions = PSThreadOptions.UseCurrentThread

        runspace.Open()
        Dim invoker As New RunspaceInvoke()
        invoker.Invoke("Set-ExecutionPolicy Unrestricted")

        Dim pipeline As Pipeline = runspace.CreatePipeline()
        Dim myCmd As New Command("C:\Users\mikihiro\Desktop\201205\Watin.ps1")

        'Dim param As New CommandParameter("responseCollection", test)
        'myCmd.Parameters.Add(param)
        pipeline.Commands.Add(myCmd)

        ' Execute PowerShell script
        Dim results As ObjectModel.Collection(Of PSObject) = pipeline.Invoke()


    End Sub


End Class

Watin.ps1

[System.Reflection.Assembly]::LoadFile("C:\Users\mikihiro\Documents\Visual Studio 2010\Projects\WatiN-2.1.0.1196\source\src\WindowsApplication1\bin\Release\WatiN.Core.dll")
$ie = New-Object WatiN.Core.IE
$ie.GoTo("http://www.moderns.co.jp/trade/")

ファイルパスは設定し直して下さい。

ザラ場ウォッチ

前から考えていた手法が成り立つか、ザラ場をウオッチした。
損切りのラインをどうするかが難しそうだな。
決算銘柄は難しいので触らないのが良いだろうな。
けれども、バックテストではどれが決算発表の日なのか判らんし、これを省くことは出来そうにないんだよな。
うーん、色々と難しい。

バックテストでがっかり

2、3日かけてプログラムを作成して行ったバックテストの結果が(想定していたけど)あんまり良くなかったがっかり感。ひょっとしたら、結構良い成績になるんじゃないか、と思ったけど、やはり駄目だった。
まあ、これで一つ調べるべき事をつぶせたので、前進したと考えよう。

HTML解析ソフト

HTML解析ソフトを公開しました。
システムトレードの自動売買用スクリプト作成を支援するためのソフトですが、IE9を操作するプログラムの参考になるはずです。
ソースコードも公開してあります。

IE9をType.InvokeMemberで操作

IE9では、Type.InvokeMemberを利用して、IEを操作することになるのだけど、情報が少ない。
従って、実験を繰り返して調べることになる。
ここでは、その研究結果のコードを掲載しておきます。もっと洗練された方法があるはずだけれど、とりあえず動くものを作るのが必要なので、野暮ったいのは勘弁して下さい。
IE8までは、もっと簡単なコードで充分だったのに、IE9では厳密に指定しないと駄目。

Visual Studio 2010 / VB.net
http://www.moderns.co.jp/files/UsingIE9Object.zip

Imports System.Reflection

Public Class Form1

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click

        'BindingFlags.GetProperty などを短く置き変えることが出来る。
        'Dim getPr As BindingFlags = BindingFlags.GetProperty
        'Dim setPr As BindingFlags = BindingFlags.SetProperty
        'Dim invMe As BindingFlags = BindingFlags.InvokeMethod


        Dim ieObj As Object

        ieObj = CreateObject("InternetExplorer.application") 'IEのオブジェクトを作る
        If ieObj Is Nothing Then
            MsgBox("IE Nothing")
        End If

        ieObj.Visible = True '見えるようにする
        Dim uri As String = My.Computer.FileSystem.CombinePath(My.Application.Info.DirectoryPath, "IE9.html")
        'uri = "http://www.moderns.co.jp/"
        Try
            ieObj.Navigate2(uri)
        Catch
            MsgBox("file not found:" & vbCrLf & uri)
            Exit Sub
        End Try

        Call ieWait(ieObj)



        Dim ieDoc As Object
        Dim typ As Type



        typ = Type.GetTypeFromProgID("mhtmlfile")
        'あるいは、こちらでもOK。
        'typ = Type.GetTypeFromCLSID(New System.Guid("{3050F4E7-98B5-11CF-BB82-00AA00BDCE0B}"))

        'ieDoc = ieObj.Document             'IE8までは、こうした文法で良かった。IE9では、以下のようにするのが良い。
        ieDoc = typ.InvokeMember("document", BindingFlags.GetProperty, Nothing, ieObj, Nothing)

        'Title
        TextBox1.Text &= "title: " & typ.InvokeMember("title", BindingFlags.GetProperty, Nothing, ieDoc, Nothing) & vbCrLf

        'URLは大文字
        TextBox1.Text &= "URL: " & typ.InvokeMember("URL", BindingFlags.GetProperty, Nothing, ieDoc, Nothing) & vbCrLf

        'Linkの数
        TextBox1.Text &= "Link count : " & typ.InvokeMember("links", BindingFlags.GetProperty, Nothing, ieDoc, Nothing).length & vbCrLf

        'TextBox1.Text &= "URL: " & GetP("URL", ieDoc) & vbCrLf
        'TextBox1.Text &= "Link count : " & GetP("links", ieDoc).length & vbCrLf

        'Typeを表示 mshtml.HTMLAnchorElementClass となる。
        TextBox1.Text &= typ.InvokeMember("links", BindingFlags.GetProperty, Nothing, ieDoc, Nothing)(1).ToString & vbCrLf

        '2番目のリンクのInnerText
        TextBox1.Text &= "Second Link InnterText : " & typ.InvokeMember("innerText", BindingFlags.GetProperty, Nothing, typ.InvokeMember("links", BindingFlags.GetProperty, Nothing, ieDoc, Nothing)(1), Nothing) & vbCrLf

        '2番目のリンクをクリックする。
        'Dim links As Object = typ.InvokeMember("links", BindingFlags.GetProperty, Nothing, ieDoc, Nothing) 'link 全部のObject
        'typ.InvokeMember("click", BindingFlags.InvokeMethod, Nothing, links(1), Nothing) 'links(1)は、2番目のlinkを意味する。

        'あるいは、こちらでも同じ。
        'Dim link2 As Object = typ.InvokeMember("links", BindingFlags.GetProperty, Nothing, ieDoc, Nothing)(1)   '2番目のlinkのObject (0)1番目 (1)2番目 (2)3番目 ・・・
        'typ.InvokeMember("click", BindingFlags.InvokeMethod, Nothing, link2, Nothing)

        'Call ieWait(ieObj)

        TextBox1.Text &= vbCrLf

        'Body Elementを確定する2種類の方法 
        '1 how to get the body object
        Dim htmlBody As Object
        htmlBody = typ.InvokeMember("body", BindingFlags.GetProperty, Nothing, ieDoc, Nothing)
        'htmlBody = GetP("body", ieDoc)
        TextBox1.Text &= "body innerHTML : " & typ.InvokeMember("innerHTML", BindingFlags.GetProperty, Nothing, htmlBody, Nothing) & vbCrLf & vbCrLf

        '2 how to get the body object
        Dim htmlBody2 As Object
        htmlBody2 = typ.InvokeMember("getElementsByTagName", BindingFlags.InvokeMethod, Nothing, ieDoc, {"body"})(0)
        'htmlBody2 = InvokeM("getElementsByTagName", ieDoc, {"body"})(0)
        TextBox1.Text &= "body innerHTML : " & typ.InvokeMember("innerHTML", BindingFlags.GetProperty, Nothing, htmlBody2, Nothing) & vbCrLf & vbCrLf


        'Form Element
        Dim formElm As Object = typ.InvokeMember("getElementsByName", BindingFlags.InvokeMethod, Nothing, ieDoc, {"formName1"})

        Dim inputElm As Object = typ.InvokeMember("getElementsByTagName", BindingFlags.InvokeMethod, Nothing, formElm(0), {"input"})


        'InputBox  inputElmを利用
        For i As Integer = 0 To inputElm.length - 1
            TextBox1.Text &= typ.InvokeMember("name", BindingFlags.GetProperty, Nothing, inputElm(i), Nothing) & vbCrLf
            If typ.InvokeMember("name", BindingFlags.GetProperty, Nothing, inputElm(i), Nothing) = "siteurl" Then
                typ.InvokeMember("value", BindingFlags.SetProperty, Nothing, inputElm(i), {"input value"})
            End If
        Next

        'CheckBox  inputElmを利用
        For i As Integer = 0 To inputElm.length - 1
            TextBox1.Text &= typ.InvokeMember("name", BindingFlags.GetProperty, Nothing, inputElm(i), Nothing) & vbCrLf
            If typ.InvokeMember("name", BindingFlags.GetProperty, Nothing, inputElm(i), Nothing) = "c1" Then
                typ.InvokeMember("checked", BindingFlags.SetProperty, Nothing, inputElm(i), {"True"})
            End If
        Next

        'RadioButton  inputElmを利用
        For i As Integer = 0 To inputElm.length - 1
            TextBox1.Text &= typ.InvokeMember("name", BindingFlags.GetProperty, Nothing, inputElm(i), Nothing) & vbCrLf
            If typ.InvokeMember("name", BindingFlags.GetProperty, Nothing, inputElm(i), Nothing) = "r1" Then
                typ.InvokeMember("checked", BindingFlags.SetProperty, Nothing, inputElm(i), {"True"})
            End If
        Next


        'Select and Option
        Dim selectElm As Object = typ.InvokeMember("getElementsByName", BindingFlags.InvokeMethod, Nothing, ieDoc, {"sele1"})(0)
        Dim optionsElm As Object = typ.InvokeMember("getElementsByTagName", BindingFlags.InvokeMethod, Nothing, selectElm, {"option"})

        For i As Integer = 0 To optionsElm.length - 1
            TextBox1.Text &= typ.InvokeMember("innerText", BindingFlags.GetProperty, Nothing, optionsElm(i), Nothing) & vbCrLf
            If typ.InvokeMember("innerText", BindingFlags.GetProperty, Nothing, optionsElm(i), Nothing) = "def" Then
                typ.InvokeMember("selected", BindingFlags.SetProperty, Nothing, optionsElm(i), {"True"})
            End If
        Next


        'TextArea
        Dim textAElem As Object
        textAElem = typ.InvokeMember("getElementsByName", BindingFlags.InvokeMethod, Nothing, ieDoc, {"te1"})
        'textAElem = InvokeM("getElementsByName", ieDoc, {"te1"})

        typ.InvokeMember("value", BindingFlags.SetProperty, Nothing, textAElem(0), {"textarea value"})
        'Call SetP("value", textAElem(0), {"textarea value"})



        'Form InnerHTML
        TextBox1.Text &= vbCrLf & "Form innerHTML : " & GetP("innerHTML", InvokeM("getElementsByName", ieDoc, {"formName1"})(0)) & vbCrLf

    End Sub

    ''' <summary>
    ''' IEの表示が完了するまで待つ
    ''' </summary>
    ''' <param name="ieObj"></param>
    ''' <remarks></remarks>
    Sub ieWait(ByRef ieObj As Object)

        Try

            Dim sw As New Stopwatch
            sw.Start()

            Do While (ieObj.Busy OrElse ieObj.ReadyState <> SHDocVw.tagREADYSTATE.READYSTATE_COMPLETE) _
                        AndAlso sw.Elapsed <= New TimeSpan(0, 0, 0, 20)     '読み込み中でも、CheckWatingSecond(20秒)を超えたら抜ける
                Application.DoEvents() '読み込み中なら待つ
            Loop

            sw = Stopwatch.StartNew()
            Do While sw.Elapsed <= New TimeSpan(0, 0, 0, 0, 1000)   '念のため読み込みが終わってもさらに1秒待つ
                Application.DoEvents()
            Loop
            sw.Reset()

        Catch ex As Exception
            'IEが閉じられていた場合などエラーになるので、それをキャッチする。
            MsgBox("Error")


        End Try

    End Sub

    ''' <summary>
    ''' InvokeMemberの、GetPropertyを実行
    ''' </summary>
    ''' <param name="name1"></param>
    ''' <param name="target1"></param>
    ''' <param name="args1"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function GetP(ByVal name1 As String, ByVal target1 As Object, Optional ByVal args1 As Object = Nothing)

        Dim typ As Type = Type.GetTypeFromProgID("mhtmlfile")
        Return typ.InvokeMember(name1, BindingFlags.GetProperty, Nothing, target1, args1)

    End Function

    ''' <summary>
    ''' InvokeMemberの、SetPropertyを実行
    ''' </summary>
    ''' <param name="name1"></param>
    ''' <param name="target1"></param>
    ''' <param name="args1"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function SetP(ByVal name1 As String, ByVal target1 As Object, Optional ByVal args1 As Object = Nothing)

        Dim typ As Type = Type.GetTypeFromProgID("mhtmlfile")
        Return typ.InvokeMember(name1, BindingFlags.SetProperty, Nothing, target1, args1)

    End Function


    ''' <summary>
    ''' InvokeMemberの、InvokeMethodを実行
    ''' </summary>
    ''' <param name="name1"></param>
    ''' <param name="target1"></param>
    ''' <param name="args1"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function InvokeM(ByVal name1 As String, ByVal target1 As Object, Optional ByVal args1 As Object = Nothing)

        Dim typ As Type = Type.GetTypeFromProgID("mhtmlfile")
        Return typ.InvokeMember(name1, BindingFlags.InvokeMethod, Nothing, target1, args1)

    End Function

End Class

対象のHTML IE9.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="ja">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
	<title>IE9 Object</title>
</head>
<body>



<p><a href="http://www.moderns.co.jp/">modern design laboratory</a></p>
<p><a href="http://www.msignal.info/">M.Signal</a></p>

<p><a href="http://www.linkget.info/">LinkGet</a></p>

<form action="form1.html" name ="formName1" method="get">

<input type="text" name="siteurl" size="20" />

<input type="checkbox" name="c1" value="">
<input type="checkbox" name="c2" value="">

<input type="radio" name="r1" value="b1">
<input type="radio" name="r1" value="b2">
<input type="radio" name="r1" value="b3">

<select name="sele1" size="1" tabindex="0">
<option value="">abc</option>

<option value="">def</option>
<option value="">ghi</option>
</select>


<textarea name="te1" rows="2" cols="20" tabindex="0"></textarea>

</form>


</body>
</html>