ブラウザもSeleniumも使わずExcelVBAでスクレイピング
以前こういう記事を書きました。
このとき、SeleniumBasicをインストールしないといけないのとWebdriverをダウンロードしてこないといけないのがめんどうでした。
イメージとしてはこんな感じです↓
さらに以前こういう記事を書きました。
このときはSeleniumは使わず(使えず)、Webdriverを使ってHTTP通信することになりました。これも結構めんどうでした。
そこで今回は、こういうイメージのことをやりたいと思います。
ただし、Webdriverを使わないのでClickとか動的な情報は難しいです。
できるのかもしれませんが、私にはやり方を見つけることはできませんでした。
静的情報をとってくるだけなら、今回紹介する方法でなんとかなりますという話です。
コード
今回こちらのサイトを参考にしました。
【ExcelVBA】HTTP/HTTPS通信でWebページを取得する
https://qiita.com/nkojima/items/c11f6369f4c32b06c90b
それから、上記のサイトでも言及がありますが、こちら↓のサイトも参考にしました。
エクセルVBAでHTTPリクエストをする最も簡単なプログラム
https://tonari-it.com/excel-vba-http-request/
Option Explicit
'HTTP通信用クラス
Private client As Object
Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal ms As Long)
'コンストラクタ
Private Sub Class_Initialize()
Set client = CreateObject("MSXML2.ServerXMLHTTP")
End Sub
'デストラクタ
Private Sub Class_Terminate()
Set client = Nothing
End Sub
'引数のURLをGETで取得
'
'url:URL文字列
'return:取得したページ
'
Public Function GetPage(url As String) As String
client.Open "GET", url
Sleep 2000 'スクレイピングのマナー(1000以上を設定のこと)
client.Send
'readyStateの値が4で読み込みが完了
Do While client.readyState < 4
DoEvents
Loop
Dim statusCode As Integer
statusCode = client.Status
Dim responseText As String
Dim lNum1 As Long, lNum2 As Long, stMoji As String
'HTTPのステータスコードが200(OK)以外であれば、ステータスコードなどを返す
If (statusCode = 200) Then
'取得した文字列を変数へ
responseText = client.responseText
'文字コード把握
lNum1 = InStr(1, responseText, "charSet")
lNum2 = InStr(lNum1, responseText, ">")
stMoji = Mid(responseText, lNum1, lNum2 - lNum1)
stMoji = Replace(stMoji, "charSet", "")
stMoji = Replace(stMoji, "=", "")
stMoji = Replace(stMoji, "/", "")
stMoji = Replace(stMoji, " ", "")
stMoji = Replace(stMoji, """", "")
Select Case LCase(stMoji)
Case "utf-8"
GetPage = client.responseText
Case Else
GetPage = StrConv(client.responseText, vbUnicode)
End Select
Else
GetPage "HTTP StatusCode:" & statusCode & "," & vbCrLf & "HTTP StatusText:" & client.statusText
End If
End Function
上記はクラスモジュールに記述します。クラスモジュールの名称は「Http」にします。
このクラスを使うサブは下記のようなコードになります。
Sub HTTPリクエスト()
Dim client As Http
Set client = New Http
Dim response As String
response = client.GetPage("https://www.yahoo.co.jp/")
' Dim localHtml As HTMLDocument '←これだとコンパイルエラーになる
Dim localHtml As Object
Set localHtml = New HTMLDocument 'CreateObject("htmlfile")
localHtml.write response 'HTTP通信でとってきたテキストデータをlocalHtml変数に書き込む
Debug.Print localHtml.innerText
Set localHtml = Nothing
Set client = Nothing
End Sub
localHtmlというローカルの変数にHTML文書を書き込んでしまいます(localHtml.write responseがそれです)ので、ネットワーク負荷がそれほどかからず、相手先サーバに迷惑をかけなくて済むのではないかと思いました。
参照設定
基本的に私は参照設定は最小限にしたい派なんですが、今回ばかりはMicrosoft HTML Object Libraryにチェックを入れました。
使えるプロパティとメソッドが分からないので参照設定しておいた方が入力補助してくれて便利です。
解説と問題点
せっかく参照設定したのに、どういう訳かDim localHtml As HTMLDocumentとした状態でlocalHtml.write responseと書くと、コンパイルのときエラーとなりました。
理由は不明です。
これ以外にも、htmlfileには不思議な現象がたくさんありました。
?Typename(localHtml)とイミディエイトペインで入力すると、HTMLDocumentとでるくせに、CreatObjectで”HTMLDocument”とするとだめで、”htmlfile”としないといけません(全部小文字)。
× CreateObject(“HTMLDocument”)
○ CreateObject(“htmlfile”)
これも理由は不明。マイクロソフトがそういう風に作った(そういう風にしか作れなかった)としか説明できません。
それから、As HTMLDocumentとしてコーディングしているときに、入力補助で選択肢にgetElementByIdとかgetElementsByClassNameとか出てきますが、動作確認ができたのはgetElementsByTagNameだけでした。
これも理由は不明です。
器だけ作ったけど、中身は実装しなかった・・・とか?
私の使い方がうまくないのかもしれません。階層の深いHTML文書では、Childの要素を順番にたどっていかないといけないのかもしれません・・・。これを確認する時間がありませんでした。
要素をとってくるコード
上記のコードではただHTMLドキュメント全体をとってくるだけですが、それではあまりにも役に立たないので、もう少し内容を細かく取得するようにしたのが次のコードです。
Option Explicit
Sub HTTPリクエスト()
Dim client As Http
Set client = New Http
Dim response As String
response = client.GetPage("https://www.yahoo.co.jp/")
' Dim localHtml As HTMLDocument
Dim localHtml As Object
Set localHtml = New HTMLDocument 'CreateObject("htmlfile")
localHtml.write response
Dim nodes As Object
Set nodes = localHtml.getElementsByTagName("div")
Dim node As HTMLUListElement
Const cstClassName As String = "TRuzXRRZHRqbqgLUCCco9" 'tabTopics1の記事一覧のクラス名
For Each node In nodes
If node.className = cstClassName Then
Debug.Print node.innerText
End If
Next node
Set nodes = Nothing
Set localHtml = Nothing
Set client = Nothing
End Sub
タグdivで要素を検索し、クラス名が”TRuzXRRZHRqbqgLUCCco9″であるならinnerTextを吐き出すようにしてあります。
“TRuzXRRZHRqbqgLUCCco9″というのは、下図のようにブラウザのF12で確認したところ8件だけしかヒットしませんでした。ちょうどtabtopics1~8のところだけで使われているようでしたので、これを使ってみました。
上記のコードを実行するとこんな感じです。
Newの文字まで取得してしまうのは仕様です。
以上、SeleniumBasicもWebdriverも使わず、ExcelVBAでスクレイピングをしてみました。
意外とできるものですね。ただ、XPathが使えないので開発には時間がかかります。
-
前の記事
100時間80時間45時間の根拠 2023.02.16
-
次の記事
迷惑メール 2023.02.18