Edited at

みずほ銀行のホームページからロト6の当選番号をとりだす

More than 1 year has passed since last update.


Summary

F#というスクリプト言語で、みずほ銀行のホームページからロト6の当選番号をとりだす


Environment

mono, paketがなければbrew installでダウンロード

firefoxがなければbrew cask installでダウンロード

$ sw_vers 

ProductName: Mac OS X
ProductVersion: 10.13.4
BuildVersion: 17E199

$ mono --version
Mono JIT compiler version 5.4.1.6 (tarball Mon Dec 11 14:59:42 GMT 2017)
Copyright (C) 2002-2014 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com
TLS: normal
SIGSEGV: altstack
Notification: kqueue
Architecture: amd64
Disabled: none
Misc: softdebug
LLVM: supported, not enabled.
GC: sgen (concurrent by default)

$ paket --version
Paket version 5.155.0

$ brew cask info firefox
firefox: 59.0.2
https://www.mozilla.org/firefox/
/usr/local/Caskroom/firefox/59.0.2 (64B)
From: https://github.com/caskroom/homebrew-cask/blob/master/Casks/firefox.rb
==> Name
Mozilla Firefox
==> Languages
cs, de, en-GB, en, es-AR, es-CL, es-ES, fi, fr, gl, in, it, ja, ko, nl, pl, pt, pt-BR, ru, tr, uk, zh-TW, zh
==> Artifacts
Firefox.app (App)


ホームページの構成

今月の当選番号はindexにあり、過去の当選番号はbucknumberにリンクが貼ってある

index                https://www.mizuhobank.co.jp/retail/takarakuji/loto/loto6/index.html

└── backnumber https://www.mizuhobank.co.jp/retail/takarakuji/loto/backnumber/index.html
├── ChartA https://www.mizuhobank.co.jp/retail/takarakuji/loto/loto6/index.html?year=2018&month=2
├── ChartB_new https://www.mizuhobank.co.jp/retail/takarakuji/loto/backnumber/detail.html?fromto=641_660&type=loto6
└── ChartB_old https://www.mizuhobank.co.jp/retail/takarakuji/loto/backnumber/loto60001.html

それぞれのページの当選番号を示すCSS Selectorは違うので確認しておく

回別
抽選
当選番号

index
table.typeTK > thead > tr > th.alnCenter.bgf7f7f7
table.typeTK > tbody > tr > td[colspan='6'].alnCenter
table.typeTK > tbody > tr > td.alnCenter.extension > strong

ChartA
table.typeTK > thead > tr > th.alnCenter.bgf7f7f7
table.typeTK > tbody > tr > td[colspan='6'].alnCenter
table.typeTK > tbody > tr > td.alnCenter.extension

ChartB_new
div.spTableScroll > table.typeTK > tbody > tr > th.bgf7f7f7
div.spTableScroll > table.typeTK > tbody > tr > td.alnRight
div.spTableScroll > table.typeTK > tbody > tr > td[class='']

ChartB_old
div.spTableScroll > table.typeTK > tbody > tr > th.bgf7f7f7
div.spTableScroll > table.typeTK > tbody > tr > td.alnRight
div.spTableScroll > table.typeTK > tbody > tr > td:not(.alnRight)


htmlをダウンロード

ロト6の当選数字はJavaScriptが実行されないとHtml上に出現しないのでBrowserを通じてHtmlをダウンロードする方法でいく

ライブラリのダウンロード

// fooフォルダを作成

$ mkdir foo
$ cd foo/

// ライブラリをダウンロード
$ paket init
$ vim paket.dependencies

source https://www.nuget.org/api/v2
nuget fsharp.data == 3.0.0-beta3
nuget Selenium.webdriver
nuget Selenium.Support

$ paket install

コードを書く(とりあえずエラー処理はかんがえない)

// File name is foo.fsx

#r "./packages/FSharp.Data/lib/net45/FSharp.Data.dll"
open FSharp.Data

#r "./packages/Selenium.WebDriver/lib/net45/WebDriver.dll"
#r "./packages/Selenium.Support/lib/net45/WebDriver.Support.dll"
open OpenQA.Selenium
open OpenQA.Selenium.Firefox
open OpenQA.Selenium.Support.UI

open System

let url = @"https://www.mizuhobank.co.jp/retail/takarakuji/loto/loto6/index.html?year=2018&month=2"
let kaisuuCSS = @"table.typeTK > thead > tr > th.alnCenter.bgf7f7f7"
let kaisaibiCSS = @"table.typeTK > tbody > tr > td[colspan='6'].alnCenter"
let hitNumberCSS = @"table.typeTK > tbody > tr > td.alnCenter.extension"

type Fox () =

// ブラウザの画面は見えないようにする
let opt = new FirefoxOptions()
do opt.AddArgument("--headless")
let driver = new FirefoxDriver( opt )
// ウエイトタイムはとりえあえず10秒に設定
let wait = WebDriverWait(driver, TimeSpan.FromSeconds(10.))

// ウエイトをかけてhtmlを取得する
member this.HtmlWithJS(url) =

// 指定したアドレスのホームページに移動
driver.Url <- url

// 指定したCSSのinnerTextが出現するまで待つ(最大10秒)
// innerTextが出現しなかったらエラー
wait.Until( fun (driver:IWebDriver) ->
[
driver.FindElements( By.CssSelector( kaisuuCSS ))
driver.FindElements( By.CssSelector( kaisaibiCSS ))
driver.FindElements( By.CssSelector( hitNumberCSS ))
]
|> Seq.concat
|> Seq.forall ( fun (x:IWebElement) -> x.Text <> String.Empty )
) |> ignore

// htmlをかえす
driver.PageSource

member this.Quit() =
driver.Quit()

// Firefoxブラウザを起動する
let f = Fox()

// みずほ銀行のロト6の当選番号が書いてあるホームページからhtmlをダウンロード
f.HtmlWithJS url

// htmlからロト6の当選番号を取り出す
|> HtmlDocument.Parse
|> fun doc ->
let index = doc.CssSelect( kaisuuCSS ) |> List.map ( fun n -> n.InnerText() |> fun s -> String.filter Char.IsDigit s )
let date = doc.CssSelect( kaisaibiCSS ) |> List.map ( fun n -> n.InnerText() |> fun s -> s.Replace("年","/").Replace("月","/").Replace("日",""))
let numbers = doc.CssSelect( hitNumberCSS ) |> List.map ( fun n -> n.InnerText() ) |> List.chunkBySize 7 |> List.map ( List.truncate 6 )
(index, date, numbers)
|||> List.map3 ( fun a b c -> [a] @ [b] @ c )
|> List.iter( fun l -> printfn "%A" l )

f.Quit()

実行してみる

$ fsharpi foo.fsx

// firefoxの処理内容が色々出力される・・・

["1255"; "2018/2/26"; "02"; "14"; "15"; "27"; "40"; "43"]
["1254"; "2018/2/22"; "07"; "08"; "11"; "15"; "36"; "39"]
["1253"; "2018/2/19"; "01"; "12"; "14"; "24"; "33"; "37"]
["1252"; "2018/2/15"; "04"; "15"; "18"; "19"; "22"; "29"]
["1251"; "2018/2/12"; "21"; "28"; "31"; "33"; "34"; "40"]
["1250"; "2018/2/8"; "03"; "10"; "18"; "22"; "23"; "40"]
["1249"; "2018/2/5"; "05"; "08"; "15"; "20"; "25"; "27"]
["1248"; "2018/2/1"; "02"; "06"; "14"; "28"; "34"; "37"]

エラー処理を加える

timeoutほか のときに FireFox を quit する

"Stale Element Reference Exception" が多発するのでその処理

コード例はここを参照

https://github.com/callmekohei/lotofs/blob/master/src/mizuho.fsx#L163

about Stale Element Reference Exception

https://docs.seleniumhq.org/exceptions/stale_element_reference.jsp


wait.Untilの動きをみてみる

htmlファイルを作成

<html>

<body>
<table>
<tr>
<td>foo</td>
<td></td>
<td class=abc>baz</td>
</tr>
</table>
</body>
</html>

<style>
table {
border-collapse: collapse;
}
td {
border: solid 1px;
padding: 0.5em;
}
</style>

コードを書いてみる

// File name is bar.fsx

#r "./packages/Selenium.WebDriver/lib/net45/WebDriver.dll"
#r "./packages/Selenium.Support/lib/net45/WebDriver.Support.dll"
open OpenQA.Selenium
open OpenQA.Selenium.Firefox
open OpenQA.Selenium.Support.UI
open System

type Fox () =

let opt = new FirefoxOptions()
do opt.AddArgument("--headless")
let driver = new FirefoxDriver( opt )
let wait = WebDriverWait(driver, TimeSpan.FromSeconds(10.))
let mutable cnt = 0

member this.Html(url) =
try
driver.Url <- url
wait.Until( fun (driver:IWebDriver) ->
stdout.WriteLine(cnt)
cnt <- cnt + 1
[
driver.FindElements( By.CssSelector( "html > body > table > tbody > tr > td:not(.abc)" ))
driver.FindElements( By.CssSelector( "html > body > table > tbody > tr > td.abc" ))
]
|> Seq.concat
|> Seq.forall ( fun (x:IWebElement) -> x.Text <> String.Empty )
) |> ignore
driver.PageSource
with e ->
driver.Quit()
stdout.WriteLine(e.Message)
String.Empty

member this.Quit() =
driver.Quit()

let f = Fox()
@"file:///Users/callmekohei/Desktop/foo.html"
|> f.Html
stdout.WriteLine("foo bar baz")
f.Quit()

結果

10秒のウエイトで20回近くポーリングが行われてるのが確認できる

// いろいろfirefoxの表示が出る

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Timed out after 10 seconds
foo bar baz


CSSセレクタでのnot

FSharp.DataCssSelectornotがつかえない(2018/4/11現在)

notを使う場合は下記のようにする

// css selector in Selenium

@"div.spTableScroll > table.typeTK > tbody > tr > td:not(.alnRight)"

// css selector in FSharp.Data
@"div.spTableScroll > table.typeTK > tbody > tr > td.''"


参考

see left column : WebDriverWait Class: https://seleniumhq.github.io/selenium/docs/api/dotnet/

https://qiita.com/Azunyan1111/items/b161b998790b1db2ff7a

http://blog.okazuki.jp/entry/2015/01/28/205817

http://hutyao.hatenablog.com/entry/20110908/1315490306

https://qiita.com/scivola/items/08a717eaba9c349fd6ba